如何基于姓名包含关系合并格式各异的Pandas DataFrame?
高效合并含复杂姓名列的DataFrame方案
针对你提出的合并需求,这里提供一套分步解决方案,既解决and/&分隔的多姓名问题,也能实现基于姓氏匹配的合并:
1. 拆分多姓名条目
首先把df2中用and或&分隔的姓名拆分成单独行,让每个行对应一个独立姓名,解决nameparser无法处理多姓名的问题:
import pandas as pd import re # 原始数据初始化 data1 = [['Anna','Tessmann',10], ['Ben','Fachmann',20], ['John','Smith',10]] df1 = pd.DataFrame(data1, columns=['FirstName','LastName','Info1']) data2 = [['Ben Fachmann',30], ['School AAA',40], ['John and Melissa Smith',50], ['Bob & Anna Tessmann',20]] df2= pd.DataFrame(data2, columns=['Name','Info2']) # 用正则拆分and/&分隔的姓名,处理前后空格 df2['split_names'] = df2['Name'].str.split(r'\s+(?:and|&)\s+') # 拆分后展开为多行,每个姓名对应原Info2值 df2_exploded = df2.explode('split_names').reset_index(drop=True)
2. 基于姓氏匹配合并
方案A:快速模糊匹配(仅姓氏)
如果只需要基于「df2姓名包含df1姓氏」的规则,用交叉连接+过滤的方式简单直接:
# 交叉连接两个DataFrame,然后过滤匹配项 cross_join = df1.merge(df2_exploded, how='cross') matched = cross_join[cross_join['split_names'].str.contains(cross_join['LastName'], case=False)] # 整理最终结果,去重后保留关键列 final_result = matched[['FirstName', 'LastName', 'Info1', 'Name', 'Info2']].drop_duplicates()
方案B:高效精确匹配(完整姓氏单词)
如果数据量较大,交叉连接效率偏低,可改用正则提取姓氏后合并,同时避免部分匹配(比如防止"Smithson"被误判为包含"Smith"):
# 构建完整姓氏的正则匹配模式 last_name_regex = r'\b(' + '|'.join(df1['LastName'].tolist()) + r')\b' # 从拆分后的姓名中提取匹配的姓氏 df2_exploded['matched_lastname'] = df2_exploded['split_names'].str.extract(last_name_regex, flags=re.IGNORECASE) # 基于提取的姓氏和df1合并 final_result = df2_exploded.dropna(subset=['matched_lastname']).merge( df1, left_on='matched_lastname', right_on='LastName', how='left' )[['FirstName', 'LastName', 'Info1', 'Name', 'Info2']]
方案C:名+姓精确匹配(可选)
如果需要更严格的名+姓匹配,拆分后可以用nameparser解析单个姓名,再和df1的名、姓对应:
from nameparser import HumanName # 解析拆分后的单个姓名 df2_exploded['parsed_name'] = df2_exploded['split_names'].apply(HumanName) df2_exploded['parsed_first'] = df2_exploded['parsed_name'].apply(lambda x: x.first) df2_exploded['parsed_last'] = df2_exploded['parsed_name'].apply(lambda x: x.last) # 基于名+姓精确合并 final_result = df2_exploded.merge( df1, left_on=['parsed_first', 'parsed_last'], right_on=['FirstName', 'LastName'], how='inner' )[['FirstName', 'LastName', 'Info1', 'Name', 'Info2']]
结果说明
- 无关内容(如"School AAA")因不包含df1中的任何姓氏,会被自动过滤
- 多姓名条目拆分后,每个姓名会单独和df1匹配,确保不遗漏有效关联
- 三种方案可根据数据规模和匹配精度需求选择
内容的提问来源于stack exchange,提问作者Anna
相关产品推荐
相关产品推荐

