我有两个可能重叠的电影片名列表,但可能以不同的形式书写
它们与熊猫有两种不同的数据帧。所以我尝试将map()
函数与fuzzywuzzy
库一起使用,如下所示:
df1.title.map(lambda x: process.extractOne(x, choices=df2.title, score_cutoff=95))
这给出了一些高质量的结果。但是时间复杂度使得我只能在两个数据帧的非常小的子集上运行它。当我尝试递增数据帧的大小时,它会很快变得无法使用。
然后我尝试将fuzzywuzzy
替换为difflib
。而且速度要快得多。但我无法得到我想要的结果。
起初我试过了:
df1.title.map(lambda x: difflib.get_close_matches(x, df2.title, n=1)
这很快但结果的质量很差。甚至错过了一些简单的大写/小写更改。使用cutoff
并没有帮助。
所以我以为我使用的是错误的工具。在文档和示例中,我看到get_close_matches
用于单个单词。在标题中有各种各样的词。
SequenceMatcher
是更好的选择吗?
如果是,那么我如何将其纳入map()
所以它与前面提到的函数相同:只返回最佳结果,并且仅当结果高于某个比率时?
答案 0 :(得分:1)
为了消除因案例差异导致的低分匹配的可能性,我建议将.upper()
或.lower()
应用于您匹配的列。调整案例后,您可以将所有标题的列表编译为ThisList
并应用以下函数(依照您的建议,依赖于SequenceMatcher
)给定tolerance
。
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)
答案 1 :(得分:0)
我编写了一个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)