Как вычисляется python-Levenshtein.ratio
Согласно источнику 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
?
Ответы
Ответ 1
Более внимательно рассмотрев код C, я обнаружил, что это кажущееся противоречие объясняется тем, что ratio
обрабатывает операцию "заменить" по-другому, чем другие операции (т.е. Со стоимостью 2), тогда как distance
обрабатывает их все равно с стоимостью 1.
Это можно увидеть в вызовах внутренней функции levenshtein_common
выполненной в функции ratio_py
:
https://github.com/miohtama/python-Levenshtein/blob/master/Levenshtein.c#L727
static PyObject*
ratio_py(PyObject *self, PyObject *args)
{
size_t lensum;
long int ldist;
if ((ldist = levenshtein_common(args, "ratio", 1, &lensum)) < 0) //Call
return NULL;
if (lensum == 0)
return PyFloat_FromDouble(1.0);
return PyFloat_FromDouble((double)(lensum - ldist)/(lensum));
}
и функцией distance_py
:
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
.
Это объясняет "как", я думаю, единственным оставшимся аспектом будет "почему", но на данный момент я доволен этим пониманием.
Ответ 2
Расстояние Левенштейна для 'ab'
и 'ac'
как 'ac'
ниже:
![image]()
поэтому выравнивание:
a c
a b
Длина выравнивания = 2
число несоответствий = 1
Levenshtein Distance
равно 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
по 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%
Почему некоторые авторы делятся по длине выравнивания, другой - максимальной длиной одного из двух?.., потому что Левенштейн не рассматривает разрыв.Distance = количество исправлений (вставка + удаление + замена). Хотя алгоритм Needleman-Wunsch, который является стандартным глобальным выравниванием, рассматривает пробел.Это разница в разрыве между Needleman-Wunsch и Levenshtein, поэтому большая часть бумаги использует максимальное расстояние между двумя последовательностями (НО ЭТО МОЕ СОБСТВЕННОЕ ПОНИМАНИЕ И IAM НЕ УВЕРЕН 100%)
Здесь IEEE TRANSACTIONS ON PAITERN ANALYSIS: Вычисление нормализованного расстояния редактирования и приложений В этой статье Нормализованное редактирование расстояния следующим образом:
Учитывая две строки X и Y над конечным алфавитом, нормализованное расстояние редактирования между X и Y, d (X, Y) определяется как минимум W (P)/L (P) w, здесь P - путь редактирования между X и Y, W (P) - сумма весов элементарных операций редактирования P, а L (P) - число этих операций (длина P).
Ответ 3
Хотя абсолютного стандарта нет, нормализованное расстояние Левенштейна обычно определяется 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))
его из одного: 1 - ldist/max(len(a), len(b))
.
Ответ 4
(lensum - ldist)/lensum
ldist - это не расстояние, это сумма затрат
![enter image description here]()
Каждый номер массива, который не совпадает, происходит сверху, слева или по диагонали
Если число приходит с левой стороны, это вставка, оно происходит сверху, это удаление, оно происходит от диагонали, это замена
Вставка и удаление имеют стоимость 1, а замена имеет стоимость 2. Затратная стоимость равна 2, поскольку это удаление и вставка
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]()
Для получения дополнительной информации: расчет отношения python-Levenshtein
Другой пример:
![enter image description here]()
Стоимость составляет 9 (4 replace => 4 * 2 = 8 и 1 delete 1 * 1 = 1, 8 + 1 = 9)
str1=len("google") #6
str2=len("look-at") #7
str1 + str2 #13
distance = 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