如何解决Levenshtein 距离给出奇怪的值
这是一个字符串 T
:
'男士衬衫团队 brienne 有趣的讽刺衬衫具有图形 T 恤马克杯婴儿装真正的激情辉煌设计详细插图强烈欣赏事物创意 br 商店在不同衬衫上发现数千种设计婴儿装马克杯有趣的流行文化抽象诙谐的许多设计使几乎任何人都感到美好的一天其他符合 ul li 质量 短袖 圆领衬衫 100 棉柔软耐用舒适感觉合身标准尺寸怀疑 l xl 可用 li li 可持续性标签公司构想相信纺织品行业开始采取负责任的行动制作棉 li li 服装印刷使用国家艺术直接服装设备裂纹剥离水洗 li li 图形 T 恤设计专业印刷独特的设计看起来很棒让人微笑有趣可爱复古表现力艺术品 li ul'
我已经突出显示了上面字符串的一部分,因为上面是字符串的预处理版本,因此可能难以阅读。
我得到以下值:
fuzz.partial_ratio('short sleeve',T)
给 50
fuzz.partial_ratio('long sleeve',T)
给 73
fuzz.partial_ratio('dsfsdf sleeve',T)
给 62
fuzz.partial_ratio('sleeve',T)
给 50
我对此感到非常困惑。第一个和第四个值不应该是 100 吗?当然我遗漏了一些东西,但我无法弄清楚。
编辑:这是我在卸载 python-Levenshtein 库后运行的另一个示例:
'第一个成功方式妻子告诉 v 2 长袖衬衫 id 1084 第一个成功方式妻子告诉 v 2 长袖衬衫设计印刷质量 100 长袖棉衬衫运动灰色 90 棉 10 涤纶标准长袖衬衫时尚合身紧身款式请检查尺码表列出的附加图片随时联系我们首先尺码问题满意度 100 件保证衬衫通常在工作日中午 est 下一个工作日订购 中午 est 长袖衬衫 100 件棉标准衬衫时尚合身 合并运输多个项目
fuzz.partial_ratio('long sleeve',T)
给出 27
fuzz.partial_ratio('short sleeve',T)
给出 33
fuzz.partial_ratio('sleeveless',T)
给出 40
fuzz.partial_ratio('dsfasd sleeve',T)
给出 23
不幸的是,这个问题似乎不是 python-Levenshtein 库独有的。
解决方法
fuzzywuzzy
库中某处存在一个非常奇怪且微妙的错误。
如果我们运行以下
from fuzzywuzzy import fuzz
fuzz.partial_ratio('funny','aa aaaaa aaaa aaaaaaa funny aaaaaaa aaaaaaaa aaaaaaa aaaa aaaa aaayaaaa auaa aaaa aaaaaaaa aaaaaaaaa aaaaaa aaaaaaaa aaaaa aaaa aa aaaaaaaaaaa aaaaaa aaaffaaaaaaa aaaaa aaayaaaa auaa funny aaaa aaaaaa')
它返回 0
而如果我们从这个字符串的开头删除一个字母:
fuzz.partial_ratio('funny','a aaaaa aaaa aaaaaaa funny aaaaaaa aaaaaaaa aaaaaaa aaaa aaaa aaayaaaa auaa aaaa aaaaaaaa aaaaaaaaa aaaaaa aaaaaaaa aaaaa aaaa aa aaaaaaaaaaa aaaaaa aaaffaaaaaaa aaaaa aaayaaaa auaa funny aaaa aaaaaa')
它返回 100
(抱歉,字符串又长又可怕。我试图将其简化为尽可能简单的字符串,但我似乎看不到驱动此错误的逻辑)
Github 上似乎有 similar bug reports。
安装 python-Levenshtein
似乎修复了我上面的示例(如果未安装 difflib
,fuzzywuzzy 将恢复为 python-Levenshtein
),但不会更改您的原始示例。
安装 python-Levenshtein
后,我可以将您的示例简化为:
fuzz.partial_ratio('sleeve','s l e e v sleeve e ')
返回 50
。
从较长的字符串中删除第一个字母:
fuzz.partial_ratio('sleeve','l e e v sleeve e ')
返回 100
。
这为可能发生的事情提供了某种提示,但我怀疑这需要深入研究 python-Levenshtein
才能弄清楚。
我的推荐?提交错误报告。然后找到另一个库来比较字符串。 RapidFuzz 可能是一个合适的选择。
更新:
我认为该错误可能与使用 opcodes
库中的 python-Levenshtein
相关:
from Levenshtein import opcodes
opcodes('sleeve','s l e e v sleeve e ')
返回:
[('equal',1,1),('insert',2),('equal',2,3),3,4),4,5),5,6),6,7),7,8),8,9),9,12),12,13),13,19)]
在 fuzzywuzzy
中使用时,这显然不是预期的结果,即使这些是一组最少的编辑操作。在 fuzzywuzzy
中,优先级应该放在连续块上,而 Levenshtein 距离的正式定义并没有优先考虑连续块和非连续块(至少我的理解不是这样)。请注意,difflib.SequenceMatcher.get_opcodes()
给出了不同的结果。
我怀疑需要一些非常仔细的考虑来修复这个错误并使其正确。
,该算法背后的一般思想是在较长的字符串中找到最佳匹配的子字符串。但是,在 FuzzyWuzzy 中完成此操作的方式存在一些问题。在下面的算法描述中,s1
指的是较短的字符串,s2
指的是较长的字符串,s2_substr
指的是 s2 的子串。
他们通过以下步骤实现该算法:
- 他们使用最长公共子序列算法在
s1
中找到s2
的最长公共子串 - 他们使用这些公共子序列的起始索引从
s1_len
中提取长度为s2
的子串。当这个子串s2_substr
放在s1_len
的末尾时,它可以比s2
短。 - 他们迭代这些子字符串
s2_substr
并使用标准化的 InDel-Distance(如 Levenshtein 距离但没有替换)将它们中的每一个与s1
进行比较
我知道这个实现有以下缺点
- 当使用 python-Levenshtein 时,FuzzyWuzzy 使用它来查找最长公共子序列并计算相似度。但是,python-Levenshtein 用于查找最长公共子序列的实现已知已损坏(请参阅 here),我不知道对此有一个简单的修复方法。有人提出了一个修复程序,但它只修复了这种情况,并在不同情况下引入了问题。 (这是您描述的原始问题)
- 当不使用 python-Levenshtein 时,使用 difflib 计算最长公共子序列,使用 difflib 计算。然而,正如 here 所描述的那样,FuzzyWuzzy 并没有禁用自动垃圾启发式算法,当字符串的长度差异很大时,这会导致错误的结果。我刚刚创建了一个 PR 来解决这个问题:https://github.com/seatgeek/fuzzywuzzy/pull/303,但是存储库并没有真正得到积极维护,而且 SeatGeek 似乎有许多缺点,因为它对于他们的用例来说已经足够好了。 (这是你后来添加的 difflib 的问题)
- 相似度本身存在缺陷。它假设最佳匹配子串
s2_substr
总是从最长公共子序列之一的起点开始。虽然在许多情况下确实如此,但情况并非总是如此。 (您没有遇到这个问题,我还没有在 FuzzyWuzzy 或 RapidFuzz 中看到关于此的错误报告。结果只是在大多数用户可能不经常遇到的一些非常特定的边缘情况下有很大差异)
哪种算法更适合在很大程度上取决于您的需求。第一个简单的解决方案是用我的库 RapidFuzz 替换 FuzzyWuzzy。这解决了我描述的 LCS 算法的问题。但是,它使用与 FuzzyWuzzy 相同的算法来计算相似度,因此也存在第三个问题。我正在寻找更好的算法(有关更多详细信息,请查看 following question)。
正如安德鲁·盖伊 (Andrew Guy) 所指出的,史密斯-沃特曼距离也可能是另一种选择。但是,它与 fuzz.partial_ratio
有一些很大的不同:
- 它使用统一的 Levenshtein 距离(插入/删除/替换的权重均为 1),而 fuzz.partial_ratio 使用 InDel 距离。如果这对您很重要,它可能可以通过在实现时为 Substitutions 赋予 2 的权重来适应使用 InDel 距离。
- fuzz.partial_ratio 总是采用长度为
s1_len
的子串,而 Smith Waterman 算法搜索最佳对齐的子串,而不关心它的长度。这还不错,你应该意识到这一点。一个缺点是,由于子串的长度未知,因此很难对结果进行归一化(使其相似度得分在 0 到 100 之间)。这不是真正的问题,因为您可以只搜索最低距离而不是最高相似度。
我没有使用 RapidFuzz 中的 Smith-Waterman 算法来计算 fuzz.partial_ratio 的原因是我希望它直接替代 FuzzyWuzzy 中的实现。不过,我也计划在未来添加 Smith-Waterman 算法。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。