如何使用 difflib 和 pandas 模糊匹配电影片名?

How to fuzzy match movie titles with difflib and pandas?

我有 2 个可能重叠的电影标题列表,但可能以不同的形式编写。
它们位于 pandas 的 2 个不同的数据帧中。所以我尝试将 map() 函数与 fuzzywuzzy 库一起使用,如下所示:

df1.title.map(lambda x: process.extractOne(x, choices=df2.title, score_cutoff=95))

这提供了一些高质量的结果。但是时间复杂度如此之高,以至于我只能 运行 它在两个数据帧的一个非常小的子集上。当我尝试增加数据帧的大小时,它很快就变得不可用了。

然后我尝试用difflib替换fuzzywuzzy。而且速度要快得多。但是我得不到我想要的结果。

起初我试过:

df1.title.map(lambda x: difflib.get_close_matches(x, df2.title, n=1)

速度很快,但结果质量很差。甚至缺少一些简单的大写/小写更改。玩 cutoff 没有帮助。

所以我以为我用错了工具。在文档和示例中,我看到 get_close_matches 用于单个单词。标题中有各种各样的词。

SequenceMatcher 是更好的选择吗?

如果是,那么我如何将它放入 map() 中,使其与上述功能相同: return 只有最好的结果,并且只有当结果高于 a一定比例?

为了消除 low-score 由于 case-differences 匹配的可能性,我建议将 .upper().lower() 应用到您正在匹配的列。调整大小写后,您可以将所有标题的列表编译成 ThisList 并使用给定的 tolerance.[=17 应用以下函数(如您所建议的,依赖于 SequenceMatcher) =]

def fuzzy_group_list_elements(ThisList,Tolerance):
    from difflib import SequenceMatcher
    Groups = {}
    TempList = ThisList.copy()
    for Elmt in TempList:
        if Elmt not in Groups.keys():
            Groups[Elmt] = []
        for OtherElmt in TempList:
            if SequenceMatcher(None,Elmt,OtherElmt).quick_ratio() > Tolerance:
                Groups[Elmt] = Groups[Elmt] + [OtherElmt]
                TempList.remove(OtherElmt)
    Groups[Elmt] = list(set(Groups[Elmt]))
    return dict((v,k) for k in Groups for v in Groups[k])

然后您可以将上述函数应用于包含电影标题的数据框列:

Mapping = fuzzy_group_list_elements(ThisList,0.85)
df['Matched Title'] = df['Title'].replace(Mapping)

我写了一个Python包来解决这个问题。除其他外,它解决了问题的 n^2 复杂性(例如,对于两个长度为 100 的数据集,您的代码需要进行 10,000 次比较)。

您可以使用 pip install fuzzymatcher

安装它

您可以找到存储库 here and docs here

基本用法:

给定两个数据帧df_leftdf_right,你想模糊连接,你可以写如下:

from fuzzymatcher import link_table, left join

# Columns to match on from df_left
left_on = ["fname", "mname", "lname",  "dob"]

# Columns to match on from df_right
right_on = ["name", "middlename", "surname", "date"]

# The link table potentially contains several matches for each record
fuzzymatcher.link_table(df_left, df_right, left_on, right_on)

或者如果您只想link最接近的匹配:

fuzzymatcher.fuzzy_left_join(df_left, df_right, left_on, right_on)