插补缺失值是数据预处理中比较常见的缺失值处理方式,如果数据不可或缺的话就必须进行数据缺失值的处理,如果数据缺失值或有或无(例如在大量数据中重复的缺失值)就可以直接删除掉缺失值。
以下是比较常见的插补缺失值的方式:
方法名称 | 类 (Class) | 适用场景 | 特点 |
---|---|---|---|
均值/中位数/众数插补 | SimpleImputer |
数值型或分类型数据的简单填充 | - mean : 适用于数值型,对异常值敏感- median : 适用于数值型,对异常值鲁棒- most_frequent : 适用于分类型或数值型- constant : 用指定常数填充 |
K近邻插补 | KNNImputer |
数据中存在局部相似性(邻近样本相似) | 基于特征空间中K个最近邻的样本的加权或非加权平均值进行插补,适合数值型数据,计算成本较高 |
迭代插补 | IterativeImputer |
多变量缺失,缺失机制较复杂(如MAR) | 使用其他特征预测缺失特征,迭代进行,功能强大但计算开销大,适用于中小型数据集 |
基于模型的插补 | (通常用 IterativeImputer 或自定义管道) |
缺失模式复杂,特征间有较强依赖 | 可结合回归、随机森林等模型进行预测插补,灵活性高,但需注意过拟合 |
删除法 | pandas.DataFrame.dropna() |
缺失比例极低,或缺失完全随机 (MCAR) | 简单直接,但会损失数据,可能导致偏差,不推荐在缺失较多时使用 |
标记法(指示变量) | MissingIndicator |
分析缺失本身是否有信息 | 生成一个布尔矩阵,标记哪些值是缺失的,可与 Imputer 结合使用,保留缺失的“信息” |
以下我们着重对前三种缺失值处理方式进行讲解,其他的缺失值使用方式也和前三种类似。
SimpleImputer
(均值/中位数/众数插补)
核心思想
SimpleImputer 的核心思想是:用一个统计量或指定的常数值来替换数据集中的缺失值。
常用的填充策略包括:
使用均值(mean)
使用中位数(median)
使用众数(most frequent)
使用指定常数(constant)
适用于数值型和类别型特征。
主要参数(Parameters)
参数 | 说明 |
---|---|
missing_values | 指定缺失值的形式,默认是 np.nan 。也可以是其他值如 0 或 -999 。 |
strategy | 填充策略,可选:'mean' (默认)'median' 'most_frequent' 'constant' |
fill_value | 当 strategy='constant' 时,用来填充的值。默认为 0 。对于字符串或类别数据,可以设为任意字符串。 |
copy | 是否复制原数据进行操作,默认为 True 。 |
常用方法(Methods)
方法 | 说明 |
---|---|
fit(X[, y]) |
计算填充所需的统计量(如均值、中位数、众数),基于训练数据。 |
transform(X) |
应用 fit 得到的规则,对数据进行填充。 |
fit_transform(X[, y]) |
先 fit 再 transform ,常用于训练集。 |
inverse_transform(X) |
如果有逆操作可能,还原填充前的数据(一般不常用)。 |
简单代码示例
import numpy as np
from sklearn.impute import SimpleImputer
# 创建包含缺失值的数据(np.nan 表示缺失)
data = np.array([[1, 2 ],
[np.nan, 3 ],
[7, np.nan],
[4, 6 ]])
print("原始数据:")
print(data)
# 创建 imputer 对象:使用均值填充
imputer = SimpleImputer(strategy='mean', missing_values=np.nan)
# 使用 fit_transform 对数据进行拟合并转换
data_imputed = imputer.fit_transform(data)
print("\n填充后的数据(按列计算均值填充):")
print(data_imputed)
结果:
原始数据:
[[ 1. 2.]
[nan 3.]
[ 7. nan]
[ 4. 6.]]
填充后的数据(按列计算均值):
[[1. 2. ]
[4. 3. ]
[7. 3.66666667]
[4. 6. ]]
类别型数据的填充示例
# 类别型数据示例
data_categorical = np.array([['男', 'A'],
['女', np.nan],
[np.nan, 'B'],
['男', 'A']], dtype=object)
# 使用众数填充
imputer_cat = SimpleImputer(strategy='most_frequent', missing_values=np.nan)
data_cat_imputed = imputer_cat.fit_transform(data_categorical)
print("类别型数据填充后:")
print(data_cat_imputed)
输出:
类别型数据填充后:
[['男' 'A']
['女' 'A']
['男' 'B']
['男' 'A']]
KNNImputer
(K近邻插补)k近邻插补(KNN Imputation)的核心思想是利用数据点之间的相似性来估计缺失值。对于一个含有缺失值的样本,算法首先在特征空间中计算它与其他样本之间的距离(如欧氏距离),然后找出距离最近的k个相似样本,即“k个最近邻”。接着,根据这k个邻居在缺失特征上的实际取值来填充原始样本的缺失值:对于数值型特征,通常采用加权或非加权的均值;对于分类特征,则常采用众数。这种方法相较于均值或中位数插补,能够更好地保留数据的局部结构和变量间的相关性,因为它只参考与目标样本最相似的那些样本进行填充。k近邻插补适用于数据具有较强局部相似性或聚类结构的情况,但其计算成本较高,尤其在大数据集上;同时,k值的选择对插补效果有较大影响,需通过交叉验证等方法合理确定。此外,该方法对特征的尺度敏感,通常需要先进行标准化或归一化处理。
数学计算
k近邻插补(KNN Imputation)的数学计算过程基于样本之间的距离度量和局部相似性假设。其核心思想是:对于一个含有缺失值的样本,通过在完整数据空间中寻找与其最相似的k个样本(即k个最近邻),并利用这些邻居的信息加权或平均来估计缺失值。整个过程可分为以下几个数学步骤:
首先,假设有数据集 X∈Rn×mX \in \mathbb{R}^{n \times m}X∈Rn×m,其中 nnn 是样本数,$ m $ 是特征数。设目标样本 xix_ixi 在第 jjj 个特征上存在缺失,即 xij=NaNx_{ij} = \text{NaN}xij=NaN。为了计算 xix_ixi 与其他样本的距离以找出k个最近邻,需要处理特征缺失带来的距离计算问题。常用的方法是仅使用两个样本都非缺失的特征来计算距离。例如,使用欧氏距离的变体:
d(xi,xl)=∑j∈Sil(xij−xlj)2 d(x_i, x_l) = \sqrt{ \sum_{j \in S_{il}} (x_{ij} - x_{lj})^2 } d(xi,xl)=j∈Sil∑(xij−xlj)2
其中 SilS_{il}Sil 表示样本 xix_ixi 和 xlx_lxl 在相同特征上均非缺失的特征索引集合。这种部分特征距离虽然近似,但允许在不完整样本间进行比较。
接下来,基于计算出的距离,选择距离最小的k个样本构成邻居集合 Nk(xi)N_k(x_i)Nk(xi)。这一步通常通过排序实现。然后,针对目标缺失特征 jjj,从这k个邻居中提取其在第 jjj 个特征上的取值 {xlj∣xl∈Nk(xi)}\{x_{lj} \mid x_l \in N_k(x_i)\}{xlj∣xl∈Nk(xi)}。
对于数值型特征,缺失值的估计通常采用简单平均或距离加权平均:
- 简单平均:x^ij=1k∑xl∈Nk(xi)xlj\hat{x}_{ij} = \frac{1}{k} \sum_{x_l \in N_k(x_i)} x_{lj}x^ij=k1∑xl∈Nk(xi)xlj
- 加权平均:x^ij=∑xl∈Nk(xi)wlxlj∑xl∈Nk(xi)wl\hat{x}_{ij} = \frac{ \sum_{x_l \in N_k(x_i)} w_l x_{lj} }{ \sum_{x_l \in N_k(x_i)} w_l }x^ij=∑xl∈Nk(xi)wl∑xl∈Nk(xi)wlxlj,其中权重 wl=1d(xi,xl)+ϵw_l = \frac{1}{d(x_i, x_l) + \epsilon}wl=d(xi,xl)+ϵ1,ϵ\epsilonϵ 为极小常数以避免除零。
对于类别型特征,则采用众数,即选择k个邻居中出现频率最高的类别作为填充值,也可引入距离权重计算加权投票。
在整个过程中,特征通常需要标准化(如Z-score或Min-Max),以消除量纲影响,确保距离度量的公平性。此外,k值的选择至关重要:k过小易受噪声影响,k过大则可能引入不相关样本。通常通过交叉验证选择最优k。KNN插补能捕捉局部结构,但计算复杂度为 O(n2)O(n^2)O(n2),适合中小规模数据。
k近邻在sklearn中的类或方法介绍
在 scikit-learn(sklearn)中,k近邻插补功能由 sklearn.impute.KNNImputer
类实现。该类通过寻找每个样本的k个最近邻,并利用这些邻居的值来填充缺失数据,是一种基于相似性的插补方法。KNNImputer
能够自动处理数值型数据中的缺失值(NaN),并支持多种距离度量和加权策略。
其主要参数包括:n_neighbors
指定用于插补的最近邻数量,默认为5;weights
控制邻居的权重方式,可选 'uniform'
(等权重)或 'distance'
(距离的倒数作为权重);metric
定义距离度量方式,默认为 'nan_euclidean'
,该度量能忽略缺失值并基于共现特征计算欧氏距离;copy
决定是否复制数据,默认为 True;add_indicator
可选是否添加一个缺失值指示矩阵,用于后续建模时保留缺失信息。
KNNImputer
的使用方式与其他 sklearn 预处理器一致:调用 fit()
方法在训练数据上学习邻居结构,transform()
方法对数据进行插补,或直接使用 fit_transform()
一步完成。它支持对二维数值数组进行操作,适用于特征矩阵的缺失值填补。需要注意的是,该方法对数据的尺度敏感,因此在使用前通常建议对数据进行标准化处理(如 StandardScaler),以避免某些特征因量纲过大而主导距离计算。此外,当数据集较大时,KNNImputer 的计算开销较高,因其需计算样本间的成对距离。它是处理结构化数据中缺失值的一种有效且直观的方法,尤其适用于数据具有局部相关性或聚类结构的场景。
代码示例
import numpy as np
from sklearn.impute import KNNImputer
from sklearn.preprocessing import StandardScaler
# 创建示例数据(包含缺失值)
data = np.array([[1.0, 2.0],
[np.nan, 3.0],
[7.0, np.nan],
[4.0, 6.0],
[5.0, 5.0]])
print("原始数据:")
print(data)
# 方法一:直接使用 KNNImputer(推荐)
# KNNImputer 默认使用欧氏距离,k=5,自动处理缺失值计算
imputer = KNNImputer(n_neighbors=2, weights='uniform') # 可选 weights='distance'
data_imputed = imputer.fit_transform(data)
print("\nKNN插补后的数据:")
print(data_imputed)
# 方法二:结合标准化(更佳实践)
# 因为KNN基于距离,建议先标准化
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data) # 注意:这里会传播NaN,实际中可先粗略填充再标准化
# 重新使用KNNImputer(它能处理NaN)
imputer_scaled = KNNImputer(n_neighbors=2)
data_imputed_scaled = imputer_scaled.fit_transform(data)
# 若需要,可将结果反标准化(但通常插补后直接使用)
print("\n标准化+KNN插补后的数据:")
print(data_imputed_scaled)
# 解释:第一行缺失值会参考第4、5行(最近邻)
# 第二行缺失值会参考第1、4行等,根据特征距离自动计算
IterativeImputer(迭代插补)
迭代插补(Iterative Imputation)的核心思想是将每个含有缺失值的特征依次视为回归问题,利用其他特征预测其缺失值。它通过多轮迭代逐步优化填充值:每一轮中,使用已填充的数据作为输入,对每个特征建立回归模型(如线性回归、贝叶斯回归)来预测其缺失部分。随着迭代进行,填充值不断更新,直至收敛或达到最大迭代次数。该方法能捕捉特征间的复杂关系,比单次插补更精确。在 sklearn 中由
IterativeImputer
实现,适用于中等规模数据集。
迭代插补的数学计算过程
迭代插补(Iterative Imputation)是一种多轮迭代的缺失值填补方法,其数学核心在于将每个特征依次作为目标变量,利用其余特征构建预测模型来估计其缺失值。整个过程从一个简单的初始填充(如均值)开始,然后通过多轮迭代逐步优化填充值,直至收敛。
设数据集为X∈Rn×mX \in \mathbb{R}^{n \times m}X∈Rn×m,其中nnn为样本数,mmm为特征数,部分元素为缺失。令X(t)X^{(t)}X(t)表示第ttt轮迭代后的数据矩阵。初始化时,对每个特征的缺失值用其均值或中位数填充,得到X(0)X^{(0)}X(0)。
在第ttt轮迭代中,对每个特征j=1,2,...,mj = 1,2,...,mj=1,2,...,m,执行以下步骤:将特征jjj作为目标变量yj=X:,j(t−1)y_j = X_{:,j}^{(t-1)}yj=X:,j(t−1),其余特征作为协变量X∖j(t−1)X_{\setminus j}^{(t-1)}X∖j(t−1)。在完整样本(即yjy_jyj非缺失的行)上训练一个回归模型fjf_jfj,例如线性回归:yj=X∖jβj+ϵjy_j = X_{\setminus j}\beta_j + \epsilon_jyj=X∖jβj+ϵj,通过最小二乘法估计参数βj\beta_jβj。然后,使用该模型预测jjj特征在缺失位置的值:y^jmiss=fj(X∖jmiss)\hat{y}_j^{\text{miss}} = f_j(X_{\setminus j}^{\text{miss}})y^jmiss=fj(X∖jmiss),并用预测值更新X(t)X^{(t)}X(t)中的对应元素。
此过程对所有特征循环一次构成一轮迭代。重复多轮,直到填充值的变化小于预设阈值(如∥X(t)−X(t−1)∥<ϵ\|X^{(t)} - X^{(t-1)}\| < \epsilon∥X(t)−X(t−1)∥<ϵ)或达到最大迭代次数。由于每次更新都基于当前最优估计,模型能逐步捕捉特征间的依赖结构。sklearn中的IterativeImputer
默认使用贝叶斯岭回归,因其对多重共线性鲁棒且能提供不确定性估计。该方法能建模非线性关系(若使用非线性回归器),但计算成本较高,且收敛性依赖于数据结构和模型选择。
sklearn中常用的迭代插补类或方法sklearn.impute.IterativeImputer
在 scikit-learn 中,迭代插补通过 sklearn.impute.IterativeImputer
类实现。该类提供了一种高级的缺失值填补策略,适用于具有复杂特征依赖关系的数据集。
核心思想
IterativeImputer
的核心思想是将缺失值填补建模为一个迭代的回归问题。它假设数据在特征之间存在潜在的统计依赖关系,因此可以利用这些关系逐步优化缺失值的估计。具体而言,算法对每个含有缺失值的特征依次作为目标变量,使用其他特征作为输入变量,构建回归模型来预测其缺失部分。这一过程在多轮迭代中重复进行:每一轮中,所有特征的缺失值都被重新预测和更新,随着迭代的进行,填充值逐渐逼近真实值,直至收敛。这种方法比简单的单次插补(如均值或KNN)更能捕捉变量间的复杂关系,尤其适用于数据满足“缺失完全随机”(MCAR)或“随机缺失”(MAR)假设的场景。
主要参数
estimator
:用于拟合回归模型的基础估计器,默认为BayesianRidge()
。也可指定其他回归器如Ridge()
,RandomForestRegressor()
等,以适应线性或非线性关系。missing_values
:指定缺失值的标记,默认为np.nan
。max_iter
:最大迭代次数,默认为10。若填充值在达到此轮数前已收敛,则提前停止。random_state
:控制随机性,确保结果可重现,特别在使用随机算法(如随机森林)时有效。imputation_order
:插补顺序,可选'ascending'
(缺失少的先填)、'descending'
、'roman'
(从左到右)、'reverse'
或'random'
。n_nearest_features
:仅使用与目标特征最相关的前k个特征进行建模,以提高效率并减少噪声干扰。initial_strategy
:初始填充策略,用于第一轮迭代前的预填充,可选'mean'
、'median'
、'most_frequent'
、'constant'
。tol
:收敛阈值,当连续两轮插补结果的平均绝对变化小于该值时停止迭代。
返回值与方法
IterativeImputer
是一个转换器,调用 fit_transform(X)
后返回一个完整的、无缺失值的数组 X_new
,其形状与输入 X
相同。该类遵循 sklearn 的统一接口:
fit(X, y=None)
:在数据X
上训练插补模型,学习特征间的依赖结构。transform(X)
:应用已学习的模型对数据进行插补,通常用于测试集。fit_transform(X, y=None)
:先fit
再transform
,常用于训练集。inverse_transform(X)
:尝试将插补后的数据还原(不常用)。
使用示例
import numpy as np
from sklearn.impute import IterativeImputer
from sklearn.linear_model import BayesianRidge
from sklearn.ensemble import RandomForestRegressor
from sklearn.datasets import make_regression
# 创建示例数据(含缺失值)
X, _ = make_regression(n_samples=100, n_features=5, noise=10, random_state=42)
X = X.astype(np.float64)
# 随机引入缺失值 (设为NaN)
rng = np.random.RandomState(0)
missing_rate = 0.1
n_missing = int(X.size * missing_rate)
missing_idx = rng.choice(X.size, n_missing, replace=False)
X.flat[missing_idx] = np.nan
print("数据形状:", X.shape)
print("缺失值总数:", np.isnan(X).sum())
# 示例1:使用默认的 BayesianRidge 作为估计器
print("\n--- 使用 BayesianRidge 进行迭代插补 ---")
imputer1 = IterativeImputer(
random_state=42,
max_iter=10,
tol=1e-3
)
X_imputed1 = imputer1.fit_transform(X)
print("插补完成,前5行数据示例:")
print(X_imputed1[:5])
# 示例2:使用 RandomForestRegressor(可捕捉非线性关系)
print("\n--- 使用 RandomForestRegressor 进行迭代插补 ---")
rf_estimator = RandomForestRegressor(n_estimators=100, random_state=42)
imputer2 = IterativeImputer(
estimator=rf_estimator,
max_iter=5,
random_state=42,
n_nearest_features=4, # 只使用最相关的4个特征
initial_strategy='median'
)
X_imputed2 = imputer2.fit_transform(X)
print("插补完成,前5行数据示例:")
print(X_imputed2[:5])
# 示例3:指定插补顺序
print("\n--- 按缺失值从少到多的顺序插补 ---")
imputer3 = IterativeImputer(
estimator=BayesianRidge(),
imputation_order='ascending', # 缺失少的特征先插补
random_state=42
)
X_imputed3 = imputer3.fit_transform(X)
print("插补完成。")
输出:
数据形状: (100, 5)
缺失值总数: 50
--- 使用 BayesianRidge 进行迭代插补 ---
插补完成,前5行数据示例:
[[-78.47730754 -45.91018342 49.86429946 23.88051971 75.90350028]
[-66.25752847 -44.90790193 39.32407676 21.75456144 55.02861352]
[-81.45750098 -43.64129887 32.46904595 18.75099757 85.35297552]
[-83.85630998 -51.82522953 49.61871728 16.20524996 97.17506948]
[-81.1710475 -46.76848787 48.76558238 20.28455949 82.16829043]]
--- 使用 RandomForestRegressor 进行迭代插补 ---
插补完成,前5行数据示例:
[[-79.55404138 -47.10818398 50.61735035 23.9607133 76.24067419]
[-65.70774146 -44.98180626 38.88447833 21.72911865 55.47114617]
[-80.40825848 -43.85005367 33.38802717 18.57819498 84.98678477]
[-83.40597077 -51.78977536 49.58798052 16.37075375 97.72328356]
[-81.76476001 -46.7489016 48.55869993 20.19476443 82.60605685]]
--- 按缺失值从少到多的顺序插补 ---
插补完成。
注意事项
由于 IterativeImputer
涉及多轮模型训练,计算开销较大,尤其在高维或大数据集上。此外,它可能在小样本或强共线性数据中不稳定,因此推荐使用正则化回归器(如贝叶斯岭回归)以增强鲁棒性。该方法适用于中等规模数据,是处理复杂缺失模式的强有力工具。