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

我正在尝试在充满组织名称的 PANDAS 列中寻找潜在匹配项。我目前正在使用 iterrows() 但它在具有 ~70,000 行的数据帧上非常慢。在查看了 StackOverflow 之后,我尝试实现一个 lambda 行(应用)方法,但这似乎几乎没有加快速度,如果有的话。


数据框的前四行如下所示:


index  org_name

0   cliftonlarsonallen llp minneapolis MN

1   loeb and troper llp newyork NY

2   dauby o'connor and zaleski llc carmel IN

3   wegner cpas llp madison WI

以下代码块有效,但需要大约五天的时间来处理:


org_list = df['org_name']

from fuzzywuzzy import process

for 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 列中的每个元素生成最佳匹配?


慕容708150
浏览 612回答 3
3回答

扬帆大鱼

此解决方案利用apply()并应展示合理的性能改进。随意使用scorer并更改threshold以满足您的需求:import pandas as pd, numpy as npfrom fuzzywuzzy import process, fuzzdf = 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 = 40def 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 matchdf['match found'] = df['org_name'].apply(find_match)返回:                                   org_name                                     match found0     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                                             NaN3                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()函数中更广泛的逻辑限制潜在的匹配选项来实现最佳性能改进,例如强制匹配选项以与查询相同的字母开头等。

慕容3067478

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

相关分类

Python