You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用循环填充NaN时出错:2023年6月数据被意外外推

问题分析与解决

背景与问题

用户拥有如下DataFrame:

import numpy as np
import pandas as pd
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. )],
          dtype=[('date', 'O'), ('series1', '<f8'), ('series2', '<f8'), ('series3', '<f8'), ('series1_yoy', '<f8'), ('series2_yoy', '<f8'), ('series3_yoy', '<f8')]))

df = df.set_index('date')

其中series1、series2、series3为水平值,后缀为yoy的对应列是12个月同比增长率。用户希望从2023年7月开始,通过取12个月前的数值并应用给定增长率来计算填充这三列的NaN值,使用了如下代码:

columns_to_process = ['series1','series2','series3']
yoy_columns = ['series1_yoy', 'series2_yoy', 'series3_yoy']

df = df.ffill()

for col, yoy_col in zip(columns_to_process, yoy_columns):
  df[col] = df[col].fillna(df[col].shift(12) * (1+df[yoy_col] / 100))

df

但运行后发现2023年6月的数据被意外外推(即后续月份被填充为6月的值),不符合预期。

代码问题根源

  1. 全局ffill()的滥用:这一步会把所有NaN值用前一行的有效值填充,直接把2023年7月及之后的空缺全部用2023年6月的值填满了,导致后续基于同比增长率的填充逻辑完全没有发挥作用——因为此时series1/series2/series3已经没有NaN需要处理了。
  2. 未限定填充范围:需求是仅对2023年7月及之后的NaN进行计算填充,但原代码的全局操作打乱了预期的填充逻辑。

修正后的代码

方式一:直接去掉全局ffill()

由于原数据中仅2023年7月及之后的series1/series2/series3存在NaN,直接执行基于同比的填充逻辑即可,fillna只会处理NaN值,不会修改已有真实数据:

columns_to_process = ['series1','series2','series3']
yoy_columns = ['series1_yoy', 'series2_yoy', 'series3_yoy']

# 去掉全局ffill,直接按同比逻辑填充
for col, yoy_col in zip(columns_to_process, yoy_columns):
    df[col] = df[col].fillna(df[col].shift(12) * (1 + df[yoy_col] / 100))

df

方式二:明确限定填充范围(更严谨)

如果需要确保只对特定日期范围的NaN进行填充,可以添加掩码过滤:

columns_to_process = ['series1','series2','series3']
yoy_columns = ['series1_yoy', 'series2_yoy', 'series3_yoy']

# 定位2023年7月及之后的行
start_date = 'Jul-23'
mask = df.index >= start_date

for col, yoy_col in zip(columns_to_process, yoy_columns):
    # 仅对指定范围内的NaN执行同比填充
    df.loc[mask, col] = df.loc[mask, col].fillna(
        df[col].shift(12)[mask] * (1 + df[yoy_col][mask] / 100)
    )

df

关键说明

  • 原代码的核心错误是提前用ffill()覆盖了需要计算填充的NaN,完全违背了用同比增长率推导数值的需求。
  • 修正后仅针对原本的NaN值,用12个月前的真实值乘以(1+同比增长率)进行填充,不会影响已有数据,也符合预期的时间范围要求。

内容的提问来源于stack exchange,提问作者jack homareau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 11:41:10