13

如何告诉 difflib.get_close_matches() 忽略大小写?我有一本字典,它有一个定义的格式,包括大写。但是,测试字符串可能有全大写或没有大写,它们应该是等价的。但是,结果需要正确大写,所以我不能使用修改过的字典。

import difflib

names = ['Acacia koa A.Gray var. latifolia (Benth.) H.St.John',
    'Acacia koa A.Gray var. waianaeensis H.St.John',
    'Acacia koaia Hillebr.',
    'Acacia kochii W.Fitzg. ex Ewart & Jean White',
    'Acacia kochii W.Fitzg.']
s = 'Acacia kochi W.Fitzg.'

# base case: proper capitalisation
print(difflib.get_close_matches(s,names,1,0.9))

# this should be equivalent from the perspective of my program
print(difflib.get_close_matches(s.upper(),names,1,0.9))

# this won't work because of the dictionary formatting
print(difflib.get_close_matches(s.upper().capitalize(),names,1,0.9))

输出:

['Acacia kochii W.Fitzg.']
[]
[]

工作代码:

根据 Hugh Bothwell 的回答,我对代码进行了如下修改以获得一个可行的解决方案(当返回多个结果时它也应该有效):

import difflib

names = ['Acacia koa A.Gray var. latifolia (Benth.) H.St.John',
    'Acacia koa A.Gray var. waianaeensis H.St.John',
    'Acacia koaia Hillebr.',
    'Acacia kochii W.Fitzg. ex Ewart & Jean White',
    'Acacia kochii W.Fitzg.']
test = {n.lower():n for n in names}    
s1 = 'Acacia kochi W.Fitzg.'   # base case
s2 = 'ACACIA KOCHI W.FITZG.'   # test case

results = [test[r] for r in difflib.get_close_matches(s1.lower(),test,1,0.9)]
results += [test[r] for r in difflib.get_close_matches(s2.lower(),test,1,0.9)]
print results

输出:

['Acacia kochii W.Fitzg.', 'Acacia kochii W.Fitzg.']
4

3 回答 3

13

我没有看到任何快速的方法可以让 difflib 进行不区分大小写的比较。

快速而肮脏的解决方案似乎是

  • 制作一个将字符串转换为某种规范形式的函数(例如:大写、单行距、无标点符号)

  • 使用该函数制作 {canonical string: original string} 的字典和 [canonical string] 的列表

  • 对规范字符串列表运行 .get_close_matches,然后通过 dict 插入结果以取回原始字符串

于 2012-07-08T17:06:28.913 回答
4

经过大量搜索后,我很遗憾地看到没有针对这个明显用例的简单预设答案。

唯一的选择似乎是“FuzzyWuzzy”库。然而它和 Python 一样依赖 Levenshtein Distance difflib,而且它的 API 不是生产质量的。它更晦涩的方法确实不区分大小写,但它没有提供直接或简单的替代get_close_matches.

所以这是我能想到的最简单的实现:

import difflib

def get_close_matches_icase(word, possibilities, *args, **kwargs):
    """ Case-insensitive version of difflib.get_close_matches """
    lword = word.lower()
    lpos = {p.lower(): p for p in possibilities}
    lmatches = difflib.get_close_matches(lword, lpos.keys(), *args, **kwargs)
    return [lpos[m] for m in lmatches]
于 2019-04-09T10:30:05.097 回答
1

@gatopeich 有正确的想法,但问题是可能有许多字符串在大小写上有所不同。我们当然希望它们全部出现在我们的结果中,而不仅仅是其中之一!

以下改编设法做到这一点:

def get_close_matches_icase(word, possibilities, *args, **kwargs):
    """ Case-insensitive version of difflib.get_close_matches """
    lword = word.lower()
    lpos = {}
    for p in possibilities:
        if p.lower() not in lpos:
            lpos[p.lower()] = [p]
        else:
            lpos[p.lower()].append(p)
    lmatches = difflib.get_close_matches(lword, lpos.keys(), *args, **kwargs)
    ret = [lpos[m] for m in lmatches]
    ret = itertools.chain.from_iterable(ret)
    return set(ret)
于 2019-12-21T19:26:28.323 回答