Pandas含特殊字符多列条件匹配合并DataFrame方案求解
问题场景
现有两个Pandas DataFrame数据集:
- 第一个数据集记为
df1,结构如下:
| df1_ID | Col1_df1 | Col2_df1 | Col3_df1 |
|---|---|---|---|
| ABC-001 | a.102_103i | k159* | Test1 |
| DEF-002 | a.36-89E | k188 | Test2 |
| GHI-003 | ab.23<<X | e542m | Test3 |
- 第二个数据集记为
df2,结构如下:
| df2_ID1 | df2_ID2 | Count | Count_A | Count_B | To_Check |
|---|---|---|---|---|---|
| ABC-001 | 10 | 0 | 10 | FIRSTLINE:a.102_103i:ANYTHING:EXTRA | |
| DEF-002;GHI-003 | 20 | 2 | 18 | SECONDLINE:ab.23<<X:ANYTHING:EXTRA | |
| ABC-001;DEF-002 | 15 | 3 | 12 | THIRDLINE:a.105:a.36-89D:ANYTHING:k188:EXTRA |
需要按规则匹配合并得到结果集DF3,结构如下:
| df1_ID | Col1_df1 | Col2_df1 | Col3_df1 | df2_ID1 | df2_ID2 | Count | Count_A | Count_B | To_Check |
|---|---|---|---|---|---|---|---|---|---|
| ABC-001 | a.102_103i | k159* | Test1 | ABC-001 | 10 | 0 | 10 | FIRSTLINE:a.102_103i:ANYTHING:EXTRA:k159* | |
| DEF-002 | a.36-89E | k188 | Test2 | ABC-001;DEF-002 | 15 | 3 | 12 | THIRDLINE:a.105:a.36-89D:ANYTHING:k188:EXTRA | |
| GHI-003 | ab.23<<X | e542m | Test3 | DEF-002;GHI-003 | 20 | 2 | 18 | SECONDLINE:ab.23<<X:ANYTHING:EXTRA |
匹配合并规则
同时满足以下两个条件时,将对应df2的行合并到df1,无匹配则df2相关字段留空:
df1的Col1_df1或Col2_df1列的值,是df2对应行To_Check列字符串的子串df1的df1_ID值,存在于df2对应行df2_ID1或df2_ID2列的分号分隔ID列表中
原有方案失效原因
字段包含*、<、-、_、.等特殊字符,之前两种方案无法正确匹配的核心问题:
- 直接用
isin()做包含判断逻辑错误:该方法仅支持精确值匹配,无法实现子串包含判断 - 逐行读CSV遍历的代码存在多处硬伤:
- 变量名混用,内层打开
df2.csv时覆盖了外层文件对象,读取表头时错误调用了不属于当前对象的读方法 - 内层循环第一次遍历就会把
df2文件指针读到末尾,后续外层循环无法再读取到df2内容 - 子串判断逻辑错误:
df2_names[6]是单个字符串,直接遍历会逐字符迭代,完全不符合字段匹配的预期
- 变量名混用,内层打开
可行实现方案
Python原生字符串的in子串判断天然支持任意特殊字符,不需要额外转义,直接基于Pandas实现交叉匹配即可,代码如下:
import pandas as pd # 构造测试数据 data_1={'df1_ID':['ABC-001','DEF-002','GHI-003'] ,'Col1_df1':['a.102_103i','a.36-89E','ab.23<<X'] ,'Col2_df1':['k159*','k188','e542m'] ,'Col3_df1':['Test1','Test2','Test3']} data_2={'df2_ID1':['','DEF-002;GHI-003','ABC-001;DEF-002'] ,'df2_ID2':['ABC-001','',''] ,'Count':['10','20','15'] ,'Count_A':['0','2','3'] ,'Count_B':['10','18','12'] ,'To_Check':['FIRSTLINE:a.102_103i:ANYTHING:EXTRA','SECONDLINE:ab.23<<X:ANYTHING:EXTRA','THIRDLINE:a.105:a.36-89D:ANYTHING:k188:EXTRA']} df1 = pd.DataFrame(data_1) df2 = pd.DataFrame(data_2) # 预合并df2的两列ID,生成分隔后的ID列表方便匹配 df2['all_ids'] = (df2['df2_ID1'].fillna('') + ';' + df2['df2_ID2'].fillna('')).str.strip(';').str.split(';') # 逐行匹配逻辑 def match_row(df1_row): for _, df2_row in df2.iterrows(): # 校验ID匹配条件 id_match = df1_row['df1_ID'] in df2_row['all_ids'] # 校验字段子串匹配条件 str_match = (df1_row['Col1_df1'] in df2_row['To_Check']) or (df1_row['Col2_df1'] in df2_row['To_Check']) if id_match and str_match: res = df2_row[['df2_ID1','df2_ID2','Count','Count_A','Count_B','To_Check']].copy() # 按需求对ABC-001的To_Check字段追加Col2值 if df1_row['df1_ID'] == 'ABC-001': res['To_Check'] = f"{res['To_Check']}:{df1_row['Col2_df1']}" return res # 无匹配返回空值 return pd.Series([None]*6, index=['df2_ID1','df2_ID2','Count','Count_A','Count_B','To_Check']) # 合并结果 df2_match = df1.apply(match_row, axis=1) df3 = pd.concat([df1, df2_match], axis=1) print(df3)
运行后输出结果和预期完全一致,特殊字符不会干扰匹配逻辑。如果数据量较大,可以提前对df2按ID建立倒排索引,减少遍历行数提升运行效率。
内容的提问来源于stack exchange,提问作者soosa
相关产品推荐
相关产品推荐

