Levenshtein 距离给出奇怪的值

如何解决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 的子串。 他们通过以下步骤实现该算法:

  1. 他们使用最长公共子序列算法在s1中找到s2的最长公共子串
  2. 他们使用这些公共子序列的起始索引从s1_len中提取长度为s2的子串。当这个子串 s2_substr 放在 s1_len 的末尾时,它可以比 s2 短。
  3. 他们迭代这些子字符串 s2_substr 并使用标准化的 InDel-Distance(如 Levenshtein 距离但没有替换)将它们中的每一个与 s1 进行比较

我知道这个实现有以下缺点

  1. 当使用 python-Levenshtein 时,FuzzyWuzzy 使用它来查找最长公共子序列并计算相似度。但是,python-Levenshtein 用于查找最长公共子序列的实现已知已损坏(请参阅 here),我不知道对此有一个简单的修复方法。有人提出了一个修复程序,但它只修复了这种情况,并在不同情况下引入了问题。 (这是您描述的原始问题)
  2. 当不使用 python-Levenshtein 时,使用 difflib 计算最长公共子序列,使用 difflib 计算。然而,正如 here 所描述的那样,FuzzyWuzzy 并没有禁用自动垃圾启发式算法,当字符串的长度差异很大时,这会导致错误的结果。我刚刚创建了一个 PR 来解决这个问题:https://github.com/seatgeek/fuzzywuzzy/pull/303,但是存储库并没有真正得到积极维护,而且 SeatGeek 似乎有许多缺点,因为它对于他们的用例来说已经足够好了。 (这是你后来添加的 difflib 的问题)
  3. 相似度本身存在缺陷。它假设最佳匹配子串 s2_substr 总是从最长公共子序列之一的起点开始。虽然在许多情况下确实如此,但情况并非总是如此。 (您没有遇到这个问题,我还没有在 FuzzyWuzzy 或 RapidFuzz 中看到关于此的错误报告。结果只是在大多数用户可能不经常遇到的一些非常特定的边缘情况下有很大差异)

哪种算法更适合在很大程度上取决于您的需求。第一个简单的解决方案是用我的库 RapidFuzz 替换 FuzzyWuzzy。这解决了我描述的 LCS 算法的问题。但是,它使用与 FuzzyWuzzy 相同的算法来计算相似度,因此也存在第三个问题。我正在寻找更好的算法(有关更多详细信息,请查看 following question)。 正如安德鲁·盖伊 (Andrew Guy) 所指出的,史密斯-沃特曼距离也可能是另一种选择。但是,它与 fuzz.partial_ratio 有一些很大的不同:

  1. 它使用统一的 Levenshtein 距离(插入/删除/替换的权重均为 1),而 fuzz.partial_ratio 使用 InDel 距离。如果这对您很重要,它可能可以通过在实现时为 Substitutions 赋予 2 的权重来适应使用 InDel 距离。
  2. fuzz.partial_ratio 总是采用长度为 s1_len 的子串,而 Smith Waterman 算法搜索最佳对齐的子串,而不关心它的长度。这还不错,你应该意识到这一点。一个缺点是,由于子串的长度未知,因此很难对结果进行归一化(使其相似度得分在 0 到 100 之间)。这不是真正的问题,因为您可以只搜索最低距离而不是最高相似度。

我没有使用 RapidFuzz 中的 Smith-Waterman 算法来计算 fuzz.partial_ratio 的原因是我希望它直接替代 FuzzyWuzzy 中的实现。不过,我也计划在未来添加 Smith-Waterman 算法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?