Pandas右连接出现NaN索引与新增key列问题及正确实现方法
问题场景与疑问
用户创建两个DataFrame并执行右连接操作,代码如下:
import pandas as pd df1 = pd.DataFrame({'key':[1,2,3], 'values':[11,22,33]}) # df1内容: # key values # 0 1 11 # 1 2 22 # 2 3 33 df2 = pd.DataFrame({'key':[1,2,4],'values':[11,222,44]}) # df2内容: # key values # 0 1 11 # 1 2 222 # 2 4 44 # 执行右连接 df12j3 = df1.join(df2, lsuffix='df1', rsuffix='df2', on='key', how='right')
实际得到的结果:
key keydf1 valuesdf1 keydf2 valuesdf2 NaN 0 NaN NaN 1 11 0.0 1 1.0 11.0 2 222 1.0 2 2.0 22.0 4 44
用户期望的结果:
keydf1 valuesdf1 keydf2 valuesdf2 0 1 11 1 11 1 2 22 2 222 2 NaN NaN 4 44
用户提出两个疑问:
- 为什么右连接结果中会生成新的
key列并出现NaN索引? - 如何实现得到期望的连接结果?
问题解答
一、异常结果的原因
DataFrame.join()的on参数是指定左DataFrame的列或索引作为连接键,但右DataFrame不会自动同步把对应列设为索引。当使用how='right'时,结果的索引会直接沿用右DataFrame的原有索引,而左DataFrame中没有匹配右df里key=4的行,对应的左df索引就会显示NaN。
另外,那个多余的key列其实是右df原本的key列(因为它没被设为连接键,所以作为普通列保留了),而keydf1、keydf2是因为左右df都有key列,通过后缀区分后的列名。本质是用join的方式不对,把左df的列当连接键,但右df还是用自身索引参与连接,导致逻辑混乱,才出现了多余列和异常索引。
二、实现期望结果的方法
方法1:用merge()替代join()(推荐)
merge()是Pandas专门用于列与列之间连接的方法,更适合这种非索引列的连接场景:
# 基础右连接,得到接近期望的结果 df_result = df1.merge(df2, on='key', how='right', suffixes=('_df1', '_df2')) # 调整列名和格式到完全匹配期望样式 df_result = df_result.rename(columns={ 'key': 'keydf2', 'values_df1': 'valuesdf1', 'values_df2': 'valuesdf2' }) # 添加keydf1列,匹配成功的行和keydf2一致,无匹配的设为NaN df_result['keydf1'] = df_result['keydf2'] df_result.loc[df_result['valuesdf1'].isna(), 'keydf1'] = pd.NA # 调整列顺序 df_result = df_result[['keydf1', 'valuesdf1', 'keydf2', 'valuesdf2']].reset_index(drop=True)
执行后得到的结果就是用户期望的样式:
keydf1 valuesdf1 keydf2 valuesdf2 0 1 11.0 1 11 1 2 22.0 2 222 2 NaN NaN 4 44
方法2:先设索引再用join()
如果一定要用join(),需要先把两个DataFrame的key列都设为索引,再执行连接:
# 将两个df的key列设为索引 df1_idx = df1.set_index('key') df2_idx = df2.set_index('key') # 执行右连接并重置索引 df_result = df1_idx.join(df2_idx, lsuffix='df1', rsuffix='df2', how='right').reset_index() # 调整列名和格式 df_result = df_result.rename(columns={'key': 'keydf2'}) df_result['keydf1'] = df_result['keydf2'] df_result.loc[df_result['valuesdf1'].isna(), 'keydf1'] = pd.NA df_result = df_result[['keydf1', 'valuesdf1', 'keydf2', 'valuesdf2']].reset_index(drop=True)
这种方式也能得到和期望一致的结果。
内容的提问来源于stack exchange,提问作者Sachin Gurarikar
相关产品推荐
相关产品推荐

