做医疗数据预测的时候,我遇到最多、也最容易被忽视的一个问题,不是算法选得不够高级,而是数据里的缺失值根本没处理好。有一回我接手一份用于疾病风险预测的临床数据,样本量不算小,但一查缺失率,血糖、胰岛素、血压这几个关键字段的缺失比例都在三成上下。当时组里的第一反应是做均值填充,理由是简单、不引入额外方差。结果模型跑出来,交叉验证的AUC勉强到0.7,特征重要性排第一的居然是缺失比例最高的那个指标——不是因为它真的强,而是因为填充方式把分布打乱了,模型学会了"这个字段等于均值"这种假规律。
后来把填充方案换成Scikit-learn的KNNImputer,同样的模型配置、同样的评估流程,AUC直接往上走了一截,而且更关键的是,多次重复实验的结果稳定了很多。这篇文章就把我在这类项目里的完整思路、代码实现和踩过的坑整理出来,给正在处理医疗数据、做预测建模的朋友一个可直接参考的路线。
1. 医疗数据里的缺失值,为什么不能小看
1.1 临床数据缺失的三个来源和两种类型
医疗数据缺失往往不是随机的,这是我做这类项目最深的体会。主要来源大致能分成三类:
- 检测设备或流程限制导致的数据未产生,比如某些昂贵的检查不是所有患者都做,这就会让某个特征成片缺失。
- 记录环节的疏漏,像电子病历录入不完整,某些字段在部分患者上就是空的。
- 患者自身状态的客观限制,比如急诊入院的患者来不及做全部检查,后续也未必补全。
从统计学角度看,缺失机制可以分成MCAR(完全随机缺失)、MAR(随机缺失,缺失概率与其他观测变量相关)和MNAR(非随机缺失,缺失概率与缺失值本身相关)。临床数据里真正MCAR的情况很少,更多是MAR甚至MNAR。举个例子,老年患者做某项侵入性检查的比例偏低,这个缺失就和年龄相关,属于MAR;如果某个检验结果本身就是异常低,低到设备测不出来就没有记录,那这就是MNAR。
处理这两类缺失,如果直接用"删除含缺失的样本"或者"填一个全局均值",后果是截然不同的。删除会让样本偏向缺失较少的群体,产生选择偏倚;填充均值会压缩这个字段的方差,削弱它和其他变量的真实相关性。
1.2 一个反面案例:均值填充把预测模型的结论带偏了
用之前提到的疾病风险预测数据来说,原始数据里有glucose和insulin两个字段,生理逻辑上二者是强相关的——胰岛素调控血糖,而血糖异常又会反馈到胰岛素分泌。可是填了均值之后,这两个字段的相关性被明显拉低了。
我做了个简单对比:把同一份数据分别用"均值填充"和"保留缺失标识 + KNN插补"处理,然后用同样的随机森林去训练,观察特征重要性排序。结果均值填充那一版把glucose的重要性顶到了第一位,而KNN插补那版里glucose和insulin的排序更贴近临床上已知的判断。原因不复杂:均值填充把这个维度上最有区分度的个体差异抹掉了,模型只能靠"这个样本是不是被填充过"来区别样本,但它看到的特征是失真的。
这就是为什么我一再强调,填充方案不是数据预处理里可以随便选的小环节,它会直接影响预测模型学到的规律是否真实。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. KNN插补的原理和它在医疗场景里的独特优势
2.1 "物以类聚":KNN插补到底在做什么
KNN插补的思路可以用一句话概括:找一个样本最相近的K个样本,用它们在目标字段上的值来估计这个样本的缺失值。这里"相近"的定义依赖距离度量,最常用的就是欧氏距离的变种nan_euclidean,也就是在计算距离时跳过缺失的维度。
比如某个样本在glucose上是空的,但它的年龄、BMI、血压这些字段和其他几个样本非常接近,那么算法就认为这些样本在glucose上的取值,就是这个缺失样本最合理的参考。
距离计算公式:
code复制d(x, y) = sqrt( (可同时观测的维度数量 / 总维度数量) × Σ(可同时观测维度上的平方差) )
这个公式里有一个权重系数(可同时观测维度数 / 总维度数),它的作用是:如果两个样本能共同观测的维度很少,那么它们距离的置信度就低,距离会被放大。
2.2 为什么说KNN插补比均值填充更适合临床数据
均值填充、中位数填充本质上是"用一个统计量替代一个值",它完全没有考虑这个样本在其他指标上的特征。但临床数据有一个很明显的特点:各指标之间往往存在生理上的协同关系。比如BMI和血压、血糖和胰岛素、年龄和某些生化指标之间,都有内在联系。
KNN插补保留了这个协同关系。它等于是在说:既然这个样本和那K个样本在很多维度上都非常相似,那么它在缺失维度上的值也应该参考这些相似样本。这比全局填一个均值要合理得多。
MICE(链式方程多重插补)这类方法也很强大,但它的计算成本明显更高,而且需要为每个含缺失的变量单独设定一个模型,调参量大、收敛判断也比较复杂。KNN插补是"一个算法解决所有字段",实现成本低,效果在中小规模数据集上通常很接近MICE,这是我在实际项目里选择它的核心原因。
2.3 KNN和KMeans别搞混
做插补的时候,经常被问到:KNN和
