时间序列缺失值填补需按真实时间逻辑插值,优先用method='time';限制ffill/bfill跨度防污染;周期性数据宜用seasonal_decompose分项填补;须先判别缺失机制再决策。

时间序列数据里出现 NaN 或空值很常见,但直接用 pandas.DataFrame.fillna() 填平均值或前向填充,往往破坏时序结构——比如跨天、跨季节、跨业务周期的缺失,填错一个值就可能让后续建模全偏。关键不是“能不能填”,而是“按什么逻辑填才不扭曲趋势和周期性”。
用 pd.Series.interpolate() 按时间索引做线性/时间加权插值
默认的 .interpolate() 只按行索引位置插值,对时间序列完全不适用——它把 2023-01-01 和 2023-01-10 当作相邻整数 0 和 9 来算距离。必须显式传入 method='time' 或 method='index'(后者要求索引是 datetime 类型)。
-
df['value'].interpolate(method='time'):按真实时间差加权,适合等频或不等频但有明确时间戳的数据 -
df.set_index('timestamp')['value'].interpolate(method='time'):确保索引是datetime64,否则报TypeError: 'method' parameter has to be 'time' for DatetimeIndex - 不建议用
method='linear',它只看索引数值,对DatetimeIndex就是纳秒级整数,插值结果严重失真
用 pd.Series.ffill() 和 bfill() 控制填补方向与跨度
前向填充(ffill)看着简单,但实际中容易把异常断点后的正常值也“污染”了。比如传感器故障停采 3 小时,后面恢复,这时不该用前面的旧值覆盖新数据。
- 加
limit参数限制连续填补个数:df['value'].ffill(limit=5),最多补 5 行,防止单次长缺失污染全局 - 组合使用更安全:
df['value'].ffill(limit=3).bfill(limit=3),优先用邻近前后值,避免单边依赖 - 注意
ffill对TimedeltaIndex也有效,但若缺失跨越多个业务周期(如周末+周一),需先用asfreq('D')对齐频率再操作
用 statsmodels.tsa.seasonal.seasonal_decompose() 做模型驱动填补
当缺失集中在周期性明显的序列中(如每小时用电量、每日访问量),简单插值会抹平季节性和趋势。此时应先分解,再分别填补残差、趋势、季节项。
立即学习“Python免费学习笔记(深入)”;
- 必须指定
period(如日频数据填period=7,小时数据填period=24),否则分解失败报ValueError: You must specify a period or x must be a pandas object with a DatetimeIndex - 分解后对
resid项用.interpolate(method='time'),对trend和seasonal用.ffill().bfill()更稳健 - 重建序列时别漏掉
model='additive'或'multiplicative'的选择——乘法模型更适合方差随均值增大的场景(如流量高峰波动更大)
真正难的不是选哪个函数,而是判断缺失机制:是随机丢失(MCAR)、依赖于观测值(MAR),还是依赖于自身历史(MNAR)?比如数据库写入失败导致某整点数据全空,属于 MNAR,这时任何插值都只是妥协。得先查日志、比对上游源,再决定是插值、标记为异常,还是弃用该段。


















