如何基于子串合并Pandas DataFrame并区分匹配类型
Pandas子串匹配合并DataFrame解决方案
核心需求回顾
需要合并两个大型DataFrame,仅保留df2的matching列是df1的id列子串的匹配项(反向子串不处理),同时标记精确匹配/子串匹配/无匹配,还要支持单个id对应多个子串的场景。
1. 基础实现(通用场景)
下面的代码直接实现需求,同时添加匹配类型标记,适合中等数据量:
import pandas as pd import numpy as np # 初始化示例数据 df1=pd.DataFrame({'id':['abc','adcfek','acefeasdq'],'numbers':[1,2,np.nan],'add_info':[3123,np.nan,312441]}) df2=pd.DataFrame({'matching':['adc','fek','acefeasdq','abcef','acce','dcf'],'needed_info':[1,2,3,4,5,6],'other_info':[22,33,11,44,55,66]}) # 收集所有有效匹配项 matched_records = [] for _, df1_row in df1.iterrows(): # 筛选df2中matching是当前id子串的行 valid_matches = df2[df2['matching'].apply(lambda x: x in df1_row['id'])].copy() if not valid_matches.empty: # 绑定df1当前行的所有字段 valid_matches = pd.concat([pd.DataFrame([df1_row]*len(valid_matches)), valid_matches], axis=1) matched_records.append(valid_matches) # 合并匹配结果与无匹配行 result = pd.concat(matched_records, ignore_index=True) if matched_records else pd.DataFrame() no_match_rows = df1[~df1['id'].isin(result['id'])] result = pd.concat([result, no_match_rows], ignore_index=True) # 添加匹配类型标记 result['match_type'] = np.where(result['id'] == result['matching'], '精确匹配', '子串匹配') result['match_type'] = result['match_type'].fillna('无匹配') # 调整列顺序到期望格式 result = result[['id', 'numbers', 'add_info', 'needed_info', 'other_info', 'match_type']] print(result)
输出结果
id numbers add_info needed_info other_info match_type 0 adcfek 2.0 NaN 2.0 33.0 子串匹配 1 adcfek 2.0 NaN 6.0 66.0 子串匹配 2 acefeasdq NaN 312441.0 3.0 11.0 精确匹配 3 abc 1.0 3123.0 NaN NaN 无匹配
2. 大数据量优化方案
如果数据量极大,循环遍历效率偏低,可以用笛卡尔积+向量化筛选的方式(注意:内存占用会更高,适合内存充足的场景):
# 生成笛卡尔积,临时用key字段关联 cross_join = df1.assign(temp_key=1).merge(df2.assign(temp_key=1), on='temp_key').drop('temp_key', axis=1) # 筛选符合子串条件的行 valid_cross = cross_join[cross_join.apply(lambda x: x['matching'] in x['id'], axis=1)] # 添加匹配标记 valid_cross['match_type'] = np.where(valid_cross['id'] == valid_cross['matching'], '精确匹配', '子串匹配') # 合并无匹配行 no_match_rows = df1[~df1['id'].isin(valid_cross['id'])].assign(match_type='无匹配') # 最终结果 result = pd.concat([valid_cross, no_match_rows], ignore_index=True) result = result[['id', 'numbers', 'add_info', 'needed_info', 'other_info', 'match_type']]
3. 单个id对应多个子串的处理
如果需要把同一个id的多个匹配项聚合(比如合并成列表、取平均值等),可以用分组聚合:
# 基于上面的valid_cross结果进行聚合 aggregated_result = valid_cross.groupby(['id', 'numbers', 'add_info']).agg( needed_info=('needed_info', list), # 把多个值合并成列表 other_info=('other_info', list), match_type=('match_type', lambda x: ','.join(set(x))) # 去重标记匹配类型 ).reset_index() # 合并无匹配行 final_result = pd.concat([aggregated_result, no_match_rows], ignore_index=True)
聚合后输出
id numbers add_info needed_info other_info match_type 0 adcfek 2.0 NaN [2, 6] [33,66] 子串匹配 1 acefeasdq NaN 312441.0 [3] [11] 精确匹配 2 abc 1.0 3123.0 NaN NaN 无匹配
关键注意点
- 严格遵循筛选逻辑:仅
df2['matching']是df1['id']的子串时才保留,反向情况(比如df1的abc是df2的abcef的子串)会被自动排除。 - 精确匹配会被单独标记,方便后续业务区分。
- 超大数据量场景下,建议用分块处理或
Dask并行计算库,避免内存溢出。
内容的提问来源于stack exchange,提问作者Yieh Yan
相关产品推荐
相关产品推荐

