如何在Python中对Levenshtein距离超过80%的单词进行分组
Posted
技术标签:
【中文标题】如何在Python中对Levenshtein距离超过80%的单词进行分组【英文标题】:How to group words whose Levenshtein distance is more than 80 percent in Python 【发布时间】:2016-05-12 08:02:09 【问题描述】:假设我有一个列表:-
person_name = ['zakesh', 'oldman LLC', 'bikash', 'goldman LLC', 'zikash','rakesh']
我正在尝试以这样一种方式对列表进行分组,以便两个字符串之间的Levenshtein distance 最大。为了找出两个词之间的比例,我使用了一个python包fuzzywuzzy。
示例:-
>>> from fuzzywuzzy import fuzz
>>> combined_list = ['rakesh', 'zakesh', 'bikash', 'zikash', 'goldman LLC', 'oldman LLC']
>>> fuzz.ratio('goldman LLC', 'oldman LLC')
95
>>> fuzz.ratio('rakesh', 'zakesh')
83
>>> fuzz.ratio('bikash', 'zikash')
83
>>>
我的最终目标:
我的最终目标是对单词进行分组,使它们之间的 Levenshtein 距离超过 80%?
我的列表应该是这样的:-
person_name = ['bikash', 'zikash', 'rakesh', 'zakesh', 'goldman LLC', 'oldman LLC'] because the distance between `bikash` and `zikash` is very high so they should be together.
代码:
我试图通过排序来实现这一点,但关键功能应该是fuzz.ratio
。下面的代码不起作用,但我正在从这个角度解决问题。
from fuzzywuzzy import fuzz
combined_list = ['rakesh', 'zakesh', 'bikash', 'zikash', 'goldman LLC', 'oldman LLC']
combined_list.sort(key=lambda x, y: fuzz.ratio(x, y))
print combined_list
谁能帮我把这些词组合起来,使它们之间的 Levenshtein 距离超过 80%?
【问题讨论】:
“combine”、“similar”、“very high”和“something like this”都没有很好的定义。你必须澄清你的规格。 我很困惑。我看到了一个优化问题,即“将这组单词分成具有最大内部距离的簇”,但您继续谈论“排序”,即“将列表按顺序排列”。我不确定这两者是如何混合的。您是否只希望两个 successive 字符串之间的距离最大? 在 python 2 中,您可以将比较函数cmp
传递给 sort
(和相关函数)。这已从 Python 3 中删除,但有一种解决方法:请参阅cmp_to_key。
关于集群问题(如果是的话):听起来像maximum clique。节点是单词,如果这些单词之间的编辑距离 >= 80%,则单词之间有边缘。其余的是 NP 完全的,采用您选择的近似算法,networkx 实现了一些。
@python 对于group by,你需要一个函数来根据单词本身来确定单词的组关联。这里的问题是:是否可以将单词添加到组中取决于 all 组中已经存在的元素。这是一个组合优化问题,据我所知,最大集团符合要求。
【参考方案1】:
这对名称进行分组
from fuzzywuzzy import fuzz
combined_list = ['rakesh', 'zakesh', 'bikash', 'zikash', 'goldman LLC', 'oldman LLC']
combined_list.append('bakesh')
print('input names:', combined_list)
grs = list() # groups of names with distance > 80
for name in combined_list:
for g in grs:
if all(fuzz.ratio(name, w) > 80 for w in g):
g.append(name)
break
else:
grs.append([name, ])
print('output groups:', grs)
outlist = [el for g in grs for el in g]
print('output list:', outlist)
生产
input names: ['rakesh', 'zakesh', 'bikash', 'zikash', 'goldman LLC', 'oldman LLC', 'bakesh']
output groups: [['rakesh', 'zakesh', 'bakesh'], ['bikash', 'zikash'], ['goldman LLC', 'oldman LLC']]
output list: ['rakesh', 'zakesh', 'bakesh', 'bikash', 'zikash', 'goldman LLC', 'oldman LLC']
如您所见,名称分组正确,但顺序可能不是您想要的。
【讨论】:
这不只是一种(贪婪的)方法,而且结果组可能不是最优的吗? @dhke 当然是的。我从来没有说过它是最佳的,也不是唯一的。请务必提供更好/替代的解决方案。 我只是想提一下,但我会尽量给出一个最佳的(最大组数)。尽管如此,您的解决方案当然是正确的并返回有效的集群。 这本质上是使用单个集群进行集群,并逐步添加元素。请注意,它取决于订单;随着我们向集群中添加更多元素(并且它们变得分散),满足“添加”条件all(fuzz.ratio(name, w) > 80 for w in g)
变得更加困难。因此,如果您在运行前将combined_list
洗牌,您可能会得到不同的结果。以上是关于如何在Python中对Levenshtein距离超过80%的单词进行分组的主要内容,如果未能解决你的问题,请参考以下文章
Python:使用 scikit-learn 的 dbscan 进行字符串聚类,使用 Levenshtein 距离作为度量: