本文介绍如何将含斜杠分隔姓氏(如 "A/B/C")的 DataFrame 与另一张含姓名-公司映射关系的表进行精准匹配,仅返回所有分项姓氏共同归属的公司名称,避免因单个姓氏多公司导致的误匹配。
本文介绍如何将含斜杠分隔姓氏(如 "a/b/c")的 dataframe 与另一张含姓名-公司映射关系的表进行精准匹配,仅返回所有分项姓氏**共同归属**的公司名称,避免因单个姓氏多公司导致的误匹配。
在实际数据处理中,常遇到类似场景:一张表存储复合标识(如 / 分隔的多个姓氏),另一张表存储细粒度映射(如每个姓氏对应一家或多家公司)。目标不是“任一匹配”,而是严格要求该行所有姓氏必须同时存在于同一家公司下,才返回该公司名称——这本质是求多个集合的交集。
以下提供两种稳健方案,推荐使用第二种(基于 frozenset),因其不依赖姓氏顺序,逻辑更严谨、可扩展性更强。
✅ 方案二:基于集合交集的通用解法(推荐)
核心思想:
- 将 df1['Name'] 拆分为姓氏列表,并转换为不可变集合(frozenset);
- 将 df2 中每个姓氏对应的公司展开,按公司分组聚合其覆盖的姓氏集合;
- 对 df1 中每行的姓氏集合,与 df2 中各公司的姓氏集合做完全匹配(即 ==),找出唯一满足“该集合等于公司所覆盖姓氏集合”的公司。
import pandas as pd
# 构造示例数据
df1 = pd.DataFrame({'Name': ['A/B/C', 'D/E', 'F/G']})
df2 = pd.DataFrame({
'First Name': ['Adam', 'Harry', 'Andrew', 'Mike', 'Sheila', 'Hash', 'Michelle', 'Morty'],
'Last Name': ['A', 'B', 'C', 'A', 'D', 'E', 'F', 'G'],
'Firm Names': ['Firm1','Firm1','Firm1', 'Firm2','Firm3','Firm3','Firm4','Firm4']
})
# 步骤1:为 df1 添加姓氏列表和 frozenset
df1_sets = df1.assign(
**{
'Last Name': df1['Name'].str.split('/'),
'sets': lambda x: x['Last Name'].apply(frozenset)
}
)
# 步骤2:展开 df1 姓氏,左连接 df2,按 index + Firm Names 聚合姓氏集合
df2_aggregated = (
df1_sets.explode('Last Name')
.reset_index()
.merge(df2, left_on='Last Name', right_on='Last Name', how='left')
.groupby(['index', 'Firm Names'])
.agg(sets=('Last Name', frozenset))
.reset_index(level=1)
)
# 步骤3:与原始 df1 合并,筛选出集合完全匹配的 Firm Names
result = (
df1_sets.merge(df2_aggregated, on='index', how='left')
.loc[lambda d: d['sets'] == d['sets_y']] # 关键:精确集合匹配
[['Name', 'Firm Names']]
.drop_duplicates(subset=['Name'], keep='first') # 防止同一行多匹配
)
print(result)
输出:
Name Firm Names 0 A/B/C Firm1 1 D/E Firm3 2 F/G Firm4
⚠️ 注意事项:
- 若某行姓氏(如 A/B/C)在 df2 中分别属于 Firm1(含 A,B,C)、Firm2(仅含 A),则仅 Firm1 满足“全部覆盖”,Firm2 被自动排除;
- 使用 frozenset 而非 set 是因 set 不可哈希,无法用于 groupby 或 merge;
- .drop_duplicates(...) 确保每行 Name 最多返回一个 Firm Names,即使存在多个公司恰好覆盖相同姓氏组合(极少见,但防错必备);
- 若无任何公司完全覆盖某行所有姓氏,对应 Firm Names 将为 NaN,可根据业务需求用 .fillna('Not Found') 处理。
该方法逻辑清晰、鲁棒性强,适用于任意顺序、任意数量的 / 分隔项,是解决此类“多值全匹配”问题的标准实践。











