ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

scikit-learn 缺失值插补完全指南:SimpleImputer / IterativeImputer / KNNImputer 与 MissingIndicator 实战与原理

scikit-learn 缺失值插补完全指南:SimpleImputer / IterativeImputer / KNNImputer 与 MissingIndicator 实战与原理 scikit-learn 缺失值插补完全指南SimpleImputer / IterativeImputer / KNNImputer 与 MissingIndicator 实战与原理【免费下载链接】scikit-learnscikit-learn: machine learning in Python项目地址: https://gitcode.com/gh_mirrors/sc/scikit-learn本文基于 scikit-learn 官方用户指南 doc/modules/impute.rst 编写并结合作者所在仓库sklearn/impute/目录的源码实现、测试用例与示例进行深度佐证。读完本文你将掌握为什么不该轻易丢弃含缺失值的样本、三种内置插补器单变量的SimpleImputer、多变量的IterativeImputer与KNNImputer各自的工作原理与完整参数含义、如何用MissingIndicator/add_indicator保留缺失模式信息、如何保持特征数量不变keep_empty_features以及哪些估算器可以原生处理 NaN 而完全无需插补。缺失值问题与处理的基本原则现实世界中的数据集几乎必然包含缺失值它们可能以空值、NaN或其他占位符的形式出现来源包括设备测量故障、问卷未作答、或某些信息从未被记录。缺失值既可能出现在特征矩阵X中也可能出现在目标y中。scikit-learn 绝大多数估算器都假设数组中的每个元素都是数值且有实际含义因此无法直接在不完整的数据上训练。处理缺失值最朴素的想法是直接丢弃包含缺失值的行或列但这种做法在两方面是有害的丢失信息这些样本中其他完整的特征信息被一并丢弃引入偏差剩余样本很少能代表原始总体除非缺失是完全随机的MCARMissing Completely At Random。同样的告诫也适用于目标变量y静默丢弃结果未知的样本同样会使分析产生偏差。特别是当某个结果尚未被观测到时这属于删失censoring问题应当使用生存分析survival analysis领域的专门方法处理而不是简单地删除数据。官方指南在文首给出了三个关键要点避免丢弃含缺失值的行否则有引入偏差的风险部分监督学习方法通常是基于树的模型可以原生处理含缺失值的数据无需任何额外成本即可取得不错的效果见下文支持 NaN 的估算器一节插补可能计算成本很高且对后续预测性能的提升很快会触及收益递减diminishing returns。预测还是重建插补投入的取舍用户指南引用 Le Morvan 与 Varoquaux 的研究强调了一个容易被忽视的结论只有当你以重建数据本身为目标时才值得在插补质量上大举投入如果目标是预测复杂插补带来的收益往往很有限。为此官方给出了几条高层选择建议从简单开始用SimpleImputer做常数 / 均值 / 众数插补是强大且廉价的基线更精细的插补通常只会带来边际的预测性能提升标记缺失条目增加缺失指示器imputer 的add_indicator选项或独立的MissingIndicator往往有助于预测即使缺失是完全随机的在监督学习环节优先选择表达能力强的模型灵活的估算器从复杂插补中获益更少有些甚至能原生处理缺失值而完全不需要插补复杂插补可能帮助预测但计算开销很大随数据规模扩展性差主要把插补质量投入在重建数据这一目标上。单变量插补与多变量插补按算法类型插补可分为两大类单变量Univariate插补仅使用第 i 个特征维度的非缺失值来填补该维度的缺失值代表是SimpleImputer多变量Multivariate插补利用所有可用特征维度来估计缺失值代表是IterativeImputer与KNNImputer。SimpleImputer单变量特征插补SimpleImputer提供最基本的插补策略可以用给定的常数值也可以用缺失值所在**每一列的描述性统计量均值、中位数或众数**来填充。该类还允许指定不同的缺失值编码missing_values参数。参数速查从 sklearn/impute/_base.py 中SimpleImputer类的定义约 L171 起与_parameter_constraints可以确认如下参数参数默认值说明missing_valuesnp.nan缺失值占位符可以是 int、float、str、np.nan、None或pd.NA所有等于该值的位置都会被插补。pandas 可空整数类型nullable integer dtype的数据可设np.nan或pd.NAstrategymean插补策略mean/median仅限数值数据most_frequent支持字符串或数值并列时返回最小者constant使用fill_value1.5 版本起还支持传入 Callable对该列非缺失值的稠密一维数组运行并返回标量统计量作为填充值fill_valueNone仅strategyconstant时使用None时数值数据默认填0字符串/object 数据默认填missing_valuecopyTrue是否复制XFalse时尽量原地插补。注意非浮点数组、CSR 矩阵、或add_indicatorTrue时仍会强制复制add_indicatorFalse为True时把MissingIndicator的输出堆叠到插补结果之后见标记插补值一节keep_empty_featuresFalse为True时保留拟合阶段全是缺失值的列1.2 版本新增见保持特征数量不变一节用均值填充缺失值下面的示例演示了如何用包含缺失值的列axis 0的均值替换以np.nan编码的缺失值 import numpy as np from sklearn.impute import SimpleImputer imp SimpleImputer(missing_valuesnp.nan, strategymean) imp.fit([[1, 2], [np.nan, 3], [7, 6]]) SimpleImputer() X [[np.nan, 2], [6, np.nan], [7, 6]] print(imp.transform(X)) [[4. 2. ] [6. 3.666] [7. 6. ]]从源码看fit阶段会把每列统计量存入statistics_属性sklearn/impute/_base.py 中_dense_fit使用np.ma.mean/np.ma.median等掩码数组运算transform阶段再按掩码把缺失位置替换为对应统计量statistics_中出现np.nan的特征会在 transform 时被丢弃并给出警告。支持稀疏矩阵SimpleImputer也支持稀疏矩阵输入 import scipy.sparse as sp X sp.csc_array([[1, 2], [0, -1], [8, 4]]) imp SimpleImputer(missing_values-1, strategymean) imp.fit(X) SimpleImputer(missing_values-1) X_test sp.csc_array([[-1, 2], [6, -1], [7, 6]]) print(imp.transform(X_test).toarray()) [[3. 2.] [6. 3.] [7. 6.]]需要特别注意的是这种稀疏格式不应用于隐式存储缺失值否则会在 transform 时被迫稠密化。如果缺失值以 0 编码必须使用稠密输入——源码_validate_input中明确检查了稀疏数据 missing_values 0的组合并抛出ValueError因为这会强制稠密化sklearn/impute/_base.py L392-L399。支持分类数据当使用most_frequent或constant策略时SimpleImputer还支持以字符串或 pandas categorical 表示的分类数据 import pandas as pd df pd.DataFrame([[a, x], ... [np.nan, y], ... [a, np.nan], ... [b, y]], dtypecategory) ... imp SimpleImputer(strategymost_frequent) print(imp.fit_transform(df)) [[a x] [a y] [a y] [b y]]注意mean/median策略配合非数值数据会报错源码会将could not convert类错误改写为更明确的提示Cannot use {strategy} strategy with non-numeric data。完整示例可参考 examples/impute/plot_missing_values.py测试覆盖见 sklearn/impute/tests/test_impute.py。IterativeImputer多变量特征插补更复杂的做法是使用IterativeImputer它把每个含缺失值的特征建模为其他特征的函数并利用该模型进行估计。整个过程以**循环轮转round-robin**方式迭代每一步指定一个特征列作为输出y其余特征列作为输入X在y已知的样本上对(X, y)拟合一个回归器用该回归器预测y的缺失值对每个特征重复上述过程如此迭代max_iter轮返回最后一轮的插补结果。 import numpy as np from sklearn.impute import IterativeImputer imp IterativeImputer(max_iter10, random_state0) imp.fit([[1, 2], [3, 6], [4, 8], [np.nan, 3], [7, np.nan]]) IterativeImputer(random_state0) X_test [[np.nan, 2], [6, np.nan], [np.nan, 6]] # 模型学会了第二个特征是第一个特征的两倍 print(np.round(imp.transform(X_test))) [[ 1. 2.] [ 6. 12.] [ 3. 6.]]SimpleImputer与IterativeImputer都可以放进Pipeline中构成支持插补的复合估算器见 examples/impute/plot_missing_values.py。参数速查根据 sklearn/impute/_iterative.py类定义约 L58 起核心参数如下参数默认值说明estimatorBayesianRidge()每轮轮转插补使用的回归器sample_posteriorTrue时其predict必须支持return_stdsample_posteriorFalse是否从拟合估计器的高斯预测后验中采样做多重插补时应设为Truemax_iter10最大插补轮数早停条件为max(abs(X_t - X_{t-1})) / max(abs(X[known_vals])) tol仅在sample_posteriorFalse时生效tol1e-3早停容差n_nearest_featuresNone每个特征仅使用其他多少个特征来估计特征间距离用初始插补后两两特征的绝对相关系数衡量并按与相关性成正比的概率抽样以确保覆盖特征数巨大时可显著加速None表示使用全部特征initial_strategymean初始化缺失值的策略与SimpleImputer.strategy相同mean/median/most_frequent/constantfill_valueNone配合initial_strategyconstant使用规则同SimpleImputerimputation_orderascending插补顺序ascending按缺失量升序、descending降序、roman从左到右、arabic从右到左、random随机skip_completeFalse为True时跳过训练阶段没有缺失值的特征可加速min_value/max_value-np.inf/np.inf插补值的裁剪范围支持数组形式的逐特征上下限verbose0打印进度详情random_stateNone控制n_nearest_features特征抽样、imputation_orderrandom及sample_posteriorTrue的后验采样传入整数可保证确定性add_indicator/keep_empty_featuresFalse/False与SimpleImputer同义收敛性与收益递减IterativeImputer会循环max_iter轮并在sample_posteriorFalse时若相邻两轮之间的变化低于tol则提前停止。但实践表明插补值常常并不真正收敛——轮转方案并不能保证达到不动点因此迭代轮数最好被视为与可用时间预算之间的权衡而不是必须达成的目标。当目标是预测时这几乎不是问题。Le Morvan 与 Varoquaux 的研究报告了强烈的收益递减效应更精确的插补对下游预测性能只能带来微小的提升尤其是配合表达能力强的模型和缺失指示器add_indicator时。官方给出的务实建议做预测时优先使用小的固定max_iter例如max_iter10配合缺失指示器与表达能力强的下游模型而不是在收敛性上投入做数据重建时迭代轮数例如max_iter50和插补器的选择才更重要应在具体任务上评估。灵活性一个类实现多种插补算法R 语言数据科学生态中有许多成熟的插补包Amelia、mi、mice、missForest 等。其中 missForest 非常流行而它其实是多种序贯插补算法的一个特例——只需给IterativeImputer传入不同的回归器即可实现这些算法。对 missForest 而言这个回归器就是随机森林Random Forest。参见 examples/impute/plot_iterative_imputer_variants_comparison.py 中的对比示例。多重插补 vs 单次插补统计学界普遍采用多重插补multiple imputation对同一特征矩阵生成m份独立的插补结果每份都跑一遍后续分析流程特征工程、聚类、回归、分类最后比较m份分析结果如留出验证误差从而了解缺失值固有不确定性对分析结果的影响。本仓库的IterativeImputer实现受 R 语言 MICE 包Multivariate Imputation by Chained EquationsVan Buuren 与 Groothuis-Oudshoorn 2011 年发表于 Journal of Statistical Software启发但与之不同它默认只返回单次插补而不是多次插补。不过当sample_posteriorTrue时可以反复以不同随机种子对同一数据集应用IterativeImputer从而实现多重插补更多讨论见 Little Rubin 1986《Statistical Analysis with Missing Data》第 4 章。需要注意IterativeImputer.transform不允许改变样本数量因此无法通过单次调用transform得到多重插补必须多次调用。另外从源码sklearn/impute/_iterative.py 的版本注释可以确认自1.10 版本起IterativeImputer不再是实验性功能可以直接从sklearn.impute导入无需再通过sklearn.experimental启用。sample_posteriorTrue的实现细节预测后验采样、truncnorm裁剪等在 sklearn/impute/tests/test_impute.py 中有大量测试覆盖。KNNImputer最近邻插补KNNImputer使用 k-最近邻方法填充缺失值。默认采用一种支持缺失值的欧氏距离度量sklearn.metrics.pairwise.nan_euclidean_distances来寻找最近邻两个样本的距离只在其都不缺失的特征上计算并对缺失维做加权校正。每个缺失特征用n_neighbors个在该特征上有值的最近邻的值来插补邻居特征值被均匀平均或按到各邻居的距离加权平均。关键行为官方文档与 sklearn/impute/_knn.py 中_calc_impute实现一致若一个样本有多个特征缺失不同特征的邻居集合可能不同当可用邻居数少于n_neighbors且与训练集不存在任何有定义的距离时使用该特征在训练集中的平均值若至少存在一个距离有定义的邻居则使用其余邻居的加权或非加权平均若某特征在训练中始终缺失则会在transform时被移除。方法学出处为 Troyanskaya 等人 2001 年发表于 Bioinformatics 的 DNA 微阵列缺失值估计论文。参数速查参数默认值说明missing_valuesnp.nan缺失值占位符int、float、str、np.nan或Nonepandas 可空整型需设np.nann_neighbors5用于插补的邻居样本数 1的整数weightsuniformuniform等权平均distance按距离倒数加权或自定义 callable接收距离数组返回同形状权重数组metricnan_euclidean邻居搜索的距离度量nan_euclidean或符合func_metric(x, y, *, missing_valuesnp.nan)签名的 callablecopyTrue是否复制Xadd_indicator/keep_empty_featuresFalse/False同前示例用两个最近邻特征值的均值替换np.nan编码的缺失值 import numpy as np from sklearn.impute import KNNImputer nan np.nan X [[1, 2, nan], [3, 4, 3], [nan, 6, 5], [8, 8, 7]] imputer KNNImputer(n_neighbors2, weightsuniform) imputer.fit_transform(X) array([[1. , 2. , 4. ], [3. , 4. , 3. ], [5.5, 6. , 5. ], [8. , 8. , 7. ]])从实现看_calc_impute用np.argpartition高效选取前n_neighbors个候选捐赠者再用掩码数组对捐赠者列做带权平均权重矩阵中的NaN被置 0sklearn/impute/_knn.py L163-L211transform阶段按块chunk计算距离矩阵以控制内存测试覆盖见 sklearn/impute/tests/test_knn.py。保持特征数量不变keep_empty_features默认情况下scikit-learn 的插补器会丢弃完全为空的特征即整列都是缺失值的列。例如 imputer SimpleImputer() X np.array([[np.nan, 1], [np.nan, 2], [np.nan, 3]]) imputer.fit_transform(X) array([[1.], [2.], [3.]])X中第一列全为np.nan插补后被丢弃。虽然这类特征对预测没有帮助但丢弃列会改变X的形状在更复杂的机器学习管道中可能引发问题例如与ColumnTransformer或特征名称映射配合时。参数keep_empty_features提供了保留空特征的选项——此时会用常数值填充大多数情况下这个常数值是 0 imputer.set_params(keep_empty_featuresTrue) SimpleImputer(keep_empty_featuresTrue) imputer.fit_transform(X) array([[0., 1.], [0., 2.], [0., 3.]])源码层面当keep_empty_featuresTrue时_dense_fit/_sparse_fit会为全空列填充0而非np.nan到statistics_transform时也不再走过滤无效统计量分支sklearn/impute/_base.py L508-L510、L630-L652。IterativeImputer与KNNImputer同样支持该参数strategyconstant时用fill_value而非 0测试见 sklearn/impute/tests/test_impute.py 中的test_*_keep_empty_features系列。MissingIndicator标记插补值MissingIndicator转换器把数据集变换为对应的二值矩阵指示每个位置是否为缺失值。该变换常与插补配合使用插补之后哪些值原本是缺失的这一信息仍然具有预测价值。与 add_indicator 的关系SimpleImputer和IterativeImputer都有布尔参数add_indicator默认False。设为True时它会自动把MissingIndicator的输出堆叠stack到插补结果之后——等价于手动拼接但更方便。内部实现上_BaseImputer._fit_indicator会创建并拟合一个MissingIndicatorerror_on_newFalse_concatenate_indicator再负责将指示矩阵与原数据水平拼接sklearn/impute/_base.py L114-L163。自定义缺失占位符与 features 参数NaN通常是缺失值占位符但它强制数据类型为 float。参数missing_values允许指定其他占位符例如整数-1 from sklearn.impute import MissingIndicator X np.array([[-1, -1, 1, 3], ... [4, -1, 0, -1], ... [8, -1, 1, 0]]) indicator MissingIndicator(missing_values-1) mask_missing_values_only indicator.fit_transform(X) mask_missing_values_only array([[ True, True, False], [False, True, True], [False, True, False]])features参数用于选择为哪些特征构造掩码。默认值为missing-only只返回在 fit 时包含缺失值的特征的掩码 indicator.features_ array([0, 1, 3])设为all则返回所有特征无论是否含缺失值 indicator MissingIndicator(missing_values-1, featuresall) mask_all indicator.fit_transform(X) mask_all array([[ True, True, False, False], [False, True, False, True], [False, True, False, False]]) indicator.features_ array([0, 1, 2, 3])其他参数sparse默认auto是否输出稀疏矩阵与error_on_new默认Truetransform 时若出现 fit 未见过的缺失特征列是否报错imputer 内部调用时设为False。在 Pipeline 中组合使用在Pipeline中使用MissingIndicator时务必借助FeatureUnion或ColumnTransformer把指示特征与常规特征合并。下面用 iris 数据集演示完整流程——先给数据注入随机缺失 from sklearn.datasets import load_iris from sklearn.impute import SimpleImputer, MissingIndicator from sklearn.model_selection import train_test_split from sklearn.pipeline import FeatureUnion, make_pipeline from sklearn.tree import DecisionTreeClassifier X, y load_iris(return_X_yTrue) mask np.random.randint(0, 2, sizeX.shape).astype(bool) X[mask] np.nan X_train, X_test, y_train, _ train_test_split(X, y, test_size100, ... random_state0)构造FeatureUnion所有特征先用SimpleImputer插补以支持分类器同时附加MissingIndicator的指示变量 transformer FeatureUnion( ... transformer_list[ ... (features, SimpleImputer(strategymean)), ... (indicators, MissingIndicator())]) transformer transformer.fit(X_train, y_train) results transformer.transform(X_test) results.shape (100, 8)注意上面的transformer本身不能做预测还需要包一层带分类器的Pipeline clf make_pipeline(transformer, DecisionTreeClassifier()) clf clf.fit(X_train, y_train) results clf.predict(X_test) results.shape (100,)iris 有 4 个特征插补后 4 列 指示器fit 时含缺失的特征4 列共 8 列与输出形状(100, 8)吻合。支持 NaN 的估算器可原生处理缺失值的模型并非所有估算器都需要先插补。官方文档的Estimators that handle NaN values一节会按类型cluster、regressor、classifier、transformer列出无需预处理即可处理 NaN 的估算器列表。这个列表是在文档构建时动态生成的doc/sphinxext/allow_nan_estimators.py中的AllowNanEstimators指令遍历sklearn.utils.all_estimators对每个估算器实例化并检查其__sklearn_tags__()中的input_tags.allow_nan标签该标签定义于 sklearn/utils/_tags.py来决定是否收录。几个值得注意的事实判定是按实例进行的因此会包含只有在特定超参数组合下才支持 NaN的估算器例如SplineTransformer仅在handle_missingzeros时才允许 NaN这一点已写入指令实现注释典型代表是基于树的模型决策树、随机森林、梯度提升等它们可以在分裂时把缺失值路由到最佳分支从源码结构看SimpleImputer的__sklearn_tags__中allow_nan is_scalar_nan(self.missing_values)sklearn/impute/_base.py L165-L168即只有当缺失值占位符是标量NaN时插补器才声明允许 NaN 输入。完整的、随版本变化的估算器清单以构建后的 API 文档doc/modules/impute.rst中.. allow_nan_estimators::指令渲染结果为准。实战选型建议综合官方指南与源码实现可以总结如下选型路径先判断目标预测任务优先追求简单与稳定数据重建任务才需要在插补质量上投入优先考虑原生支持 NaN 的模型如树模型可能完全省去插补步骤需要插补时从SimpleImputer起步均值 / 中位数 / 众数 / 常数并配合add_indicatorTrue保留缺失模式特征间存在强关联且数据集规模可接受时尝试IterativeImputer默认BayesianRidge固定max_iter10即可或KNNImputer注意其距离计算与邻居加权开销关心缺失带来的不确定性时用IterativeImputer(sample_posteriorTrue)配多个随机种子做多重插补在复杂管道中使用时设置keep_empty_featuresTrue以避免全空列导致的特征形状变化并用FeatureUnion/ColumnTransformer正确接入MissingIndicator。参考与延伸阅读官方用户指南doc/modules/impute.rst核心源码sklearn/impute/_base.pySimpleImputer/MissingIndicator、sklearn/impute/_iterative.pyIterativeImputer、sklearn/impute/_knn.pyKNNImputer端到端示例examples/impute/plot_missing_values.py、examples/impute/plot_iterative_imputer_variants_comparison.py测试用例sklearn/impute/tests/test_impute.py、sklearn/impute/tests/test_knn.py、sklearn/impute/tests/test_common.py文档生成逻辑doc/sphinxext/allow_nan_estimators.py关键文献标题与出处见官方指南引用Van Buuren Groothuis-Oudshoorn2011mice: Multivariate Imputation by Chained Equations in RJournal of Statistical SoftwareLittle Rubin1986《Statistical Analysis with Missing Data》Le Morvan Varoquaux2025Imputation for prediction: beware of diminishing returnsICLRTroyanskaya 等2001Missing value estimation methods for DNA microarraysBioinformatics。【免费下载链接】scikit-learnscikit-learn: machine learning in Python项目地址: https://gitcode.com/gh_mirrors/sc/scikit-learn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表