首页 / Pandas 入门教程 / 缺失值填充与插值

Pandas 入门教程

缺失值填充与插值

本教程共 54 篇 · 第 25 篇 · 更新于 2026-08-11 · 约 5 分钟阅读

PandasPandas 入门教程fillnaffillinterpolate插值

本节目标:学会 fillna、ffill/bfill、interpolate 三种填充思路,并理解 Nullable 类型下缺失值的特殊行为。

填充前先想清楚

删除缺失(§24)是”减法”,填充是”加法”:用合理的值把空洞补上,保住数据量。但补什么值不是拍脑袋,得看业务:

  • 固定值:比如缺失的转化率按 0 计
  • 统计值:均值、中位数、众数
  • 邻居值:时间序列里用前后一天的值
  • 插值:按已有趋势推算

fillna:按值填充

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "A": [1.0, np.nan, np.nan, 4.0],
    "B": [10.0, np.nan, 30.0, np.nan],
})
print(df.fillna(0))                  # 全部填 0
print(df.fillna({"A": 0, "B": 99}))  # 不同列填不同值
print(df.fillna(df.mean()))          # 每列填自己的均值

fillna 按索引和列名对齐,所以 df.fillna(df.mean()) 这种”填统计值”的写法很自然。均值、中位数、众数怎么选:

  1. 数据接近对称、没有极端值 → 均值
  2. 有极端值(比如收入)→ 中位数更稳
  3. 文本类 → 众数

fillna 还能用另一个 Series 对齐填充——比如用”上个月的销售额”补”这个月”的缺失:

s = pd.Series([1.0, np.nan, 3.0], index=[0, 1, 2])
s_fill = pd.Series([10.0, 20.0, 30.0], index=[0, 1, 2])
print(s.fillna(s_fill))
Tip

3.0 默认 Copy-on-Write,fillna 返回新对象,直接赋值即可:df = df.fillna(0)。老代码里的 inplace=True 没必要再用了。

ffill / bfill:向前向后填充

时间序列里,某天没数据,用前一天的值顶上很常见——这就是前向填充:

ts = pd.Series(
    [8.0, np.nan, np.nan, 2.0, np.nan],
    index=pd.date_range("2024-01-01", periods=5, freq="D"),
)
print(ts.ffill())            # 缺失值用上一个值填
print(ts.bfill())            # 缺失值用下一个值填
print(ts.ffill(limit=1))     # 最多连续填 1 个
Warning

老教程里的 fillna(method="ffill") 写法在 pandas 3.0 已经移除,直接调用 df.ffill() / df.bfill()。注意区分:fillna 不再接受 method 参数,但 interpolate 的 method 参数照常可用(见下节)。

ffill 填不了”开头就是缺失”的位置(前面没有值),bfill 则填不了结尾的空洞。填完用 ts.isna().sum() 复查一遍。

每个分组内部的序列要各自前向填充(比如每个产品单独按天补数),可以先 groupby 再 ffill,§37 会讲到这个组合。

interpolate:按趋势插值

interpolate 用已有数据”画线”推算缺失值。默认是线性插值:把缺失段两端连一条直线,按位置取中间点:

s2 = pd.Series([1.0, np.nan, np.nan, 4.0, np.nan, 6.0])
print(s2.interpolate())

结果里两个连续缺失补成 2.0、3.0(在 1 和 4 之间等分),后面的一个补成 5.0。

其他常用方法:

  • method=“time”:时间索引下按时间间隔加权,间隔不均匀时比默认更准
  • method=“nearest”:取最近的非缺失值
  • method=“polynomial” / “spline”:多项式 / 样条拟合,需要配 order 参数(依赖 scipy)
print(ts.interpolate(method="time"))

DataFrame 上调用时按列逐列插值,limit、limit_direction、limit_area 控制”补几个、往哪个方向补、只补中间还是边缘”:

s3 = pd.Series([np.nan, np.nan, 5.0, np.nan, np.nan, 13.0])
print(s3.interpolate(limit=1))                 # 每个缺口最多补 1 个
print(s3.interpolate(limit_direction="both"))  # 开头结尾也补
Note

interpolate 默认不补开头(第一个有效值之前)的连续缺失;结尾的缺失会补上。想连开头一起补,配 limit_direction="both"(或 "backward")。

填充要适度

填充不是越多越好,两点提醒:

  1. 填充会改变数据分布:大量用均值填充,方差会被压缩,统计结果偏”整齐”,可能掩盖真实波动
  2. 连续缺失一大片时,任何填充方法都是在”编故事”,不如删掉这段或标注出来

怎么选:一张决策表

场景推荐
数值列,缺失少,想省事fillna(均值/中位数)
分类文本列fillna(众数) 或固定值
时间序列,趋势平稳ffill / bfill
时间序列,有趋势interpolate(method=“time”) 或默认线性
传感器类连续数据interpolate

原则:填充方式要让数据尽量”像真实情况”,而不是让数字好看。填完统一复查 isna().sum(),确认没有漏网的。

Nullable 类型下的缺失语义

Int64、boolean、string 这些 Nullable 类型用 pd.NA 表示缺失(§07 讲过)。它的行为比 NaN “较真”:

s4 = pd.Series([1, 2, None], dtype="Int64")
print(s4)
print(s4.fillna(0))    # 缺失照常填充
print(s4 == 1)         # 缺失位置返回 <NA>,不是 False

NA 参与比较会”传播”:不知道就是不知道,结果还是 NA。逻辑运算遵循三值逻辑,只有结果确定时才给出 True/False:

print(True | pd.NA)    # True,结果已确定
print(False | pd.NA)   # <NA>,取决于 NA 是什么

还有个容易踩的坑:NA 不能当布尔值用,if pd.NA: 会抛 TypeError。写条件判断前先 fillna,或改用 isna 判断。

另外提醒一句:空字符串 "" 不是缺失值,isna 对它返回 False。如果业务上要把空字符串当缺失,先 df.replace("", np.nan) 再处理。(read_csv 解析阶段空串默认会被读成 NaN,那是解析行为,见 §11。)

小结

填充三件套:fillna 填固定值或统计值、ffill/bfill 借邻居值、interpolate 按趋势插值。填充前想清楚业务含义,填完用 isna().sum() 复查。Nullable 类型的 NA 比较会传播,别拿它当布尔值用。