【问题标题】:Python: How to correct misspelled namesPython:如何更正拼写错误的名称
【发布时间】:2016-12-17 07:46:17
【问题描述】:

我有一个城市名称列表,其中一些拼写错误:

['bercelona', 'emstrdam', 'Praga']

还有一个包含所有可能的城市名称的列表:

['New York', 'Amsterdam', 'Barcelona', 'Berlin', 'Prague']

我正在寻找一种算法,能够找到第一个和第二个列表的名称之间最接近的匹配项,并返回第一个列表及其拼写正确的名称。所以它应该返回以下列表:

['Barcelona', 'Amsterdam', 'Prague']

【问题讨论】:

  • 使用了一些语言工具包

标签: python spell-checking


【解决方案1】:

您可以使用内置的 Ratcliff 和 Obershelp 算法:

def is_similar(first, second, ratio):
    return difflib.SequenceMatcher(None, first, second).ratio() > ratio


first = ['bercelona', 'emstrdam', 'Praga']
second = ['New York', 'Amsterdam', 'Barcelona', 'Berlin', 'Prague']

result = [s for f in first for s in second if is_similar(f,s, 0.7)]
print result
['Barcelona', 'Amsterdam', 'Prague']

其中 0.7 是相似系数。它可能会为您的案例做一些测试并设置此值。它显示了两个字符串的相似程度(1 - 它是相同的字符串,0 - 非常不同的字符串)

【讨论】:

  • 根据 difflib 的文档,它不是 Levenshtein 距离算法,但它有类似的目的,我赞成使用标准库函数。
  • @Gribouillis,你是对的!它使用 Ratcliff 和 Obershelp 算法。我忘了。
  • @ebeneditos 您可以关注文档:docs.python.org/2/library/…
  • 谢谢@pivanchy!这是一种完全不同的方法,但它很有帮助。请编辑您的第一句话,以澄清它使用的是 Ratcliff/Obershelp 算法,而不是 Levensthen 的算法,如果有一天没有更好的答案,我会接受。
  • @ebeneditos,已修复!这种方法需要一些研究来选择最佳比率,但它可以很好地用于自己的生产目的
【解决方案2】:

这可能是一个名为fuzzywuzzy 的优秀包的很好用例。

from fuzzywuzzy import fuzz
import numpy as np

bad = ['bercelona', 'emstrdam', 'Praga']

good = ['New York', 'Amsterdam', 'Barcelona', 'Berlin', 'Prague']

# you can even set custom threshold and only return matches if above certain
# matching threshold
def correctspell(word, spellcorrect, thresh = 70):
    mtchs = map(lambda x: fuzz.ratio(x, word) if fuzz.ratio(x, word) > thresh else None, spellcorrect)
    max = np.max(mtchs)
    if max is not None:
        return spellcorrect[mtchs.index(max)]
    else:
        return None

# get correct spelling
map(lambda x: correctspell(x, good, thresh = 70), bad) # ['Barcelona', 'Amsterdam', 'Prague']

【讨论】:

    【解决方案3】:

    首先,你应该在字符串之间使用 Levenshtein 距离,我找到了一个链接,链接如下Levenshtein Distance Algorithm for Python

    # Define Levenshtein distance function (from the mentioned link)
    def levenshtein(s1, s2):
        if len(s1) < len(s2):
            return levenshtein(s2, s1)
    
        if len(s2) == 0:
            return len(s1)
    
        previous_row = range(len(s2) + 1)
        for i, c1 in enumerate(s1):
            current_row = [i + 1]
            for j, c2 in enumerate(s2):
                insertions = previous_row[j + 1] + 1 
                deletions = current_row[j] + 1  
                substitutions = previous_row[j] + (c1 != c2)
                current_row.append(min(insertions, deletions, substitutions))
            previous_row = current_row
    
        return previous_row[-1]
    

    一旦你得到这个,你应该使一个函数能够找到给定字符串和具有正确拼写名称的列表之间最接近的匹配项。

    names_list = ['bercelona', 'emstrdam', 'Praga']
    good_names = ['New York', 'Amsterdam', 'Barcelona', 'Berlin', 'Prague']
    
    # Define a function that returns the best match
    def get_closest_match(name, real_names):
        levdist = [levenshtein(name, real_name) for real_name in real_names]
        for i in range(len(levdist)):
            if levdist[i] == min(levdist):
                return real_names[i]
    
    # Loops the first list
    final_list = [ get_closest_match(name, good_names) for name in names_list ]
    

    最后你只需要用这个函数循环第一个列表。结果如下:

    >>> print final_list
    ['Barcelona', 'Amsterdam', 'Prague']
    

    【讨论】:

    • 您在一分钟内提出并回答了自己的问题?
    • 当您提出问题时,最后会有一个“回答您自己的问题”选项。 StackOverflow 鼓励这样做,以便其他人从中受益。这是我几天前遇到的一个问题,我在这里找不到解决方案。因此我制作了这个算法,我现在发布它,以便其他人可以看到它并提出更好的答案:)
    • @Dan-Dev:明确鼓励自行回答,请参阅stackoverflow.com/help/self-answer
    • @martijn-pieters 在一分钟内提出和回答问题似乎有点极端。他说他几天前遇到了这个问题。不过他当时没有问。还说在搜索 stackoverflow.com/search?q=levenshtein 显示超过 3,000 次点击时,SO 上没有关于该问题的任何内容。是否鼓励发布发布者知道答案的问题?
    • 您阅读了链接吗?是的,这是鼓励的。关键是这个网站收集知识,而自我回答是分享知识的一种方式。如果您对此有任何疑问,请随时在Meta Stack Overflow 上提问! :-)
    猜你喜欢
    • 2017-10-14
    • 1970-01-01
    • 2021-03-08
    • 2011-11-24
    • 1970-01-01
    • 2011-07-15
    • 1970-01-01
    • 2012-09-09
    • 1970-01-01
    相关资源
    最近更新 更多