如何使用 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
安装它
基本用法:
给定两个数据帧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)
或者如果您只想link最接近的匹配:
fuzzymatcher.fuzzy_left_join(df_left, df_right, left_on, right_on)
我有 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
基本用法:
给定两个数据帧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)
或者如果您只想link最接近的匹配:
fuzzymatcher.fuzzy_left_join(df_left, df_right, left_on, right_on)