Что такое простой алгоритм сопоставления нечетких строк в Python?

Я пытаюсь найти какой-то хороший, нечеткий алгоритм сопоставления строк. Прямое сопоставление не работает для меня - это не слишком хорошо, потому что, если мои строки не похожи на 100%, совпадение не удастся. Метод Левенштейна не очень хорошо работает со строками, так как работает на уровне символов. Я искал что-то вроде соответствия уровня слов, например

Строка A: быстрая коричневая лиса.

Строка B: Быстрая коричневая лиса перепрыгнула через ленивую собаку.

Они должны совпадать, так как все слова в строке A находятся в строке B.

Теперь это упрощенный пример, но кто-нибудь знает хороший, нечеткий алгоритм сопоставления строк, который работает на уровне слов.

Ответы

Ответ 1

Мне нравится Дрю ответ.

Вы можете использовать difflib, чтобы найти самое длинное совпадение:

>>> a = 'The quick brown fox.'
>>> b = 'The quick brown fox jumped over the lazy dog.'
>>> import difflib
>>> s = difflib.SequenceMatcher(None, a, b)
>>> s.find_longest_match(0,len(a),0,len(b))
Match(a=0, b=0, size=19) # returns NamedTuple (new in v2.6)

Или выберите минимальный порог соответствия. Пример:

>>> difflib.SequenceMatcher(None, a, b).ratio()
0.61538461538461542

Ответ 2

Взгляните на эту библиотеку python, которую SeatGeek открыла вчера. Очевидно, что большинство из этих проблем очень зависимы от контекста, но это может вам помочь.

from fuzzywuzzy import fuzz

s1 = "the quick brown fox"
s2 = "the quick brown fox jumped over the lazy dog"
s3 = "the fast fox jumped over the hard-working dog"

fuzz.partial_ratio(s1, s2)
> 100

fuzz.token_set_ratio(s2, s3)
> 73

Сайт SeatGeek

и рефинансирование Github

Ответ 3

Если все, что вы хотите сделать, это проверить, соответствуют ли все слова в строке другой строке, что один лайнер:

if not [word for word in b.split(' ') if word not in a.split(' ')]:
    print 'Match!'

Если вы хотите забить их вместо бинарного теста, почему бы просто не сделать что-то вроде:

((# совпадающих слов)/(# слов в большей строке)) * ((# слов в меньшей строке)/(# слов в большей строке))

?

Если бы вы этого захотели, вы могли бы стать фаворитом и сделать нечеткое соответствие для каждой строки.

Ответ 4

Вы можете изменить алгоритм Левенштейна для сравнения слов, а не символов. Это не очень сложный алгоритм, и источник доступен на многих языках в Интернете.

Левенштейн работает, сравнивая два массива символов. Нет никакой причины, чтобы та же логика не могла применяться к двум массивам строк.

Ответ 5

Я сделал это некоторое время назад с С#, мой предыдущий вопрос здесь. Для вашего интереса есть стартовый алгоритм, вы можете легко преобразовать его в python.

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

  • У вас есть список с оригинальными "заголовками" (слова/предложения, которые вы хотите сопоставить с).
  • Каждый элемент заголовка должен иметь минимальный балл матча на слово/предложение, игнорировать название также.
  • Вы также должны иметь глобальный минимальный процент от конечного результата.
  • Вы должны рассчитать каждое слово слово Левенштейн расстояние.
  • Вы должны увеличить общий вес матча, если слова идут в том же (быстрый коричневый против быстрого коричневого цвета, должен иметь окончательно более высокий вес чем быстрый коричневый или коричневый быстро.)

Ответ 6

Вы можете попробовать FuzzySearchEngine из https://github.com/frazenshtein/fastcd/blob/master/search.py.

Этот нечеткий поиск поддерживает только поиск слов и имеет фиксированную допустимую ошибку для слова (только одна замена или транспонирование двух смежных символов).

Однако, например, вы можете попробовать что-то вроде:

import search

string = "Chapter I. The quick brown fox jumped over the lazy dog."
substr = "the qiuck broqn fox."

def fuzzy_search_for_sentences(substr, string):  
    start = None
    pos = 0
    for word in substr.split(" "):
        if not word:
            continue
        match = search.FuzzySearchEngine(word).search(string, pos=pos)
        if not match:
            return None
        if start is None:
            start = match.start()
        pos = match.end()
    return start

print(fuzzy_search_for_sentences(substr, string))

11 будет напечатано

Ответ 7

Левенштейн должен работать нормально, если вы сравниваете слова (строки, разделенные последовательностями символов остановки) вместо отдельных букв.

def ld(s1, s2):  # Levenshtein Distance
    len1 = len(s1)+1
    len2 = len(s2)+1
    lt = [[0 for i2 in range(len2)] for i1 in range(len1)]  # lt - levenshtein_table
    lt[0] = list(range(len2))
    i = 0
    for l in lt:
        l[0] = i
        i += 1
    for i1 in range(1, len1):
        for i2 in range(1, len2):
            if s1[i1-1] == s2[i2-1]:
                v = 0
            else:
                v = 1
            lt[i1][i2] = min(lt[i1][i2-1]+1, lt[i1-1][i2]+1, lt[i1-1][i2-1]+v)
    return lt[-1][-1]

str1 = "The quick brown fox"
str2 = "The quick brown fox jumped over the lazy dog"

print("{} words need to be added, deleted or replaced to convert string 1 into string 2".format(ld(str1.split(),str2.split())))