我有 2 个可能重叠的电影标题列表,但可能以不同的形式编写。
它们与熊猫位于 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()
中,使其与上述函数相同:仅返回最佳结果,并且仅在结果高于某个比率时才返回?
为了消除由于大小写差异而导致低分匹配的可能性,我建议将.upper()
或.lower()
应用于要匹配的列。 调整大小写后,您可以将所有标题的列表编译成ThisList
,并使用给定的tolerance
应用以下功能(如您所建议的,依赖于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
安装它
您可以在此处找到存储库,在此处找到文档。
基本用法:
给定要模糊连接的 df_left
和 df_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)
或者,如果您只想链接最接近的匹配项:
fuzzymatcher.fuzzy_left_join(df_left, df_right, left_on, right_on)