Как питон-Левенштейн.вычисляется отношение

по словам python-Levenshtein.ratio источник:

https://github.com/miohtama/python-Levenshtein/blob/master/Levenshtein.c#L722

он вычисляется как (lensum - ldist) / lensum. Это работает для

distance('ab', 'a') = 1
ratio('ab', 'a') = 0.666666

однако, кажется, что он ломается с

distance('ab', 'ac') = 1
ratio('ab', 'ac') = 0.5

Я чувствую, что упускаю что-то очень простое.. но почему не 0.75?

4 ответов


расстояние Левенштейна на 'ab' и 'ac' как показано ниже:

image

Итак, расклад такой:

  a c
  a b 

длина трассы = 2
количество несоответствий = 1

Levenshtein Distance is 1 потому что для передачи требуется только одна замена ac на ab (или наоборот)

коэффициент расстояния = (расстояние Левенштейна) / (длина выравнивания) = 0.5

редактировать

вы пишите

(lensum - ldist) / lensum = (1 - ldist/lensum) = 1 - 0.5 = 0.5.

но это соответствующего (не расстояние)
REFFRENCE, вы можете заметить его написано

Matching %

p = (1 - l/m) × 100

здесь l - это levenshtein distance и m - это length of the longest of the two слова:

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

(1 - 3/7) × 100 = 57.14...  

  (Word 1    Word 2    RATIO   Mis-Match   Match%
   AB         AB         0       0        (1 - 0/2 )*100  = 100%  
   CD         AB         1       2        (1 - 2/2 )*100  = 0%   
   AB         AC        .5       1        (1 - 1/2 )*100  = 50%      

почему некоторые авторы делят на длину выравнивания,другие-на максимальную длину одного из обоих?.. потому что Левенштейн не считает разрыв. Расстояние = количество правок (вставка + удаление + замена), в то время как Нидлмана–Вунш это стандартное глобальное выравнивание рассматривает разрыв. Это (разрыв) разница между Нидлмана–Вунш и Левенштейна, так много бумаги использовать максимальное расстояние между двумя последовательностями (НО ЭТО МОЕ СОБСТВЕННОЕ ПОНИМАНИЕ, И Я НЕ УВЕРЕН НА 100%)

вот транзакции IEEE по анализу PAITERN:вычисление нормализованного расстояния редактирования и приложений в этой статье Нормализованное Расстояние Редактирования следующим образом:

даны два строки X и Y над конечным алфавитом, нормализованное расстояние редактирования между X и Y, d( X , Y ) определяется как минимум W( P ) / L ( P )w, здесь P-путь редактирования между X и Y , W ( P) - сумма весов элементарных операций редактирования P, а L(P) - количество этих операций (длина P).


при более внимательном рассмотрении кода C я обнаружил, что это кажущееся противоречие связано с тем, что ratio относится к операции редактирования" заменить " иначе, чем к другим операциям (т. е. со стоимостью 2), тогда как distance лечит их все равно со стоимостью 1.

это можно увидеть в призывах к внутреннему :

https://github.com/miohtama/python-Levenshtein/blob/master/Levenshtein.c#L715

static PyObject*
distance_py(PyObject *self, PyObject *args)
{
  size_t lensum;
  long int ldist;

  if ((ldist = levenshtein_common(args, "distance", 0, &lensum)) < 0)
    return NULL;

  return PyInt_FromLong((long)ldist);
}

что в конечном итоге приводит к отправке различных аргументов стоимости в другую внутреннюю функцию,lev_edit_distance, который имеет следующий документ фрагмент:

@xcost: If nonzero, the replace operation has weight 2, otherwise all
        edit operations have equal weights of 1.

код lev_edit_distance():

/**
 * lev_edit_distance:
 * @len1: The length of @string1.
 * @string1: A sequence of bytes of length @len1, may contain NUL characters.
 * @len2: The length of @string2.
 * @string2: A sequence of bytes of length @len2, may contain NUL characters.
 * @xcost: If nonzero, the replace operation has weight 2, otherwise all
 *         edit operations have equal weights of 1.
 *
 * Computes Levenshtein edit distance of two strings.
 *
 * Returns: The edit distance.
 **/
_LEV_STATIC_PY size_t
lev_edit_distance(size_t len1, const lev_byte *string1,
                  size_t len2, const lev_byte *string2,
                  int xcost)
{
  size_t i;

[ответ]

так в моем примере

ratio('ab', 'ac') подразумевает операцию замены (стоимость 2), по всей длине строк (4), следовательно 2/4 = 0.5.

это объясняет "как", я думаю, единственным оставшимся аспектом будет" почему", но на данный момент я удовлетворен этим пониманием.


Хотя нет абсолютного стандарта, нормализованное расстояние Levensthein чаще всего определяется ldist / max(len(a), len(b)). Что бы урожай .5 для обоих примеров.

на max имеет смысл, так как это самая низкая верхняя граница на расстояние Левенштейна: получить a С b здесь len(a) > len(b), вы всегда можете заменить первый len(b) элементов b с соответствующими из a, затем вставьте недостающую часть a[len(b):], итого len(a) изменить оперативный.

этот аргумент очевидным образом распространяется на случай, когда len(a) <= len(b). Чтобы превратить нормализованное расстояние в меру подобия, вычитаем его из единицы:1 - ldist / max(len(a), len(b)).


(lensum - ldist) / lensum

ldist-это не расстояние, это сумма затрат

enter image description here

каждый номер массива, который не соответствует, приходит сверху, слева или по диагонали

если номер идет слева он является вставкой, он приходит сверху это удаление, это происходит от диагонали это замена

вставка и удаление стоят 1, а замена-2. Стоимость замены 2 потому что это delete и insert

AB ac стоимость 2, потому что это замена

>>> import Levenshtein as lev
>>> lev.distance("ab","ac")
1
>>> lev.ratio("ab","ac")
0.5
>>> (4.0-1.0)/4.0    #Erro, the distance is 1 but the cost is 2 to be a replacement
0.75
>>> lev.ratio("ab","a")
0.6666666666666666
>>> lev.distance("ab","a")
1
>>> (3.0-1.0)/3.0    #Coincidence, the distance equal to the cost of insertion that is 1
0.6666666666666666
>>> x="ab"
>>> y="ac"
>>> lev.editops(x,y)
[('replace', 1, 1)]
>>> ldist = sum([2 for item in lev.editops(x,y) if item[0] == 'replace'])+ sum([1 for item in lev.editops(x,y) if item[0] != 'replace'])
>>> ldist
2
>>> ln=len(x)+len(y)
>>> ln
4
>>> (4.0-2.0)/4.0
0.5

enter image description here

дополнительные сведения: расчет коэффициента питона-Левенштейна

еще пример:

enter image description here

стоимость 9 (4 заменить => 4*2=8 и 1 удалить 1*1=1, 8+1=9)

str1=len("google") #6
str2=len("look-at") #7
str1 + str2 #13

расстояние = 5 (в соответствии с вектором (7, 6) = 5 из матрица)

отношение (13-9)/13 = 0,3076923076923077

>>> c="look-at"
>>> d="google"
>>> lev.editops(c,d)
[('replace', 0, 0), ('delete', 3, 3), ('replace', 4, 3), ('replace', 5, 4), ('replace', 6, 5)]
>>> lev.ratio(c,d)
0.3076923076923077
>>> lev.distance(c,d)
5