趋近智
数据集很少是完整的。缺失值,通常在pandas DataFrames等数据结构中表示为NaN(非数字)或类似占位符,是一种常见情况。它们可能源于多种原因:数据录入错误、传感器故障、问卷未响应或数据整合中的问题。忽略缺失数据通常是不可行的,因为大多数机器学习算法无法直接处理它们,需要完整的数据集。此外,我们如何处理这些空缺,会显著影响我们分析的质量和预测模型的性能。
虽然用列的均值、中位数或众数填充缺失值这样的简单方法很直接(并且可能在入门学习中已经熟悉),但它们通常会过度简化数据结构,可能扭曲变量之间的关系或减少方差。本节考察处理缺失数据的更复杂策略,超越基本填充,探讨更能保持数据集完整性的方法。
在选择策略之前,思考数据为何缺失是有益的,尽管这通常有难度。统计学家将数据缺失机制分为三种主要类型:
虽然明确判断数据缺失机制有难度,但思考潜在原因可以指导你选择处理策略。
最简单的方法是移除含有缺失值的数据点。
这涉及丢弃含有任何缺失值的整行(观测值)。
谨慎使用行删除法,或许只在缺失数据比例很小(例如,<5%)并且你有充分理由相信它是MCAR时,或者当特定算法要求如此,且填充在该情境下被认为过于复杂或不可靠时才使用。
# 使用pandas的例子
import pandas as pd
import numpy as np
data = {'A': [1, 2, np.nan, 4, 5],
'B': [6, np.nan, 8, 9, 10],
'C': [11, 12, 13, 14, 15]}
df = pd.DataFrame(data)
# 原始数据框:
print("原始数据框:")
print(df)
# 行删除后的DataFrame:
df_dropped = df.dropna()
print("\n行删除后:")
print(df_dropped)
与删除整行不同,对删除法仅使用可用的数据进行特定计算(例如相关或协方差矩阵)。对于列X和Y之间的相关性,它使用X和Y都有非缺失值的所有行。这意味着不同的计算可能使用不同的数据子集。
单值填充法用一个估计值替换每个缺失值。
用列的均值或中位数替换数值型缺失值,用众数替换类别型缺失值,这是一个常见的起始方法。
在此,我们将具有缺失值的特征视为目标变量,并使用其他特征作为回归模型(例如,线性回归)中的预测变量。模型对缺失项的预测结果被用作填充值。
这是对标准回归填充的改进。在使用回归预测缺失值后,将一个从回归误差分布中抽取的随机误差项(残差)添加到预测结果中。
这些方法通常更复杂,并且通常能提供更好的结果,特别是当简单方法的假设不成立时。
这种方法根据其“邻居”的值来填充缺失值。对于某个特征中存在缺失值的数据点,KNN填充基于其他可用特征(使用欧几里得距离等距离度量)识别特征空间中K个最近的数据点(邻居)。然后,利用这K个邻居中该特征的平均值(对于数值型)或众数(对于类别型)来填充缺失值。
# 使用scikit-learn的例子
from sklearn.impute import KNNImputer
import numpy as np
X = [[1, 2, np.nan], [3, 4, 3], [np.nan, 6, 5], [8, 8, 7]]
# 在使用KNNImputer之前,通常应对特征进行缩放
# 初始化KNNImputer(例如,n_neighbors=2)
imputer = KNNImputer(n_neighbors=2)
# 拟合并转换数据
X_imputed = imputer.fit_transform(X)
print("原始数据:")
print(np.array(X))
print("\n填充数据 (KNN):")
print(X_imputed)
多重填充 (MI) 被认为是一种高效方法。MI不只是为每个缺失项填充一个值,而是创建m个(例如5或10个)完整数据集。每个数据集的创建方式是,使用一种包含随机性的方法填充缺失值,以承认真实值的不确定性。
一种常用的MI算法是MICE(多重链式方程填充),通常通过迭代填充来实现:
创建m个数据集后,你分别对每个数据集进行分析(例如,训练你的机器学习模型)。最后,你使用特定的公式(如鲁宾规则)汇总结果(例如,模型系数、预测值、评估指标),以获得一个考虑了填充所引入不确定性的最终结果。
Scikit-learn的IterativeImputer提供了一个基于这种迭代方法的实现,它将每个具有缺失值的特征建模为其他特征的函数。虽然它默认通常生成一个填充数据集,但它构成了MICE的根本原理。
# 使用scikit-learn的IterativeImputer的例子
from sklearn.experimental import enable_iterative_imputer # 启用实验性功能
from sklearn.impute import IterativeImputer
import numpy as np
X = [[1, 2, np.nan], [3, 4, 3], [np.nan, 6, 5], [8, 8, 7]]
# 初始化IterativeImputer
# 它将每个有缺失值的特征建模为其他特征的函数
# 并迭代直到收敛。添加随机性以实现类似MI的行为。
imputer = IterativeImputer(max_iter=10, random_state=0)
# 拟合并转换数据
X_imputed_iterative = imputer.fit_transform(X)
print("原始数据:")
print(np.array(X))
print("\n填充数据 (IterativeImputer):")
print(X_imputed_iterative)
# 注意: 对于真正的多重填充,此过程将重复
# 多次,使用不同的随机状态以生成多个数据集。
有时,某个值缺失的事实本身就包含信息。在进行填充之前,你可以创建额外的二元指示器(虚拟)特征,如果相应特征中的原始值缺失,则为1,否则为0。
然后,这些指示器特征可以与填充后的数据一起包含在你的模型中。这使得模型可能学习与缺失本身相关的模式(捕获一些MNAR情况)。
# 使用pandas的例子
import pandas as pd
import numpy as np
data = {'Age': [25, 30, np.nan, 35],
'Income': [50000, np.nan, 75000, 80000]}
df = pd.DataFrame(data)
# 创建指示器列
for col in df.columns:
if df[col].isnull().any():
df[f'{col}_missing_indicator'] = df[col].isnull().astype(int)
print("包含缺失指示器的DataFrame:")
print(df)
# 现在你可以继续填充'Age'和'Income'中的np.nan
# 例如,使用SimpleImputer:
# from sklearn.impute import SimpleImputer
# imputer = SimpleImputer(strategy='mean')
# df[['Age', 'Income']] = imputer.fit_transform(df[['Age', 'Income']])
# print("\n填充后的DataFrame(保留指示器):")
# print(df)
没有普遍最佳的缺失数据处理方法。最佳选择取决于:
重要实施实践:分割后填充
为防止数据泄露,始终在将数据分割为训练集和测试集之后执行填充。仅使用训练数据拟合你的填充器(例如,SimpleImputer、KNNImputer、IterativeImputer)。然后,使用已拟合的填充器转换训练数据和测试数据。这确保了测试集中的信息不会影响填充过程,模拟了模型在生产环境中遇到新、未见数据的情况。
强烈建议使用scikit-learn管道来简化此过程并避免错误。
流程图,展示了填充器如何融入典型的机器学习流程,并强调仅在训练数据上进行拟合,然后转换训练集和测试集。
周全地处理缺失数据是数据准备中的重要一步。通常需要尝试不同的策略并评估它们对特定建模任务的影响,以找出最适合你的数据集的方法。
这部分内容有帮助吗?
KNNImputer 和 IterativeImputer。© 2026 ApX Machine Learning用心打造