训练数据有缺失值怎么办?用 SimpleImputer 填补特征并检查空列

用 SimpleImputer 检查训练中位数、缺失指示列与全空列输出,区分模型特征填补和原始业务事实修复。

数值机器学习特征出现NaN时,可以用SimpleImputer确定一套可复核的填补规则。关键是先看缺的是特征还是业务事实,再检查训练统计量和变换后的列数;单纯让fit不报错,还不足以说明数据处理正确。

先规定填补对象和缺失含义

本例只填补模型输入特征的缺失值,按训练集每列中位数填入;不填补目标标签,也不把预测用的填充值写回原始业务记录。

训练数据有缺失值怎么办?用 SimpleImputer 填补特征并检查空列

示例的两列叫age和amount,只用于说明数值特征接口,没有对应真实客户。0是一个已经观测到的数值,不等于NaN;若输入用空字符串、−999或“未知”表示缺失,应先按字段规则解析,不能无依据把所有0都当缺失。

本文示例已在 Python 3.11、scikit-learn 1.7.2、NumPy 2.4.6、pandas 2.3.3、SciPy 1.17.1 的本地 CPU 环境运行;输入是人为生成的教学数据,不是客户案例。

python -m venv .venv
# Windows PowerShell
.\.venv\Scripts\Activate.ps1
python -m pip install scikit-learn==1.7.2 pandas==2.3.3 numpy==2.4.6 scipy==1.17.1

拟合填补器,检查值与缺失指示列

将代码保存为 missing_features.py 并运行 python missing_features.py;先核对statistics_、输出列名和测试特征,再检查全空列示例的形状。

前半段将填补器和逻辑回归串进Pipeline。训练8行、测试2行仅用于检验处理链,不能提供可信的模型效果评估。测试里的10000故意大于训练金额,检查它不会影响已经学到的300。

import numpy as np
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline

X_train = np.array([[20, 100], [np.nan, 200], [40, np.nan],
                    [25, 250], [35, 350], [30, 300],
                    [45, 450], [np.nan, 400]], dtype=float)
y_train = np.array([0, 0, 1, 0, 1, 0, 1, 1])
X_test = np.array([[np.nan, 10000], [50, np.nan]], dtype=float)
pipe = Pipeline([
    ("fill", SimpleImputer(strategy="median", add_indicator=True)),
    ("model", LogisticRegression(max_iter=1000)),
])
pipe.fit(X_train, y_train)
fill = pipe.named_steps["fill"]
before = fill.statistics_.copy()
transformed = fill.transform(X_test)
print("训练中位数:", fill.statistics_)
print("输出列:", fill.get_feature_names_out(["age", "amount"]))
print("测试特征:", transformed)
print("预测:", pipe.predict(X_test))
assert np.allclose(fill.statistics_, [32.5, 300])
assert np.allclose(transformed, [[32.5, 10000, 1, 0], [50, 300, 0, 1]])
assert np.array_equal(before, fill.statistics_)

all_empty = np.array([[1, np.nan], [2, np.nan], [3, np.nan]])
empty_test = np.array([[4, 8]])
drop = SimpleImputer(strategy="median").fit(all_empty)
keep = SimpleImputer(strategy="median", keep_empty_features=True).fit(all_empty)
print("默认空列输出:", drop.transform(empty_test))
print("保留空列输出:", keep.transform(empty_test))
print("保留时统计量:", keep.statistics_)
assert drop.transform(empty_test).shape == (1, 1)
assert keep.transform(empty_test).shape == (1, 2)
print("保留时缺失填补:", keep.transform([[4, np.nan]]))

核对实际输出的四列含义

训练填充值应为32.5和300,两条测试特征补完后分别是[32.5,10000,1,0]和[50,300,0,1];末尾两列记录原特征是否缺失。

列名 来自哪里 本例检查点
age 原年龄特征或训练中位数 第一条NaN变成32.5
amount 原金额特征或训练中位数 第二条NaN变成300
missingindicator_age age缺失状态 第一条1、第二条0
missingindicator_amount amount缺失状态 第一条0、第二条1

本地示例输出预测[1,1],它只证明两条输入可以经过同一处理链,不表示这两个标签可靠。statistics_在transform之后仍等于之前的副本,测试金额10000没有进入填补统计量;不要对测试集重新fit。

add_indicator=True只为拟合阶段已经出现缺失的特征添加指示列。若某列训练时从未缺失,预测时才第一次缺失,这个选项不会临时增加一个新的指示列;应在上线前用输入契约覆盖这种情况。

空列必须单独验收,不能只看程序能否运行

训练时整列为空就没有中位数可学;默认会丢弃该列,设置keep_empty_features=True可保留列,但本例缺失填补值为0,并不表示已获得有意义的统计量。

第二段代码的训练第2列全是NaN。默认填补器输出[[4.]],shape为(1,1),同时发出跳过无观测特征的UserWarning。保留空列的填补器输出[[4.,8.]];它保留预测时真实提供的8,而预测时仍缺失则变成[[4.,0.]]。这两种结果的列数不同,应连同模型一起检查。

不要为保持维度就把所有全空列机械填0。先找上游是否漏采或字段错名;若字段确实无法观测,决定训练时是否删除。保存预处理器与模型时一并记录输出列名,避免把丢列之后的矩阵继续按旧列含义解释。

替换真实数据后的下一步

按你的样本关系先划分训练、验证和测试集合,再在训练部分拟合。若用交叉验证评估填补方案,把填补器留在Pipeline里,让每一折重新从该折训练数据学习统计量;不能先对全表fit_transform再切分。

中位数是便于解释的基线,不保证补回真实值。缺失原因与业务结果相关、缺失比例很高或目标本身缺失时,需要先查数据采集过程,并在独立数据上比较方案。预测用填补不能替代账务、健康记录等原始事实修复。

官方资料与适用版本

本文于2026年10月1日核对下列官方资料,接口说明对应 scikit-learn 1.7;运行示例使用1.7.2。

Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30722.html

赞 (0)
AI小管家的头像AI小管家
数值特征有离群点怎么缩放?用 RobustScaler 核对中位数与四分位距
上一篇 3小时前
回归目标怎么做对数变换?用 TransformedTargetRegressor 还原原尺度预测
下一篇 3小时前

相关推荐

联系我们

联系我们

1

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
关注微信
分享本页
返回顶部