月度数据NaN值填充公式失效:如何补全全周期缺失值?
问题:填充Pandas DataFrame全周期缺失值
问题背景
现有如下Pandas DataFrame,其中series1、series2、series3为数值水平列,后缀_yoy的列对应12个月同比增长率:
import pandas as pd import numpy as np from numpy import rec,nan df=pd.DataFrame.from_records(rec.array([ ('Jan-22', 200., 100., 325., nan, nan, nan), ('Feb-22', 210., 101., 328., nan, nan, nan), ('Mar-22', 212., 102., 329., nan, nan, nan), ('Apr-22', 211., 104., 330., nan, nan, nan), ('May-22', 208., 105., 329., nan, nan, nan), ('Jun-22', 215., 107., 330., nan, nan, nan), ('Jul-22', 219., 108., 321., nan, nan, nan), ('Aug-22', 220., 105., 332., nan, nan, nan), ('Sep-22', 222., 111., 335., nan, nan, nan), ('Oct-22', 223., 115., 338., nan, nan, nan), ('Nov-22', 225., 118., 339., nan, nan, nan), ('Dec-22', 230., 122., 341., nan, nan, nan), ('Jan-23', 235., 124., 342., 17.5, 24. , 5.2), ('Feb-23', 236., 126., 348., 12.4, 24.8, 6.1), ('Mar-23', 267., 128., 351., 25.9, 25.5, 6.7), ('Apr-23', 288., 130., 326., 36.5, 25. , -1.2), ('May-23', 299., 132., 359., 43.8, 25.7, 9.1), ('Jun-23', 300., 133., 363., 39.5, 24.3, 10. ), ('Jul-23', nan, nan, nan, 12. , 11. , 5. ), ('Aug-23', nan, nan, nan, 11. , 12. , 4. ), ('Sep-23', nan, nan, nan, 10. , 11. , 3. ), ('Oct-23', nan, nan, nan, 9. , 13. , 2. ), ('Nov-23', nan, nan, nan, 8. , 14. , 1. ), ('Dec-23', nan, nan, nan, 7. , 7. , 4. ), ('Jan-24', nan, nan, nan, 6. , 8. , 3. ), ('Feb-24', nan, nan, nan, 6. , 7. , 1. ), ('Mar-24', nan, nan, nan, 5. , 8. , 0. ), ('Apr-24', nan, nan, nan, 4. , 9. , -5. ), ('May-24', nan, nan, nan, 3. , 10. , -5. ), ('Jun-24', nan, nan, nan, 4. , 5. , -4. ), ('Jul-24', nan, nan, nan, 5. , 4. , -2. ), ('Aug-24', nan, nan, nan, 6. , 2. , -1. ), ('Sep-24', nan, nan, nan, 7. , 2. , -4. ), ('Oct-24', nan, nan, nan, 7. , 2. , -4. ), ('Nov-24', nan, nan, nan, 7. , 2. , -4. ), ('Dec-24', nan, nan, nan, 7. , 2. , -4. )], dtype=[('date', 'O'), ('series1', '<f8'), ('series2', '<f8'), ('series3', '<f8'), ('series1_yoy', '<f8'), ('series2_yoy', '<f8'), ('series3_yoy', '<f8')])) df = df.set_index('date')
需求是从2023年7月开始,用12个月前的数值水平值 × (1 + 同比增长率/100) 填充series1、series2、series3的NaN值。原代码如下:
level_cols = ['series1','series2','series3'] yoy_cols = ['series1_yoy', 'series2_yoy', 'series3_yoy'] for i, x in enumerate(level_cols): df[x].fillna(df[x].shift(12) * (1 + df[yoy_cols[i]]/100), inplace=True) df
但该代码仅填充了2024年6月及之前的缺失值,2024年7月至12月的NaN未被填充。
原因分析
原代码仅执行一次填充操作:
- 2023年7-12月的缺失值可以通过12个月前(2022年7-12月)的有效值计算得到,因此被填充;
- 2024年1-6月的缺失值可以通过12个月前(2023年1-6月)的有效值计算得到,因此被填充;
- 2024年7-12月的缺失值需要12个月前(2023年7-12月)的数值,但原代码计算
shift(12)时用的是填充前的原始数据(2023年7-12月当时还是NaN),因此无法完成填充。
需要多次迭代填充,每次迭代都会用上一次填充后的结果来计算后续行的缺失值。
解决方案:使用计数器迭代填充
可以通过循环计数器控制迭代次数,直到所有缺失值被填充,或者根据需要填充的周期数设置固定迭代次数。
方法1:循环直到无缺失值
level_cols = ['series1','series2','series3'] yoy_cols = ['series1_yoy', 'series2_yoy', 'series3_yoy'] # 计数器初始化 iteration_count = 0 max_iterations = 10 # 设置最大迭代次数防止无限循环 while df[level_cols].isna().any().any() and iteration_count < max_iterations: for i, x in enumerate(level_cols): # 计算填充值:用上一次迭代后的数据做shift fill_values = df[x].shift(12) * (1 + df[yoy_cols[i]]/100) df[x].fillna(fill_values, inplace=True) iteration_count += 1 print(f"完成填充,共迭代{iteration_count}次") df
方法2:固定迭代次数(针对已知周期)
已知需要填充的周期是2023年7月到2024年12月,共18个月,只需迭代2次即可覆盖所有缺失值:
level_cols = ['series1','series2','series3'] yoy_cols = ['series1_yoy', 'series2_yoy', 'series3_yoy'] # 设置固定迭代次数 for _ in range(2): for i, x in enumerate(level_cols): fill_values = df[x].shift(12) * (1 + df[yoy_cols[i]]/100) df[x].fillna(fill_values, inplace=True) df
效果说明
- 第一次迭代:填充2023年7-12月的缺失值;
- 第二次迭代:2024年7-12月的
shift(12)会指向第一次迭代后填充完成的2023年7-12月数据,因此可以顺利计算并填充缺失值。
内容的提问来源于stack exchange,提问作者jack homareau
相关产品推荐
相关产品推荐

