第4章:缺失值处理——缺失机制、填补方法与筛选阈值
处理缺失值,是QSAR模型构建中绕不开的一环。说实话,我见过太多项目因为这一步没处理好,模型训练出来直接“翻车”。很多人上来就删或者简单填充,根本不关心数据为什么会缺失。嗯,今天我们就来聊聊这个事。
4.1 缺失机制:三类情况
数据缺失不是随机的。它有内在逻辑。我个人习惯先把缺失机制分清楚,再决定怎么处理。理解缺失机制能帮我们避免系统性偏差。
- 完全随机缺失(MCAR):缺失与观测值无关,纯粹是意外。比如仪器偶然故障,或者样品洒了。这种缺失最理想,处理方法也最简单。
- 随机缺失(MAR):缺失依赖于其他观测变量,但不依赖于缺失变量本身。举个例子,分子量大的化合物,可能更不容易溶解,导致某些测量值缺失——但缺失与否跟分子量有关,跟实际测量值无关。
- 非随机缺失(MNAR):缺失与缺失变量本身的值有关。比如,毒性太高的化合物,实验直接中断了,数据缺失;缺失的值恰恰是那些高的值。这种最难处理,需要领域知识做针对性建模。
关键点:MCAR 和 MAR 可以用统计方法填补;MNAR 则需要更复杂的建模,甚至可以考虑单独处理。
我早些年做过一个QSAR项目,用到了几百个分子描述符。结果发现某个关键描述符缺失率高达30%,一开始以为是随机缺失。后来一查原因,缺失的样品全是脂溶性特别高的化合物。
这就是典型的MAR——缺失与脂溶性相关。如果我们按均值填充,模型直接就跪了。
4.2 常用填补方法
方法选不对,模型白费。我列几个常用的,每个都有适用场景和坑。
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 均值/中位数填补 | MCAR,缺失率低(<5%) | 简单、快速 | 降低方差,扭曲分布 |
| KNN 填补 | MAR,数据有结构 | 利用相似样本信息 | 计算量大,对异常值敏感 |
| MICE | MAR/MNAR,高缺失率 | 保留不确定性,多轮预测 | 计算成本高,需迭代 |
| 指示变量法 | 任何情况,辅助特征 | 保留缺失信息 | 增加维度,解释性降低 |
均值与中位数填补
最简单,但最容易被滥用。我建议只在缺失率很低(比如<5%)且确认MCAR时使用。中位数比均值对异常值更鲁棒一些。
个人习惯:我一般先用中位数而不是均值——除非分布是对称的。不然均值容易被“尾巴”带偏。
KNN 填补
KNN 填补的思路很直观:找最相似的那几个样本,用它们的数值来推测缺失值。适合MAR场景,尤其是数据本身有聚类结构时,效果比均值好不少。
from sklearn.impute import KNNImputer
import numpy as np
X = np.array([[1, 2, np.nan],
[3, 4, 5],
[6, np.nan, 8],
[9, 10, 11]])
imputer = KNNImputer(n_neighbors=2)
X_filled = imputer.fit_transform(X)
print(X_filled)
我曾经有个项目,用了KNN填补缺失的描述符,模型性能提升了15%。但要注意:K值不要设太大,我一般3到5就足够了。大数据集里K选大了,反而引入噪声。
MICE(多重插补)
MICE 是目前最稳健的方法之一。它的核心是多次迭代预测:每次把某个缺失变量当成目标,其他变量当特征,轮流建模填补。这样做的好处是保留了多次填补的不确定性。
import pandas as pd
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
df = pd.DataFrame({
'A': [1, 2, np.nan, 4, 5],
'B': [5, np.nan, 7, 8, 9],
'C': [10, 11, 12, np.nan, 14]
})
imputer = IterativeImputer(max_iter=10, random_state=0)
df_filled = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
print(df_filled)
注意:MICE 计算量很大。如果你的数据有几百个描述符,迭代次数设到50,说实话,跑起来很慢。我建议先用简单方法做个初步清理,再用MICE精细处理。
4.3 筛选阈值设置
填补不是万能的。有些变量缺失太严重,还不如直接扔掉。阈值怎么定?我个人经验是这样的:
- 缺失率 > 50%:直接删掉这个变量。填补后的信息量太低,反而增加噪声。
- 缺失率 20%–50%:结合变量重要性判断。如果是关键描述符,尝试MICE填补;如果是次要变量,我建议删除。
- 缺失率 < 20%:可以填补,优先用KNN或MICE。
核心思路:阈值不是死的。你要看数据量、变量重要性、模型类型。树模型对缺失容忍度高一些,线性模型就相对敏感。
嗯,这里再补充一点。阈值筛选不能只看缺失率,还要看缺失模式。我曾经遇到一个案例:某个变量缺失率只有10%,但缺失样本全集中在最高活性的那群化合物上。这种缺失是有偏的,直接填充会严重扭曲关系。后来我单独给这个变量建了个预测模型,才把问题解决。
所以,我的建议是:
- 先做缺失模式分析——可视化缺失矩阵、热图、相关性。
- 再设定初步阈值,删掉高缺失变量。
- 对保留变量,根据缺失机制选择填补方法。
- 评估填补效果——交叉验证模型性能,看是否稳定。
4.4 本章知识结构图
下面这张SVG图总结了缺失值处理的完整流程,从识别机制到最终评估,你可以把它当成自己项目的检查清单。
4.5 你会遇到的实际问题
避坑指南
我曾经犯过一个错:在填补缺失后,直接拿去训练模型,结果模型在验证集上表现极好,但实际预测一塌糊涂。后来发现,填补过程引入了数据泄漏——填补时用了全部样本的信息,包括测试集。所以记住:填补一定要在划分训练/测试集之后做,或者用交叉验证的方式填补。
另一个常见陷阱是:对分类变量也用均值填补。比如分子指纹这种0/1变量,均值填补出来是0.5,毫无意义。这个时候要用众数填补,或者用MICE里的逻辑回归模型。
小技巧:用KNN填补之前,先把数据做标准化。否则距离计算会被量纲大的描述符支配,结果会偏。
好了,缺失值处理这件事,说白了就是“先看缺失的成因,再选合适的方法”。不要一上来就删,也别一上来就填。多看数据,多问几个为什么——数据比你想象的更有道理。