为了账号安全,请及时绑定邮箱和手机立即绑定

向量化或加速 PANDAS 列上的 Fuzzywuzzy 字符串匹配

向量化或加速 PANDAS 列上的 Fuzzywuzzy 字符串匹配

慕容708150 2021-07-02 10:11:15
我正在尝试在充满组织名称的 PANDAS 列中寻找潜在匹配项。我目前正在使用 iterrows() 但它在具有 ~70,000 行的数据帧上非常慢。在查看了 StackOverflow 之后,我尝试实现一个 lambda 行(应用)方法,但这似乎几乎没有加快速度,如果有的话。数据框的前四行如下所示:index  org_name0   cliftonlarsonallen llp minneapolis MN1   loeb and troper llp newyork NY2   dauby o'connor and zaleski llc carmel IN3   wegner cpas llp madison WI以下代码块有效,但需要大约五天的时间来处理:org_list = df['org_name']from fuzzywuzzy import processfor index, row in df.iterrows():    x = process.extract(row['org_name'], org_list, limit=2)[1]    if x[1]>93:        df.loc[index, 'fuzzy_match'] = x[0]        df.loc[index, 'fuzzy_match_score'] = x[1]实际上,对于每一行,我将组织名称与所有组织名称的列表进行比较,取前两个匹配项,然后选择第二个最佳匹配项(因为顶部匹配项将是相同的名称),然后设置一个条件分数必须高于 93 才能创建新列。我创建附加列的原因是我不想简单地替换值——我想先仔细检查结果。有没有办法加快这个速度?我阅读了几篇博客文章和 StackOverflow 问题,这些问题讨论了“向量化”这段代码,但我的尝试失败了。我还考虑过简单地创建一个 70,000 x 70,000 Levenshtein 距离矩阵,然后从中提取信息。有没有更快的方法来为列表或 PANDAS 列中的每个元素生成最佳匹配?
查看完整描述

3 回答

?
扬帆大鱼

TA贡献1799条经验 获得超9个赞

此解决方案利用apply()并应展示合理的性能改进。随意使用scorer并更改threshold以满足您的需求:


import pandas as pd, numpy as np

from fuzzywuzzy import process, fuzz


df = pd.DataFrame([['cliftonlarsonallen llp minneapolis MN'],

        ['loeb and troper llp newyork NY'],

        ["dauby o'connor and zaleski llc carmel IN"],

        ['wegner cpas llp madison WI']],

        columns=['org_name'])


org_list = df['org_name']


threshold = 40


def find_match(x):


  match = process.extract(x, org_list, limit=2, scorer=fuzz.partial_token_sort_ratio)[1]

  match = match if match[1]>threshold else np.nan

  return match


df['match found'] = df['org_name'].apply(find_match)

返回:


                                   org_name                                     match found

0     cliftonlarsonallen llp minneapolis MN             (wegner cpas llp madison WI, 50, 3)

1            loeb and troper llp newyork NY             (wegner cpas llp madison WI, 46, 3)

2  dauby o'connor and zaleski llc carmel IN                                             NaN

3                wegner cpas llp madison WI  (cliftonlarsonallen llp minneapolis MN, 50, 0)

如果你只想返回匹配的字符串本身,那么你可以修改如下:


match = match[0] if match[1]>threshold else np.nan

我在此处添加了与列表理解相关的 @user3483203 评论作为替代选项:


df['match found'] = [find_match(row) for row in df['org_name']]

请注意,process.extract()它旨在处理单个查询字符串并将传递的评分算法应用于该查询和提供的匹配选项。因此,您必须针对所有 70,000 个匹配选项(您当前设置代码的方式)评估该查询。因此,您将评估len(match_options)**2(或 4,900,000,000)字符串比较。因此,我认为可以通过find_match()函数中更广泛的逻辑限制潜在的匹配选项来实现最佳性能改进,例如强制匹配选项以与查询相同的字母开头等。


查看完整回答
反对 回复 2021-07-06
?
慕容3067478

TA贡献1773条经验 获得超3个赞

不建议在数据帧上使用 iterrows(),您可以使用 apply() 代替。但这可能不会大大加快速度。慢的是fuzzywuzzy 的提取方法,其中将您的输入与所有70k 行进行比较(字符串距离方法在计算上很昂贵)。因此,如果您打算坚持使用fuzzywuzzy,一个解决方案是将您的搜索限制为例如仅具有相同首字母的搜索。或者,如果您的数据中有另一列可用作提示(州、城市、...)


查看完整回答
反对 回复 2021-07-06
  • 3 回答
  • 0 关注
  • 610 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号