在以下限制条件下,是否有更好的算法(并且希望有一个Python库):
- 我想在字符串之间进行模糊匹配。例如,matches('Hello, All you people', 'hello, all You peopl') 应该返回True。
- 假阴性是可以接受的,但假阳性除非极其罕见,否则不可接受。
- 这是在非实时设置中完成的,因此速度不是(太)关键。
- [编辑] 我正在比较多个单词的字符串。
除了Levenshtein距离(或Levenshtein比率)之外,对于我的情况,是否有更好的算法?
除了Levenshtein距离(或Levenshtein比率)之外,对于我的情况,是否有更好的算法?
我知道这不是同样的事情,但足够接近:
>>> import difflib
>>> a = 'Hello, All you people'
>>> b = 'hello, all You peopl'
>>> seq=difflib.SequenceMatcher(a=a.lower(), b=b.lower())
>>> seq.ratio()
0.97560975609756095
你可以将这个变成一个函数。
def similar(seq1, seq2):
return difflib.SequenceMatcher(a=seq1.lower(), b=seq2.lower()).ratio() > 0.9
>>> similar(a, b)
True
>>> similar('Hello, world', 'Hi, world')
False
在谢菲尔德大学有一个很棒的字符串相似度度量资源。它列出了各种度量标准(不仅仅是莱文斯坦距离),并提供了它们的开源实现。看起来其中许多指标应该很容易适应Python。
http://web.archive.org/web/20081224234350/http://www.dcs.shef.ac.uk/~sam/stringmetrics.html
以下是列表的一部分:
以下代码片段将计算两个字符串的difflib、Levenshtein、Sørensen和Jaccard相似度值。在下面的代码片段中,我正在迭代一个tsv文件,其中感兴趣的字符串占据了tsv的[3]
列和[4]
列。(pip install python-Levenshtein
和pip install distance
):
import codecs, difflib, Levenshtein, distance
with codecs.open("titles.tsv","r","utf-8") as f:
title_list = f.read().split("\n")[:-1]
for row in title_list:
sr = row.lower().split("\t")
diffl = difflib.SequenceMatcher(None, sr[3], sr[4]).ratio()
lev = Levenshtein.ratio(sr[3], sr[4])
sor = 1 - distance.sorensen(sr[3], sr[4])
jac = 1 - distance.jaccard(sr[3], sr[4])
print diffl, lev, sor, jac
我建议使用Levenshtein距离或所谓的Damerau距离(考虑了交换位置),而不是difflib库,有两个原因:(1)可以使用“足够快”的动态规划算法和“飞快”的C代码(2)其行为被充分理解,例如Levenshtein满足三角不等式,因此可以在Burkhard-Keller树中使用。
阈值:您应该仅将距离<(1-X)*max(len(string1),len(string2))视为“正面”,并调整X(相似性因子)以适合自己。选择X的一种方法是获取匹配样本,为每个计算X,忽略X<0.8或0.9的情况,然后按降序排列剩余项的X,眼球观察并插入正确结果,并针对各种X水平计算某些错误成本度量。
N.B.您的猿/苹果示例距离为2,因此X为0.6……如果我迫切寻找某物并且有很高的假阴性惩罚,我只会使用低至0.75的阈值。
>>> get_close_matches('appel', ['ape', 'apple', 'peach', 'puppy'])
['apple', 'ape']
>>> import keyword
>>> get_close_matches('wheel', keyword.kwlist)
['while']
>>> get_close_matches('apple', keyword.kwlist)
[]
>>> get_close_matches('accept', keyword.kwlist)
['except']
get_close_matches('appel', ['ape', 'peach', 'puppy'])
会返回猿猴。我可以设置截断值,但从一些快速实验来看,误报太常见了。 - agiliqdef spellcheck(self, sentence):
#return ' '.join([difflib.get_close_matches(word, wordlist,1 , 0)[0] for word in sentence.split()])
return ' '.join( [ sorted( { Levenshtein.ratio(x, word):x for x in wordlist }.items(), reverse=True)[0][1] for word in sentence.split() ] )
https://pypi.python.org/pypi/python-Levenshtein/
导入Levenshtein
ngramratio
中的方法 nratio()
。>>> pip install ngramratio
>>> from ngramratio import ngramratio
>>> SequenceMatcherExtended = ngramratio.SequenceMatcherExtended
>>> a = 'Hi there'
>>> b = 'Hit here'
>>> seq=SequenceMatcherExtended(a=a.lower(), b=b.lower())
>>> seq.ratio()
>>> 0.875
>>> seq.nratio(1) #this replicates `seq.ratio`.
>>> 0.875
>>> seq.nratio(2)
>>> 0.75
>>> seq.nratio(3)
>>> 0.5
nratio(n)
仅匹配长度 >= n 的n元组。
您可以选择一个值为n,比如n=2,并像Nadia在之前的回复中所做的那样创建一个布尔相似度函数。
def similar(seq1, seq2):
return SequenceMatcherExtended(a=seq1.lower(), b=seq2.lower()).nratio(2) > 0.8
>>> similar(a, b)
False
>>> similar('Hi there', 'Hi ther')
True
(2) 如果有“接近人类智能”的技术可用,它既不会出现在屏幕代码中,也不会免费提供。
(3) 考虑使用允许置换的方法——将"appel/apple"排名高于"ape/apple"和"ape/appel"。 - John Machin