Pandas如何用groupby结合滚动均值填充DataFrame的NaN值
问题根源
你之前的代码有两个核心错误:
- 分组逻辑完全错了:把
date和ID同时作为分组键的话,每个分组只有1条唯一记录,根本没法做窗口大小为2的滚动计算,也不符合“每个ID独立按时间顺序算滚动值”的需求 - 索引没对齐:不管是全局滚动还是错误分组后的滚动,算出来的结果索引和原DataFrame不匹配,才会报
cannot join with no overlapping index names的错,而且全局滚动会把不同ID的数值混在一起算,结果完全不对。
正确实现方案
先记住一个前提:时序滚动计算必须先保证数据顺序正确,所以第一步永远是先按ID、date升序排序,不然乱序下算出来的滚动值没有任何意义。
之后只需要按ID分组(注意不要加date),对Value列做窗口为2的滚动均值计算,再向前偏移1位——这一步是为了保证填充值只用NaN位置之前的历史数据,避免未来数据泄露,最后用算好的值填充NaN即可,这个写法天然支持多ID、任意日期长度的数据集,pandas内部是向量化计算,性能很高,不会出现耗时极长的问题。
完整可运行代码:
import pandas as pd import numpy as np # 测试数据(额外加入ID=2的样例验证多ID适配性) d = { 'date': ['1999-01-01', '1999-01-02', '1999-01-03', '1999-01-04', '1999-01-05', '1999-01-06', '1999-01-01', '1999-01-02', '1999-01-03', '1999-01-04'], 'ID': [1,1,1,1,1,1,2,2,2,2], 'Value':[1,2,3,np.NaN,5,6,10,np.NaN,30,40] } df = pd.DataFrame(data=d) # 1. 排序保证时序顺序正确 df = df.sort_values(by=['ID', 'date']).reset_index(drop=True) # 2. 按ID分组计算偏移1位的2窗口滚动均值,处理索引对齐 fill_values = df.groupby('ID')['Value']\ .rolling(window=2, min_periods=1)\ .mean()\ .shift(1)\ .droplevel(0) # 去掉分组生成的ID层级索引,和原表行索引一一对应 # 3. 填充缺失值 df['Value'] = df['Value'].fillna(fill_values)
运行后填充结果符合预期:
- ID=1在1999-01-04的NaN,前两个有效值是2、3,滚动均值为2.5,填充值为2.5
- ID=2在1999-01-02的NaN,前序只有10(
min_periods=1允许窗口不足2条),滚动均值为10,填充值为10
注意点
- 不要把
date放到groupby的键里,除非你明确要跨ID按日期分组计算,这和你要按每个ID独立算时序滚动值的需求完全冲突 - 如果你的业务场景允许用当前行的有效值参与计算(绝大多数时序预测/填充场景不建议这么做,会引入未来数据泄露),可以删掉代码里的
.shift(1),滚动计算会自动跳过窗口内的NaN值取有效数计算。
内容的提问来源于stack exchange,提问作者ThatQuantDude
相关产品推荐
相关产品推荐

