缺失值填充与插值
本教程共 54 篇 · 第 25 篇 · 更新于 2026-08-11 · 约 5 分钟阅读
本节目标:学会 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()) 这种”填统计值”的写法很自然。均值、中位数、众数怎么选:
- 数据接近对称、没有极端值 → 均值
- 有极端值(比如收入)→ 中位数更稳
- 文本类 → 众数
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))
Tip3.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")) # 开头结尾也补
Noteinterpolate 默认不补开头(第一个有效值之前)的连续缺失;结尾的缺失会补上。想连开头一起补,配
limit_direction="both"(或"backward")。
填充要适度
填充不是越多越好,两点提醒:
- 填充会改变数据分布:大量用均值填充,方差会被压缩,统计结果偏”整齐”,可能掩盖真实波动
- 连续缺失一大片时,任何填充方法都是在”编故事”,不如删掉这段或标注出来
怎么选:一张决策表
| 场景 | 推荐 |
|---|---|
| 数值列,缺失少,想省事 | 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 比较会传播,别拿它当布尔值用。