Нечеткое сопоставление Python имен только с первыми инициалами

У меня есть случай, когда мне нужно сопоставить имя из заданной строки с базой данных имен. Ниже я привел очень простой пример проблемы, с которой я сталкиваюсь, и мне непонятно, почему один случай работает над другим? Если я не ошибаюсь, алгоритмом по умолчанию Python для extractOne() является алгоритм расстояния Левенштейна. Это потому, что имена Клеменов содержат первые два инициала, а не только один в случае Гонсалеса?

from fuzzywuzzy import fuzz
from fuzzywuzzy import process

s = ['Gonzalez, E. walked down the street.', 'Gonzalez, R. went to the market.', 'Clemens, Ko. reach the intersection; Clemens, Ka. did not.']

names = []

for i in s:

    name = [] #clear name
    for k in i.split():
        if k[0].isupper(): name.append(k)
        else: break
    names.append(' '.join(name))

    if ';' in i:
        for each in i.split(';')[1:]:
            name = [] #clear name
            for k in each.split():
                if k[0].isupper(): name.append(k)
                else: break
            names.append(' '.join(name))

print(names)

choices = ['Kody Clemens','Kacy Clemens','Gonzalez Ryan', 'Gonzalez Eddy']

for i in names:
    s = process.extractOne(i, choices)
    print(s, i)

ВЫХОД:

['Gonzalez, E.', 'Gonzalez, R.', 'Clemens, Ko.', 'Clemens, Ka.']
('Gonzalez Ryan', 85) Gonzalez, E.
('Gonzalez Ryan', 85) Gonzalez, R.
('Kody Clemens', 86) Clemens, Ko.
('Kacy Clemens', 86) Clemens, Ka.

person rahlf23    schedule 12.09.2017    source источник
comment
Можете ли вы опубликовать, что вы получаете, когда запускаете дела? Я не уверен, в чем твоя проблема   -  person OmegaNalphA    schedule 13.09.2017
comment
Отредактировано, чтобы включить вывод   -  person rahlf23    schedule 13.09.2017
comment
Я не знаю, как именно происходит сопоставление, но s = process.extractOne(i, choices, scorer=fuzz.token_sort_ratio) работает   -  person Igle    schedule 13.09.2017
comment
Вау, это работает очень хорошо. Вероятно, потому что мои совпадения больше зависят от сортировки, чем от реальных похожих слов/имен. Включите свой ответ в качестве ответа, и я приму. Спасибо!   -  person rahlf23    schedule 13.09.2017


Ответы (1)


Хотя комментарий @Igle действительно решает эту конкретную проблему, я хочу подчеркнуть, что это узкое решение, которое не обязательно будет работать для всего. Fuzzywuzzy имеет несколько счетчиков, которые используют алгоритм расстояния Левенштейна в сочетании с другой логикой для сравнения строк. Средство оценки по умолчанию, fuzz.WRatio, сравнивает оценку соответствия алгоритма прямого расстояния Левенштейна (fuzz.ratio) с другими вариантами и возвращает наилучшее совпадение из всех средств оценки. Это больше, чем просто это, в том числе дополнительная логика для взвешивания оценок с помощью разных методов, если вам интересно, я предлагаю посмотреть исходный код fuzz.WRatio.

Чтобы увидеть, что происходит в вашем случае, вы можете сравнить оценки для всех вариантов в разных счетчиках, немного адаптировав последние строки вашего кода:

Для token_set_ratio:

for i in names:
   s = process.extract(i, choices,scorer=fuzz.token_set_ratio)
   print(s, i)

[('Gonzalez Ryan', 89), ('Gonzalez Eddy', 89), ('Kody Clemens', 27), ('Kacy Clemens', 27)] Gonzalez, E.
[('Gonzalez Ryan', 89), ('Gonzalez Eddy', 89), ('Kody Clemens', 27), ('Kacy Clemens', 27)] Gonzalez, R.
[('Kody Clemens', 91), ('Kacy Clemens', 82), ('Gonzalez Ryan', 26), ('Gonzalez Eddy', 26)] Clemens, Ko.
[('Kacy Clemens', 91), ('Kody Clemens', 82), ('Gonzalez Ryan', 35), ('Gonzalez Eddy', 26)] Clemens, Ka.

Для token_sort_ratio:

for i in names:
   s = process.extract(i, choices,scorer=fuzz.token_sort_ratio)
   print(s, i)

[('Gonzalez Eddy', 87), ('Gonzalez Ryan', 70), ('Kody Clemens', 27), ('Kacy Clemens', 27)] Gonzalez, E.
[('Gonzalez Ryan', 87), ('Gonzalez Eddy', 70), ('Kody Clemens', 27), ('Kacy Clemens', 27)] Gonzalez, R.
[('Kody Clemens', 91), ('Kacy Clemens', 82), ('Gonzalez Ryan', 26), ('Gonzalez Eddy', 26)] Clemens, Ko.
[('Kacy Clemens', 91), ('Kody Clemens', 82), ('Gonzalez Ryan', 35), ('Gonzalez Eddy', 26)] Clemens, Ka.

Хотя token_sort_ratio показывает явное выигрышное совпадение, token_set_ratio возвращает более высокие баллы, и именно так fuzz.WRatio выбирает, какой результат он возвращает. Еще одна важная проблема заключается в том, что когда у вас есть такие похожие запросы и варианты выбора, порядок, в котором они сравниваются, начинает иметь значение. Например, когда я запускаю тот же самый код, что и выше, но меняю порядок списка вариантов, мы получаем «Gonzalez Eddy» для обоих:

for i in names:
   s = process.extract(i, choices[::-1],scorer=fuzz.token_set_ratio)
   print(s, i)
[('Gonzalez Eddy', 89), ('Gonzalez Ryan', 89), ('Kacy Clemens', 27), ('Kody Clemens', 27)] Gonzalez, E.
[('Gonzalez Eddy', 89), ('Gonzalez Ryan', 89), ('Kacy Clemens', 27), ('Kody Clemens', 27)] Gonzalez, R.
[('Kody Clemens', 91), ('Kacy Clemens', 82), ('Gonzalez Eddy', 26), ('Gonzalez Ryan', 26)] Clemens, Ko.
[('Kacy Clemens', 91), ('Kody Clemens', 82), ('Gonzalez Ryan', 35), ('Gonzalez Eddy', 26)] Clemens, Ka.

Я предполагаю, что правильное совпадение на самом деле имеет более высокий балл, но «Эдди» и «Райан» достаточно близки к тому, чтобы оба раунда получили одинаковый окончательный результат.

Способы, которыми я справлялся с подобными проблемами в прошлом:

  1. Используйте экстракт вместо ExtractOne (как я делал в примерах выше)
  2. Обрабатывайте одни и те же запросы/выборы с несколькими оценщиками (ratio, token_set_ratio, token_sort_ratio) и используйте средневзвешенное значение этих оценок, чтобы выбрать наилучшее совпадение.
  3. Скорректируйте исходный код fuzzywuzzy, чтобы включить пользовательский вес или удалить округление.
person YG14    schedule 08.11.2017
comment
Спасибо за ваш подробный ответ. Я продвигался вперед с решением в предыдущих комментариях, и оно работало очень надежно для моего приложения, однако это дает большую ясность. Спасибо! - person rahlf23; 09.11.2017
comment
Рад помочь, рад, что у вас все получилось. На самом деле это помогло мне лучше понять внутреннюю работу модуля для моего собственного проекта, так что все в выигрыше! - person YG14; 09.11.2017