4. 缺失值填充基础:fillna() 方法

在数据清洗流程中,fillna() 是 Pandas 提供的核心缺失值填充接口。它支持固定值填充、前向/后向传播填充以及填充数量限制,是处理缺失数据最直接的工具。本章将系统讲解 fillna() 的四种典型用法及其适用场景。

4.1 方法概览与参数说明

fillna() 的完整签名如下,其中 value、method、axis、limit 是最常用的四个参数:

DataFrame.fillna(
    value=None,          # 固定填充值,如 0、'Unknown'、均值
    method=None,         # 填充方法:'ffill' 或 'bfill'
    axis=None,           # 填充轴:0 为行方向,1 为列方向
    limit=None,          # 连续缺失的最大填充个数
    inplace=False        # 是否原地修改
)
参数 类型 说明 典型值
value 标量 / dict / Series / DataFrame 用于替换缺失值的固定值 0, '未知', df.mean()
method 'ffill' / 'bfill' / None 使用前一个或后一个有效值填充 'ffill'
axis 0 / 1 填充方向:0 沿行(上下),1 沿列(左右) 0
limit int 连续缺失值最多填充多少个 2
💡 使用建议: 固定值填充适合分类特征(如用 '缺失' 标记)或业务上有明确默认值的场景;前向/后向填充更适合时间序列数据,能保留趋势连续性。

4.2 固定值填充

固定值填充是最直观的方式:将全部缺失值替换为同一个指定值。适用于缺失量少、特征含义明确或业务规则固定的情况。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    '年龄': [25, np.nan, 30, np.nan, 28],
    '收入': [5000, 6000, np.nan, 6500, np.nan]
})

# 用 0 填充所有缺失值
df_fixed = df.fillna(0)
print(df_fixed)

输出结果:

   年龄    收入
0  25.0  5000.0
1   0.0  6000.0
2  30.0     0.0
3   0.0  6500.0
4  28.0     0.0

更精细的做法是按列指定不同填充值:

# 按列指定填充值
df_fixed_col = df.fillna({'年龄': df['年龄'].mean(), '收入': 0})
print(df_fixed_col)
⚠️ 注意: 使用均值/中位数填充连续型特征时,会降低特征方差,可能影响模型对极端值的敏感度。建议在填充后检查分布变化。

4.3 前向填充(ffill)

前向填充将缺失值替换为它前面的最近一个非缺失值。在时间序列中,这相当于“沿用上一时刻的值”。

# 前向填充:用上一个有效值填充
df_ffill = df.fillna(method='ffill')
print(df_ffill)

输出结果:

   年龄    收入
0  25.0  5000.0
1  25.0  6000.0
2  30.0  6000.0
3  30.0  6500.0
4  28.0  6500.0

可以看到,第 1 行的年龄被第 0 行的 25 填充,第 3 行的年龄被第 2 行的 30 填充,第 4 行的收入被第 3 行的 6500 填充。

4.4 后向填充(bfill)

后向填充与 ffill 方向相反:用缺失值后面的第一个非缺失值来填充。适用于“未来值已知,向前回填”的场景。

# 后向填充:用下一个有效值填充
df_bfill = df.fillna(method='bfill')
print(df_bfill)

输出结果:

   年龄    收入
0  25.0  5000.0
1  30.0  6000.0
2  30.0  6500.0
3  28.0  6500.0
4  28.0   NaN

注意:如果末尾连续缺失,后向填充无法处理(因为没有后续值),结果仍为 NaN。

4.5 限制填充数量(limit)

limit 参数控制连续缺失值最多被填充多少个。超过限制的缺失值保持 NaN,避免过度传播。

# 限制:连续缺失最多填充 1 个
df_limit = df.fillna(method='ffill', limit=1)
print(df_limit)

输出结果:

   年龄    收入
0  25.0  5000.0
1  25.0  6000.0
2  30.0  6000.0
3   NaN  6500.0
4  28.0  6500.0

第 3 行的年龄原本缺失,前一行(第 2 行)是 30,但因为 limit=1,第 1 行已经消耗了这次填充机会(第 1 行年龄被第 0 行填充),所以第 3 行保持 NaN。

📌 关键规则: limit 限制的是连续缺失块中每个块内被填充的个数,而不是全局总填充数。每个连续缺失段独立计数。

4.6 四种填充方式对比

填充方式 适用场景 优点 缺点
固定值填充 分类特征、业务默认值 简单、可解释性强 可能引入偏差
前向填充 (ffill) 时间序列、传感器数据 保留趋势连续性 首行缺失无法填充
后向填充 (bfill) 未来值已知、回填修正 利用后续信息 末行缺失无法填充
限制填充 (limit) 防止过度传播 控制填充范围 需根据业务设定阈值

4.7 填充流程示意图

下图展示了 fillna() 四种核心填充策略在二维数据上的执行逻辑:

fillna() 四种填充策略对比 原始数据 [25, NaN, 30, NaN, 28] [5000, 6000, NaN, 6500, NaN] NaN 表示缺失值 固定值填充 fillna(0) NaN → 0 所有缺失替换为同一值 前向填充 ffill method='ffill' NaN ← 前一个有效值 保留趋势连续性 后向填充 bfill method='bfill' NaN ← 后一个有效值 利用后续信息 限制填充 limit 参数 limit=2:每个连续缺失块最多填充 2 个 NaN 超过限制的缺失值保持 NaN,防止过度传播 核心原则 固定值填充 → 简单直接;ffill/bfill → 保留序列结构;limit → 控制传播范围 实际使用中常组合:先 ffill(limit=2),再对剩余缺失用固定值填充

4.8 实战代码模板

以下是一个综合示例,展示如何在实际项目中组合使用多种填充策略:

import pandas as pd
import numpy as np

# 模拟时间序列数据
df = pd.DataFrame({
    '时间': pd.date_range('2024-01-01', periods=10, freq='D'),
    '温度': [22.5, np.nan, np.nan, 23.0, np.nan, 24.1, np.nan, np.nan, np.nan, 25.0],
    '湿度': [60, 62, np.nan, np.nan, 65, np.nan, 66, np.nan, 68, 70]
})

# 策略:先前向填充,限制连续填充 2 个
df['温度_ffill'] = df['温度'].fillna(method='ffill', limit=2)

# 对剩余缺失值用后向填充
df['温度_final'] = df['温度_ffill'].fillna(method='bfill')

# 湿度列:用均值填充
df['湿度_final'] = df['湿度'].fillna(df['湿度'].mean())

print(df[['时间', '温度', '温度_final', '湿度', '湿度_final']])
📝 最佳实践: 对于时间序列数据,推荐先使用 ffill(limit=n) 处理短期缺失,再对剩余缺失使用 bfill() 或固定值填充。这种“分层填充”策略能最大限度保留数据原始分布。