【问题标题】:How to link two dataframes based on the string similarity of one column如何根据一列的字符串相似性链接两个数据框
【发布时间】:2019-09-20 18:12:30
【问题描述】:

我有两个数据框,都有一个 ID 和一个包含字符串的列 Name。它们可能看起来像这样:

数据框:

DF-1                              DF-2
---------------------             ---------------------
     ID          Name                  ID          Name
1    56       aaeessa             1    12    H.P paRt 1 
2    98       1o7v9sM             2    76       aa3esza
3   175    HP. part 1             3   762    stakoverfl 
4     2     stackover             4     2       lo7v9Sm

我想计算一个元素与所有其他元素之间的字符串相似度(例如:Jaccard,Levenshtein),然后选择得分最高的那个。然后匹配这两个 ID,以便我以后可以加入完整的数据帧。结果表应如下所示:

结果:

Result
-----------------
     ID1     ID2
1    56       76
2    98        2
3   175       12
4     2      762

这可以使用双 for 循环轻松实现,但我正在寻找一种优雅(且更快)的方法来实现这一点,也许是 lambdas 列表理解或一些 pandas 工具。也许groupbyidxmax 的一些组合用于相似度得分,但我自己无法完全提出解决方案。

编辑: DataFrame 具有不同的长度,此函数的目的之一是确定较小数据帧的哪些元素出现在较大数据帧中并匹配这些元素,丢弃其余元素。所以在结果表中应该只出现匹配的 ID 对,或 ID1 - NaN 对(假设 DF-1 的行数比 DF-2 多)。

【问题讨论】:

  • 看看 Pandas Dedipe 库:pypi.org/project/pandas-dedupe 它有一个使用模糊匹配的记录链接功能。虽然速度不是很快,但会占用大量 CPU。
  • 感谢您的回答,它适用于不同长度的数据帧吗?
  • 看看我的fuzzy merging函数
  • 我会在下面添加一个示例

标签: python pandas dataframe


【解决方案1】:

使用 pandas 去重包:https://pypi.org/project/pandas-dedupe/

您需要使用人工输入训练分类器,然后它将使用学习设置来匹配整个数据框。

首先pip install pandas-dedupe 试试这个:

import pandas as pd
import pandas_dedupe

df1=pd.DataFrame({'ID':[56,98,175],
                 'Name':['aaeessa', '1o7v9sM', 'HP. part 1']})

df2=pd.DataFrame({'ID':[12,76,762,2],
                 'Name':['H.P paRt 1', 'aa3esza', 'stakoverfl ', 'lo7v9Sm']})


#initiate matching
df_final = pandas_dedupe.link_dataframes(df1, df2, ['Name'])

# reset index
df_final = df_final.reset_index(drop=True)

# print result

print(df_final)

    ID        Name  cluster id  confidence
0   98     1o7v9sm         0.0    1.000000
1    2     lo7v9sm         0.0    1.000000
2  175  hp. part 1         1.0    0.999999
3   12  h.p part 1         1.0    0.999999
4   56     aaeessa         2.0    0.999967
5   76     aa3esza         2.0    0.999967
6  762  stakoverfl         NaN         NaN

您可以看到匹配的对被分配了一个集群和置信水平。无与伦比的是nan。您现在可以根据需要分析此信息。例如,也许只取置信度高于 80% 的结果。

【讨论】:

  • 虽然输出不是您想要的格式,但我相信您可以创建另一个 stackoverflow 问题以将其转换为所需的格式。我做不到! (还在学习中)
  • 我在 Windows 上使用 Anaconda,无法让这个软件包工作,通过 pip3 安装它,因为 conda 版本仅适用于 linux,并且无法找到该软件包:S
  • 我在 Windows 10 上也使用它和 anaconda。错误信息是什么?
  • 当你输入pip install pandas-dedupe 时会发生什么?
  • 我会更正并指出我的错误:使用 pip3 将软件包安装在不同的目录中。在 anaconda 提示符中使用 pip,现在可以正常工作了!
【解决方案2】:

我建议你一个名为 Python Record Linkage Toolkit 的库。

导入库后,您必须为要比较的源编制索引,如下所示:

 indexer = recordlinkage.Index()

 #using url as intersection
 indexer.block('id')
 candidate_links = indexer.index(df_1, df_2)

 c = recordlinkage.Compare()

假设您想根据字符串的相似性进行比较,但它们并不完全匹配:

 c.string('name', 'name', method='jarowinkler', threshold=0.85)

如果你想要完全匹配,你应该使用:

c.exact('name')

【讨论】:

  • 这可以使用不同长度的数据帧吗?以及具有不同名称的列?或者我应该重命名该列?我会去图书馆看看
  • 您可以使用具有不同名称的列,但数据框应具有相同的大小或拆分为唯一的数据框。网上有教程和文档。
  • 感谢您的回答,更新了问题以澄清问题。
【解决方案3】:

使用链接答案中的fuzzy_wuzzy 函数:

from fuzzywuzzy import fuzz
from fuzzywuzzy import process

mrg = fuzzy_merge(df1, df2, 'Name', 'Name', threshold=70)\
      .merge(df2, left_on='matches', right_on='Name', suffixes=['1', '2'])\
      .filter(like='ID')

输出

   ID1  ID2
0   56   76
1   98    2
2  175   12
3    2  762

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-17
    • 2015-06-24
    • 2021-11-08
    • 2021-05-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-29
    相关资源
    最近更新 更多