Python:找到最接近的字符串(从列表中)到另一个字符串
Posted
技术标签:
【中文标题】Python:找到最接近的字符串(从列表中)到另一个字符串【英文标题】:Python: find closest string (from a list) to another string 【发布时间】:2012-04-18 14:34:31 【问题描述】:假设我有一个 string
"Hello"
和一个列表
words = ['hello', 'Hallo', 'hi', 'house', 'key', 'screen', 'hallo','question', 'Hallo', 'format']
如何找到最接近"Hello"
并出现在列表words
中的n words
?
在这种情况下,我们将拥有['hello', 'hallo', 'Hallo', 'hi', 'format'...]
所以策略是从最近的单词到最远的单词对列表单词进行排序。
我想过这样的事情
word = 'Hello'
for i, item in enumerate(words):
if lower(item) > lower(word):
...
但在大型列表中非常慢。
更新
difflib
有效,但也很慢。 (words list
里面有 630000+ 个单词(已排序,每行一个))。因此,每次搜索最接近的单词时,检查列表需要 5 到 7 秒!
【问题讨论】:
也许您正在寻找诸如编辑距离或 Levinshtein 距离之类的东西? 没错,这在很大程度上取决于您对“最接近”的定义。 这630,000字是否排序?它们是否在一个文件中,每行一个单词? 您打算如何定义“最接近”?在您的示例代码中,您使用的是字典比较,但是这将 'hermitage' 列为比 'jello' 更适合 'hello' 的匹配项。 您是否为超过 6M 的字典项找到了有效的解决方案?我这里也有货。 【参考方案1】:使用difflib.get_close_matches
。
>>> words = ['hello', 'Hallo', 'hi', 'house', 'key', 'screen', 'hallo', 'question', 'format']
>>> difflib.get_close_matches('Hello', words)
['hello', 'Hallo', 'hallo']
请查看文档,因为该函数默认返回 3 个或更少的最接近的匹配项。
【讨论】:
仅供参考:difflib.get_close_matches("Hallo", words, len(words), 0)
将提供所有匹配项 :)
Levenshtein 差异也可以使用。有一个很好的python实现pypi.python.org/pypi/python-Levenshtein
difflib 可以工作,但也很慢。 (单词列表里面有 630000+ 个单词)。因此,每次搜索最接近的单词时,检查列表需要 5 到 7 秒!
@Laura 慢和花很长时间是有区别的。 7 秒可能很长,但可能不会很慢。
有人能指出difflib
背后的算法吗?我很想知道它。【参考方案2】:
Peter Norvig 提供了一篇很棒的文章,其中包含完整的源代码(21 行)关于拼写更正。
http://norvig.com/spell-correct.html
我们的想法是为您的单词构建所有可能的编辑,
hello - helo - deletes
hello - helol - transpose
hello - hallo - replaces
hello - heallo - inserts
def edits1(word):
splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]
deletes = [a + b[1:] for a, b in splits if b]
transposes = [a + b[1] + b[0] + b[2:] for a, b in splits if len(b)>1]
replaces = [a + c + b[1:] for a, b in splits for c in alphabet if b]
inserts = [a + c + b for a, b in splits for c in alphabet]
return set(deletes + transposes + replaces + inserts)
现在,在您的列表中查找这些编辑。
Peter 的文章非常值得一读。
【讨论】:
谢谢,这是一个很棒的发现。我正在编写一个集成字典,这可能很有用。【参考方案3】:为您的单词创建一个排序列表,并使用bisect module 根据排序顺序确定排序列表中适合您单词的位置。根据该位置,您可以给出上方和下方的 k 个最近邻,以找到 2k 个最接近的单词。
【讨论】:
你确定这会起作用吗?我不认为字典顺序是 OP 想要的...... 考虑到问题中的代码 sn-p,可能只需要这样一个简单的解决方案。 如果要考虑可能的拼写错误,尤其是在单词开头出现错误时,这将不起作用。但是对于正确的单词确实是一个很好的解决方案。 如果我没记错的话,这应该行不通。例如:zoom
和boom
的拼写非常相似,但它们将位于排序列表的两端。你如何匹配它们?【参考方案4】:
我正在查看此答案,以从列表中获得最接近的匹配项或可能的替代项
difflib.get_close_matches
我找到了Amjith's answer based on Peter Norwig's post,并认为它可能是一个很好的替代品。在我意识到它可能不太适合我的用例之前,我已经用它做了一个类。所以这是一个拼写版本,您可以将它用于不同的单词集。也许最好的用途是人口名称匹配。
import re
from collections import Counter
def words(text): return re.findall(r'\w+', text.lower())
# WORDS = Counter(words(open('big.txt').read()))
class WordMatcher:
def __init__(self, big_text):
self.WORDS=Counter(words(big_text))
self.N = sum(self.WORDS.values())
def P(self, word):
"Probability of `word`."
return self.WORDS[word] / self.N
def correction(self, word):
"Most probable spelling correction for word."
return max(self.candidates(word), key=self.P)
def candidates(self, word):
"Generate possible spelling corrections for word."
return (self.known([word]) or self.known(self.edits1(word)) or self.known(self.edits2(word)) or [word])
def known(self, words):
"The subset of `words` that appear in the dictionary of WORDS."
return set(w for w in words if w in self.WORDS)
def edits1(self, word):
"All edits that are one edit away from `word`."
letters = 'abcdefghijklmnopqrstuvwxyz'
splits = [(word[:i], word[i:]) for i in range(len(word) + 1)]
deletes = [L + R[1:] for L, R in splits if R]
transposes = [L + R[1] + R[0] + R[2:] for L, R in splits if len(R)>1]
replaces = [L + c + R[1:] for L, R in splits if R for c in letters]
inserts = [L + c + R for L, R in splits for c in letters]
return set(deletes + transposes + replaces + inserts)
def edits2(self, word):
"All edits that are two edits away from `word`."
return (e2 for e1 in self.edits1(word) for e2 in self.edits1(e1))
【讨论】:
【参考方案5】:也许heap 可以帮助你。
你有一个名为 Heap
的堆,直到它的大小小于 n
,你使用函数 close
将单词插入到 Heap
[显示这个字符串是否比另一个字符串更接近]。
当n
很小的时候这个方法可以帮到你:)
Heap = []
for word in words:
if len(Heap)<n:
Heap.insert(word)
else
if close(word,Heap[0]): # it means Heap[0] is the nth farthest word until now
Heap.pop():
Heap.insert(word)
【讨论】:
需要实现'close' 这只是一个保存单词的数据结构,它没有解释计算相似度。以上是关于Python:找到最接近的字符串(从列表中)到另一个字符串的主要内容,如果未能解决你的问题,请参考以下文章