4. 缺失值处理(下):插值法、向前/向后填充、基于模型预测填充
好,咱们接着聊缺失值处理。上一节我们讲了删除法和简单填充,说白了就是「粗暴但有效」的手段。但现实中的数据往往没那么听话——你想想看,一条时间序列里突然缺了几个点,直接删掉会破坏连续性,用均值填充又太粗糙。这时候,就该请出我们今天的主角了。
4.1 插值法:让数据自己「长」出来
插值法的核心思想很简单:用已知点的趋势,去推测未知点的值。我个人的习惯是,只要数据量够大、缺失比例不高(比如5%以内),优先试试插值。
Pandas 里的 interpolate() 方法,默认是线性插值。什么意思呢?就是拿缺失值前后两个点的连线,取中间位置的值。举个例子:
import pandas as pd
import numpy as np
# 模拟一组有缺失的时间序列
data = pd.Series([1, np.nan, np.nan, 4, 5, np.nan, 7])
print(data.interpolate())
输出结果:
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 6.0
6 7.0
dtype: float64
你看,缺失的1、2位置被填成了2.0和3.0,正好是1到4的等分点。线性插值在数据变化平缓时效果不错,但遇到剧烈波动就有点力不从心了。
我的经验: 在材料科学数据中,比如温度-电阻曲线,如果缺失点不多,线性插值基本够用。但如果是相变点附近,数据变化很陡,我会改用样条插值(method='spline'),效果会好很多。
4.2 向前/向后填充:时间序列的「惯性」思维
向前填充(ffill)和向后填充(bfill),说白了就是「用上一个值填下一个空」或者「用下一个值填上一个空」。这在时间序列数据里特别常见——比如传感器每隔1秒采集一次数据,中间断了几秒,用前一个值填充往往比插值更合理。
# 向前填充
data_ffill = data.ffill()
print(data_ffill)
# 向后填充
data_bfill = data.bfill()
print(data_bfill)
输出:
# ffill
0 1.0
1 1.0
2 1.0
3 4.0
4 5.0
5 5.0
6 7.0
# bfill
0 1.0
1 4.0
2 4.0
3 4.0
4 5.0
5 7.0
6 7.0
我曾经踩过的坑: 有一次处理电池循环寿命数据,连续缺失了10个点,我用ffill填充后,模型训练出来效果奇差。后来才发现,电池容量是逐渐衰减的,用前一个值填充会严重高估后续容量。所以,ffill/bfill只适合短时间缺失(比如连续缺失不超过3个点),长缺失一定要慎重。
4.3 基于模型预测填充:高级玩家的选择
如果缺失值比较多,或者数据关系比较复杂,前面的方法就不太够用了。这时候我会用模型来预测缺失值。说白了,就是把缺失值当作「待预测的标签」,用其他特征去拟合。
常用的模型有:
- KNN填充:找最相似的k个样本,取平均值
- 随机森林填充:用随机森林回归预测缺失值
- 迭代填充(IterativeImputer):把每个有缺失的特征轮流当作目标,反复迭代
来看一个用KNN填充的例子:
from sklearn.impute import KNNImputer
import numpy as np
# 模拟数据:3个特征,第2行有缺失
X = np.array([[1, 2, 3],
[4, np.nan, 6],
[7, 8, 9]])
imputer = KNNImputer(n_neighbors=2)
X_filled = imputer.fit_transform(X)
print(X_filled)
输出:
[[1. 2. 3.]
[4. 5. 6.]
[7. 8. 9.]]
你看,缺失的5.0被算出来了——它取了第0行和第2行的平均值((2+8)/2=5)。
核心要点: 模型填充虽然精度高,但计算量大。我一般只在缺失率超过20%时才用。而且要注意,如果特征之间本身相关性很弱,模型填充的效果还不如简单插值。
4.4 知识体系总览
下面这张图,是我自己梳理的缺失值处理方法选择逻辑,你可以照着这个思路来:
4.5 实战中的选择策略
说了这么多,到底该用哪个?我总结了一个简单的决策表:
| 缺失比例 | 数据类型 | 推荐方法 | 注意事项 |
|---|---|---|---|
| < 5% | 数值型 | 线性插值 | 数据变化平缓时效果最好 |
| 5% - 20% | 时间序列 | ffill / bfill | 连续缺失不超过3个点 |
| 20% - 40% | 多特征 | KNN填充 | 特征之间要有相关性 |
| > 40% | 复杂关系 | 迭代填充 / 随机森林 | 计算量大,注意过拟合 |
一个小技巧: 不管用哪种方法填充,填充完后一定要做两件事:一是对比填充前后的数据分布(均值、方差有没有突变),二是用可视化看看填充点是否「自然」。我见过太多人填完数据就直接建模,结果模型效果反而变差了。
嗯,关于缺失值处理,咱们就聊到这里。记住一句话:没有最好的方法,只有最合适的方法。多试试不同的策略,结合你的数据特点来选,这才是数据清洗的正确打开方式。