如何用Pandas函数替代for循环实现按日期匹配填充DataFrame
问题与解决方案
我希望将多个透视表的结果填充到一个DataFrame中,要求透视表中的日期与另一个DataFrame的日期匹配。我不想直接替换所有NaN值,因为这些NaN在后续会发挥作用。下方代码已实现需求,但了解到Pandas中不推荐使用for循环,请问如何仅使用Pandas函数替代循环来实现相同效果?
原实现代码
import pandas as pd cols = ['date', 'Heavy', 'Light'] daysdf = pd.DataFrame(columns = cols) # 设置日期范围 start = '2015-06-06' end = '2015-07-06' # daysdf初始包含该范围内的所有日期,Heavy和Light列均为NaN daysdf['date']= pd.date_range(start=start, end=end) # 模拟透视表数据 pf = pd.DataFrame({'date': ['2015-06-06', '2015-06-16', '2015-06-26', '2015-07-05'], 'Heavy': [450, 567, 612, 701], 'Light': [450, 567, 612, 701]}) # 获取pf日期在daysdf中对应的行索引 indexes = [] for p in pf['date']: my_index = daysdf.index[daysdf['date']==p] indexes.append(my_index) # 执行填充操作 count = 0 for i in indexes: daysdf.iloc[i, 1] = pf.iloc[count, 1] daysdf.iloc[i, 2] = pf.iloc[count, 2] count +=1
无循环替代方案
Pandas的矢量化操作可以完全替代循环,这里提供两种高效实现方式,均保留原DataFrame中的NaN值,仅填充匹配日期的数值:
方法1:set_index + update(最简洁)
update方法会自动根据索引对齐,用pf中的非NaN值替换daysdf对应位置的NaN,完美契合需求:
import pandas as pd cols = ['date', 'Heavy', 'Light'] start = '2015-06-06' end = '2015-07-06' # 初始化日期DataFrame daysdf = pd.DataFrame({'date': pd.date_range(start=start, end=end)}, columns=cols) # 模拟透视表数据,统一日期类型为datetime(避免字符串与日期类型不匹配) pf = pd.DataFrame({'date': ['2015-06-06', '2015-06-16', '2015-06-26', '2015-07-05'], 'Heavy': [450, 567, 612, 701], 'Light': [450, 567, 612, 701]}) pf['date'] = pd.to_datetime(pf['date']) # 将date设为索引,利用索引对齐完成填充 daysdf = daysdf.set_index('date') pf = pf.set_index('date') daysdf.update(pf) # 仅替换匹配日期的NaN值,其余保留 # 恢复date为普通列 daysdf = daysdf.reset_index()
方法2:merge + combine_first(步骤更清晰)
通过左连接合并两个DataFrame,再用combine_first填充匹配值,适合需要查看中间过程的场景:
import pandas as pd cols = ['date', 'Heavy', 'Light'] start = '2015-06-06' end = '2015-07-06' daysdf = pd.DataFrame({'date': pd.date_range(start=start, end=end)}, columns=cols) pf = pd.DataFrame({'date': ['2015-06-06', '2015-06-16', '2015-06-26', '2015-07-05'], 'Heavy': [450, 567, 612, 701], 'Light': [450, 567, 612, 701]}) # 统一日期类型,确保匹配准确 daysdf['date'] = pd.to_datetime(daysdf['date']) pf['date'] = pd.to_datetime(pf['date']) # 左连接合并,保留daysdf的所有日期 merged_df = daysdf.merge(pf, on='date', how='left', suffixes=('_orig', '_pf')) # 用pf的值填充原列的NaN,原NaN以外的内容(此处初始全为NaN)保留 merged_df['Heavy'] = merged_df['Heavy_pf'].combine_first(merged_df['Heavy_orig']) merged_df['Light'] = merged_df['Light_pf'].combine_first(merged_df['Light_orig']) # 提取最终需要的列 daysdf = merged_df[cols]
关键说明
- 两种方法均为Pandas原生矢量化操作,执行效率远高于循环,且代码更简洁易维护。
- 操作后仅填充了与
pf日期匹配的数值,daysdf中其他日期的NaN值完全保留,不影响后续使用。
内容的提问来源于stack exchange,提问作者Pedroski
相关产品推荐
相关产品推荐

