基于两列左连接DataFrame:仅将第二表不匹配列设为NaN的实现问题
解决Pandas左连接时仅对不匹配列单独设NaN的问题
需求说明
基于两列对两个DataFrame执行左连接,但要求仅将第二个DataFrame中未匹配的单个列设为NaN,而非整行关联列全部置为NaN。
输入数据
import pandas as pd df1 = pd.DataFrame({"A": [1, 1, 2, 2, 3], "B": [1, 2, 1, 3, 1]}) df2 = pd.DataFrame({"C": [1, 1, 2, 2], "D": [1, 2, 1, 4]})
预期输出
| A | B | C | D |
|---|---|---|---|
| 1 | 1 | 1 | 1 |
| 1 | 2 | 1 | 2 |
| 2 | 1 | 2 | 1 |
| 2 | 3 | 2 | NaN |
| 3 | 1 | NaN | NaN |
现有问题
用户尝试了常规的双列匹配左连接:
joined = pd.merge(df1, df2, left_on=['A', 'B'], right_on=['C', 'D'], how='left')
得到的结果中,第4行(索引3)的C和D均为NaN,但预期中C应保留为2,仅D为NaN:
A B C D 0 1 1 1.0 1.0 1 1 2 1.0 2.0 2 2 1 2.0 1.0 3 2 3 NaN NaN 4 3 1 NaN NaN
这是因为常规双列连接要求A=C且B=D同时满足才会匹配,而我们需要的是先匹配A=C,再在组内单独判断B与D是否匹配。
解决方案
方法一:分步连接+条件赋值
先基于A和C左连接,再通过条件判断将不匹配的D列设为NaN:
# 第一步:基于A=C左连接,保留df1所有行及对应df2的D值 merged = pd.merge(df1, df2, left_on='A', right_on='C', how='left') # 第二步:仅保留需要的列,将B≠D的行的D替换为NaN result = merged[['A', 'B', 'C', 'D']].assign(D=lambda x: x['D'].where(x['B'] == x['D'], pd.NA))
执行结果与预期完全一致:
A B C D 0 1 1 1.0 1 1 1 2 1.0 2 2 2 1 2.0 1 3 2 3 2.0 <NA> 4 3 1 NaN <NA>
方法二:标记匹配关系后处理
先给df2添加匹配标记,再合并后过滤:
# 给df2添加(A,B)格式的匹配标记 df2['match_key'] = df2.apply(lambda row: (row['C'], row['D']), axis=1) # 基于A=C左连接 merged = pd.merge(df1, df2, left_on='A', right_on='C', how='left') # 仅保留匹配成功的D值,否则设为NaN result = merged.assign( D=lambda x: x['D'].where( x.apply(lambda row: (row['A'], row['B']) in df2['match_key'].tolist(), axis=1), pd.NA ) )[['A', 'B', 'C', 'D']]
两种方法都能实现需求,方法一的效率更高,适合处理大数据集。
内容的提问来源于stack exchange,提问作者Rupal
相关产品推荐
相关产品推荐

