基于字符串部分匹配合并DataFrame的问题求助
解决方案:Pandas 模糊匹配合并(高效且准确)
问题背景
需要基于Name列合并两个DataFrame,但因名称存在特殊字符(如.、')、后缀差异(如Jr/Jr.、III缺失)、昵称/全名差异(如Nic/Nicolas)导致直接合并失败。试过fuzzywuzzy的process.extractOne速度过慢,difflib匹配结果不准确,需要高效且精准的合并方案。
数据示例
df1
| Name | Performance |
|---|---|
| Matt Cole Jr | 89 |
| Steve Smith | 72 |
| DJ Watson | 91 |
| Amar'e George | 92 |
| Steve Nurkić | 84 |
| Charles Long | 87 |
| Nic Kitch | 73 |
df2
| Name | Year |
|---|---|
| Matt Cole Jr. | 2023 |
| Steve Smith III | 2022 |
| D.J. Watson | 2021 |
| Amare George | 2020 |
| Steve Nurkic | 2021 |
| Charles Long Jr. | 2022 |
| Nicolas Kitch | 2023 |
期望合并结果
| Name | Year | Performance |
|---|---|---|
| Matt Cole Jr. | 2023 | 89 |
| Steve Smith III | 2022 | 72 |
| D.J. Watson | 2021 | 91 |
| Amare George | 2020 | 92 |
| Steve Nurkic | 2021 | 84 |
| Charles Long Jr. | 2022 | 87 |
| Nicolas Kitch | 2023 | 73 |
尝试过的方法
- fuzzywuzzy(速度过慢)
from fuzzywuzzy import process dfm = pd.DataFrame(df2["Name"].apply(lambda x: process.extractOne(x, df1["Name"])) .tolist()])
- difflib(匹配错误)
import difflib from functools import partial def get_closest_match(x,y): """x=possibilities , y = input""" f = partial( difflib.get_close_matches, possibilities=x.unique(), n=1,cutoff=0.5) matches = y.astype(str).drop_duplicates().map(f).fillna('').str[0] return pd.DataFrame([y,matches.rename('Name')]).T temp = get_closest_match(df2['Name'],df1['Name']) display(temp)
可行解决方案
方案1:先标准化名称,再用快速模糊匹配(推荐)
先对两个DataFrame的Name列做标准化处理,消除格式差异,再用rapidfuzz(比fuzzywuzzy快数十倍)做模糊匹配,最后合并。
步骤1:安装依赖
pip install rapidfuzz pandas
步骤2:定义名称标准化函数
import pandas as pd from rapidfuzz import process, fuzz def standardize_name(name): # 去除特殊字符(撇号、点号) name = name.replace("'", "").replace(".", "") # 移除重音符号并转小写(比如ć转c) name = name.normalize('NFKD').encode('ascii', errors='ignore').decode('utf-8').lower() return name
步骤3:添加标准化名称列
df1['standard_name'] = df1['Name'].apply(standardize_name) df2['standard_name'] = df2['Name'].apply(standardize_name)
步骤4:匹配并合并
# 为df2的每个标准化名称匹配df1中最相似的结果 df2['matched_standard_name'] = df2['standard_name'].apply( lambda x: process.extractOne(x, df1['standard_name'], scorer=fuzz.WRatio)[0] ) # 合并数据并整理列 merged_df = df2.merge(df1, left_on='matched_standard_name', right_on='standard_name', how='left') merged_df = merged_df[['Name_x', 'Year', 'Performance']].rename(columns={'Name_x': 'Name'}) # 输出结果 print(merged_df)
方案2:直接使用rapidfuzz批量匹配(无需标准化)
如果不想做标准化,直接用rapidfuzz的批量匹配功能,效率远高于fuzzywuzzy:
from rapidfuzz import process, fuzz # 批量匹配df2的Name到df1的Name matches = process.extract(df2['Name'].tolist(), df1['Name'].tolist(), scorer=fuzz.WRatio, limit=1) # 提取匹配结果并映射Performance列 df2['matched_name'] = [match[0] for match in matches] df2['Performance'] = df2['matched_name'].map(df1.set_index('Name')['Performance']) # 整理结果列 merged_df = df2[['Name', 'Year', 'Performance']] print(merged_df)
方案说明
- 标准化的作用:消除格式差异(如
.、'、重音),让模糊匹配更精准,同时减少计算量。 - rapidfuzz优势:C语言实现的模糊匹配算法,速度比
fuzzywuzzy快数倍,适合处理较大数据集。 - WRatio评分器:能较好处理大小写、空格、后缀差异,比默认评分器更适合人名匹配。
内容的提问来源于stack exchange,提问作者bangbanggggg
相关产品推荐
相关产品推荐

