Как найти совпадающие уникальные элементы в двух списках?(Используя функцию расстояния здесь) - PullRequest
1 голос
/ 08 мая 2019

Я пытаюсь создать сопоставление имен для сравнения, скажем, от 'JOHN LEWIS' до 'JOHN SMITH LEWIS'.Это явно один и тот же человек, и я хочу создать функцию, в которой при вводе этих имен она превращается в список, а затем выдает соответствующие имена.

Проблема в том, что мой цикл возвращает, что 'LEWIS' соответствует 'LEWIS', а 'SMITH' соответствует 'LEWIS' из-за порядка, в котором он находится.

from pyjarowinkler import distance

entered_name = 'JOHN LEWIS'.split(' ')  # equals ['JOHN','LEWIS']
system_name = 'JOHN SMITH LEWIS'.split(' ')  # equals ['JOHN','SMITH','LEWIS']

ratio = []

for i in entered_name:
    maximum = 0 
    for j in system_name:
        score = distance.get_jaro_distance(i, j, winkler=True, 
                                           scaling=0.1)
        while score > maximum:
            maximum = score
            new = (i, j, maximum)
            system_name.remove(i) 
            #removes that name from the original list
    ratio.append(new)

вернул бы что-то вроде: [('JOHN', 'JOHN', 1.0), ('LEWIS', 'SMITH', 0.47)]

, а не: [('JOHN', 'JOHN', 1.0), ('LEWIS', 'LEWIS', 1.0)] <- это то, что я хочу.</p>

Кроме того, если вы попробуете что-то вроде 'ALLY A ARM' с 'ALLY ARIANA ARMANI', оно совпадет с 'ALLY' дважды, если вы не сделаете эту строку remove(i).Вот почему я хочу только уникальные совпадения!

Я просто получаю ошибки или ответы, которые я не ищу.

Ответы [ 2 ]

0 голосов
/ 08 мая 2019

Расстояние Джаро-Винклера - для сравнения последовательностей, нет необходимости сравнивать отдельные элементы, как если бы вы пытались найти расстояние редактирования между отдельными символами, а не целыми словами.

Имея это в виду, вероятно, следует рассматривать части имени как отдельные буквы, а все имя как слово, сравнивая, скажем, "JL" с "JSL" вместо "JOHN LEWIS" и "JOHN SMITH LEWIS":

import string
import itertools

from pyjarowinkler import distance


WORDS_CACHE = {}


def next_letter():
    base = ""
    while True:
        for ch in string.ascii_lowercase:
            yield base + ch
        base += ch


GENERATOR = next_letter()


def encode(word):
    if word not in WORDS_CACHE:
        WORDS_CACHE[word] = GENERATOR.next()
    return WORDS_CACHE[word]


def score(first_name, second_name):
    return distance.get_jaro_distance(
        "".join(map(encode, first_name.split())),
        "".join(map(encode, second_name.split())),
    )
0 голосов
/ 08 мая 2019

Проблема с вашей system_name.remove(i) строкой.Прежде всего, обычно плохая идея изменить список , пока вы выполняете этот список .Это может привести к неожиданному поведению.В вашем случае вот что делает ваш код:

  1. Первый раз, соответствует 'JOHN' и 'JOHN'.Нет проблем.
  2. Удаляет 'JOHN' из system_name.Теперь system_name = ['SMITH', 'LEWIS'].
  3. Второй раз, i = 'LEWIS', j = 'SMITH', score = .47, что больше 0, поэтому ваш чек score > maximum проходит
  4. Мы устанавливаем maximum = score
  5. Устанавливаем new = ('LEWIS', 'SMITH', 0.47)
  6. Удаляем 'LEWIS' из system_name.Сейчас system_name = ['SMITH'].Э-э-э ...

Простое переписывание ниже, используя if вместо while цикла, потому что цикл while совершенно не нужен:

for i in entered_name:
    maximum = 0 
    for j in system_name:
        score = distance.get_jaro_distance(i, j, winkler=True, 
                                           scaling=0.1)
        if score > maximum:
            maximum = score
            new = (i, j, maximum)
    system_name.remove(new[1])  # want to remove 'SMITH' in the example, not 'LEWIS' 
    ratio.append(new)

ВсеЯ переместил вызов system_name.remove() за пределы цикла на system_name и заменил i на j (используя new[1], поскольку я вне цикла j).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...