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() 四种核心填充策略在二维数据上的执行逻辑:
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() 或固定值填充。这种“分层填充”策略能最大限度保留数据原始分布。