第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%,但缺失样本全集中在最高活性的那群化合物上。这种缺失是有偏的,直接填充会严重扭曲关系。后来我单独给这个变量建了个预测模型,才把问题解决。

所以,我的建议是:

  1. 先做缺失模式分析——可视化缺失矩阵、热图、相关性。
  2. 再设定初步阈值,删掉高缺失变量。
  3. 对保留变量,根据缺失机制选择填补方法。
  4. 评估填补效果——交叉验证模型性能,看是否稳定。

4.4 本章知识结构图

下面这张SVG图总结了缺失值处理的完整流程,从识别机制到最终评估,你可以把它当成自己项目的检查清单。

缺失值处理流程 原始数据 缺失机制识别 MCAR MAR MNAR 缺失率阈值筛选 删除变量 选择填补方法 最终数据集 评估与验证 交叉验证+扰动测试

4.5 你会遇到的实际问题

避坑指南

我曾经犯过一个错:在填补缺失后,直接拿去训练模型,结果模型在验证集上表现极好,但实际预测一塌糊涂。后来发现,填补过程引入了数据泄漏——填补时用了全部样本的信息,包括测试集。所以记住:填补一定要在划分训练/测试集之后做,或者用交叉验证的方式填补。

另一个常见陷阱是:对分类变量也用均值填补。比如分子指纹这种0/1变量,均值填补出来是0.5,毫无意义。这个时候要用众数填补,或者用MICE里的逻辑回归模型。

小技巧:用KNN填补之前,先把数据做标准化。否则距离计算会被量纲大的描述符支配,结果会偏。

好了,缺失值处理这件事,说白了就是“先看缺失的成因,再选合适的方法”。不要一上来就删,也别一上来就填。多看数据,多问几个为什么——数据比你想象的更有道理。