能否用Pandas的apply方法替代combine_first实现多列数据更新?
使用apply替代combine_first完成Pandas多列数据更新
首先明确你的需求:通过df1的id匹配df2、df3的no字段,更新df1里的date_i、rl、date_f列,同时保留原df1的其他字段(比如cho、gp),最终只保留cho不为空的行。
当然可以用apply来实现这个需求!相比combine_first的索引对齐逻辑,apply的逐行处理思路更直观,也更容易自定义更新规则。下面是具体的实现步骤:
步骤1:提前构建映射字典(提升效率)
为了避免在apply逐行处理时反复查询df2和df3(拖慢效率),我们先把需要匹配的字段转换成字典映射,这样查找值的速度会快很多:
import pandas as pd import numpy as np # 定义原始三个DataFrame df1 = pd.DataFrame({'id' : ['1470', '1550', '1562', '1578', '1645', '1867', '1888', '2205', '2283', '2306'], 'gp' : ['nl_i', 'adv_i', 'adv_i', 'nl_i', 'adv_i', 'early_i', 'nl_i', 'nl_i', 'nl_i', 'nl_i'], 'cho' : [69626.0, 183425.0, 75418.0, 84239.0, 158721.0, 122857.0, 166052.0, 86686.0, 140407.0, 122792.0], 'date_i' : ['2000-11-29', '2000-11-28', '2000-11-27', '2000-11-26', '2000-11-25', '2000-11-24', '2000-11-23', '2000-11-22', '2000-11-21', '2000-11-20'], }) df1['rl'] = np.NAN df1['date_f'] = np.NAN df2 = pd.DataFrame({'no' : ['2939', '2283', '1578', '2781', '2319', '2306', '1888', '1470', '2869', '2205'], 'date_i' : ['2010-09-18', '2012-02-08', '2012-04-09', '2012-04-23', '2012-05-08', '2012-09-04', '2013-08-29', '2013-09-09', '2014-02-24', '2015-11-19'], 'rl' : ['r', 'l', 'r', 'r', 'l', 'r', 'r', 'r', 'r', 'r']}) df3 = pd.DataFrame ({'no' : ['2319', '2306', '1888', '1470', '2869', '2205'], 'date_f' : ['2019-05-10', '2013-09-24', '2019-06-12', '2016-08-29', '2016-10-10', '2017-11-30']}) # 构建映射字典:key是no/id,value是对应要更新的字段值 rl_map = df2.set_index('no')['rl'].to_dict() df2_date_i_map = df2.set_index('no')['date_i'].to_dict() df3_date_f_map = df3.set_index('no')['date_f'].to_dict()
步骤2:定义逐行更新的函数
写一个函数,接收df1的每一行数据,根据id去上面的字典里查找对应的值,更新目标列(如果找不到匹配的id,就保留原行的原值):
def update_row(row): # 更新rl列:如果id在rl_map里,用映射值,否则保留原row['rl'] row['rl'] = rl_map.get(row['id'], row['rl']) # 更新date_i列:优先用df2的date_i,否则保留原df1的date_i row['date_i'] = df2_date_i_map.get(row['id'], row['date_i']) # 更新date_f列:如果id在df3的映射里,用对应值,否则保留原NaN row['date_f'] = df3_date_f_map.get(row['id'], row['date_f']) return row
步骤3:用apply执行更新并整理结果
调用df1.apply()逐行处理,最后做和原来combine_first一样的列顺序保留、空值过滤操作:
# 执行更新 df1_new = df1.apply(update_row, axis=1) # 保持原df1的列顺序(和你原来的逻辑一致) df1_new = df1_new[df1.columns] # 过滤掉cho为空的行 df1_new.dropna(subset=['cho'], inplace=True)
效果对比
这个方法最终得到的df1_new和你用combine_first得到的结果完全一致。两者的区别在于:
combine_first是向量化操作,在大数据量下效率更高;apply是逐行处理,逻辑更直观,如果你之后需要修改更新规则(比如添加条件判断),会更容易调整。
内容的提问来源于stack exchange,提问作者Charlie Yoon
相关产品推荐
相关产品推荐

