Функция, которая возвращает сродство между текстами?

считать, что у меня есть

string1 = "hello hi goodmorning evening [...]"

и у меня есть несколько младших ключевых слов

compare1 = "hello evening"
compare2 = "hello hi"

Мне нужна функция, которая возвращает сродство между текстом и ключевыми словами. Пример:

function(string1,compare1);  // returns: 4
function(string1,compare2);  // returns: 5 (more relevant)

Обратите внимание, что 5 и 4 - это только пример.

Вы можете сказать - написать функцию, которая учитывает вхождения, но для этого примера это не сработает, потому что у обоих появилось 2 вхождения, но compare1 менее релевантно, потому что "hello evening" точно не встречается в string1 (2 слова hello и вечер более отдаленный, чем привет привет)

Есть ли какой-нибудь известный алгоритм?

ADD1:

algos, как Edit Distance, в этом случае НЕ будет работать. Поскольку string1 является полным текстом (например, 300-400 слов), а строки сравнения - не более 4-5 слов.

Ответ 1

Алгоритм динамического программирования

Кажется, что то, что вы ищете, очень похоже на то, что делает алгоритм Smith-Waterman.

Из Википедии:

Алгоритм был впервые предложен Храм Ф. Смитом и Майклом С. Уотерманом в 1981 году. Подобно алгоритму Needleman-Wunsch, из которого оно является вариацией, Smith-Waterman - это динамический алгоритм . Таким образом, он обладает желательным свойством, что гарантировано найти оптимальное локальное выравнивание по отношению к используемой системе подсчета (которая включает в себя матрицу замещения и схему подсчета пробелов).

Посмотрите на практический пример, чтобы оценить его полезность.

Предположим, у нас есть текст:

text = "We the people of the United States, in order to form a more 
perfect union, establish justice, insure domestic tranquility, 
provide for the common defense, 

  promote the general welfare, 

  and secure the blessings of liberty to ourselves and our posterity, 
do ordain and establish this Constitution for the United States of 
America.";  

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

Мы сравниваем сродство (или сходство) со списком строк:

list = {
   "the general welfare",
   "my personal welfare",
   "general utopian welfare",
   "the general",
   "promote welfare",
   "stackoverflow rulez"
   };  

У меня уже реализованный алгоритм, поэтому я рассчитываю сходство и нормализую результаты:

sw = SmithWatermanSimilarity[ text, #] & /@ list;
swN = (sw - Min[sw])/(Max[sw] - Min[sw])  

Затем назовем результаты:

enter image description here

Я думаю, что он очень похож на ваш ожидаемый результат.

НТН!

Некоторые реализации (с исходным кодом)

Ответ 2

Взгляните на создание N-граммов из ваших входных данных, а затем на N-граммах. У меня есть решение, где я рассматриваю каждый n-грамм как размерность в векторном пространстве (который в этом случае становится пространством 4000 измерений), а затем сродство является косинусом угла между двумя векторами (здесь участвует точечный продукт).

Жесткая часть состоит в том, чтобы придумать метрику, определяющую сродство так, как вы хотите.

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

Ответ 3

py-editdist даст вам расстояние редактирования Levenshtein между двумя строками, что является одной метрикой, которая может быть полезна.

Смотрите: http://www.mindrot.org/projects/py-editdist/

Пример кода с этой страницы:

import editdist

# Calculate the edit distance between two strings
d = editdist.distance("abc", "bcdef")

Связанный: https://stackoverflow.com/questions/682367/good-python-modules-for-fuzzy-string-comparison

Ответ 5

Здесь вы можете найти список показателей для вычисления расстояния между строками и java-библиотеку с открытым исходным кодом, которая просто делает это. http://en.wikipedia.org/wiki/String_metric В частности, взгляните на алгоритм Смита-Уотермана, имея в виду, что то, что они называют "Алфавит", может быть составлено тем, что мы называем "Строки": поэтому, учитывая алфавит

{A = "hello", B = "hi",C = "goodmorning",D = "evening"}

и называемый d расстоянием, ваша функция пытается вычислить

d(ABCD,AB) vs d(ABCD,AC)

Ответ 6

Ну, вы можете подсчитать вхождения фрагментов сравнивающего текста, т.е.

"a-b-c" → "a", "b", "c", "a-b", "b-c", "a-b-c" (возможно, "a-c", если вы этого хотели)

И затем подсчитывайте вхождения каждого из них и суммируйте их, возможно, с весом (длина строки)/(длина всей строки).

Тогда вам просто нужен способ для создания этих фрагментов и выполнить проверку для всех.

Ответ 7

В то время как расстояние Levenshtein в его нынешнем виде может не соответствовать вашим целям, его модификация может: Попробуйте реализовать его, сохранив вставки, удаления, и подстановки отдельно.

Тогда расстояние будет следующим:

  • Все подстановки
  • Число пробелов минус единица в каждом наборе последовательных вставок/исключений:
    • (В вашем случае: "привет приветствую" считается только двумя изменениями, а "[...]" считается как.)

Конечно, вам придется протестировать это, но если это не сработает, попробуйте просто использовать сумму последовательных вставок/исключений (так что "привет доброе утро" - это только 1 редактирование).

ИЗМЕНИТЬ

PS: это предполагает относительно существенное изменение того, как работает Levenshtein, вы хотите сначала "выровнять" ваши данные (выяснить, где значительное (более двух символов) перекрывается и вставлять "нулевые" символы, которые считались бы вставки).

Кроме того, это просто непроверенная идея, поэтому любые идеи для улучшения приветствуются.