深入理解 KNN 填充器:如何在机器学习中高效处理缺失数据

在处理现实世界的数据集时,我们经常遇到一个棘手的问题:数据缺失。无论是因为传感器故障、人工录入错误还是调查问卷中的未填写项,缺失值(在数据集中通常显示为 INLINECODE7d47b53f 或 INLINECODEa549cc65)都是数据科学家必须面对的挑战。

简单地删除包含缺失值的行往往不是最佳选择,因为这会导致宝贵的数据流失。而传统的填充方法——如使用平均值、中位数或众数——虽然简单,却完全忽略了特征之间可能存在的复杂关系。

在本文中,我们将深入探讨一种更智能的方法:KNN 填充器(KNN Imputer)。我们将一起探索它如何利用“物以类聚”的原理,通过寻找最相似的样本来填补空白,从而最大程度地保留数据的内在结构。让我们开始吧!

什么是 KNN 填充器?

KNN 填充器是一种基于机器学习算法的无监督学习方法,专门用于解决数据缺失问题。与单变量填充方法(仅使用当前列的统计信息)不同,KNN 填充器是一种多元方法。这意味着在估算某个缺失值时,它会同时考虑数据集中其他特征的信息。

核心优势

  • 多元处理:它不仅关注单一特征,而是利用特征之间的相关性来推断缺失值,从而提高预测的准确性。
  • 数据驱动:该方法不依赖外部假设,而是完全基于数据集内部蕴含的模式进行估算。
  • 鲁棒性强:相比简单的均值填充,它能更好地保持变量之间的相关性和数据的整体分布结构。

KNN 填充器的工作原理

该方法的构建基础是 K-近邻算法(KNN),一种非常直观且强大的算法。其核心思想是:“近朱者赤,近墨者黑”——即一个数据点的属性通常与它在特征空间中最近的邻居相似。

具体来说,填充过程分为以下几个关键步骤:

  • 距离计算:算法首先会计算包含缺失值的数据点(目标点)与数据集中其他所有数据点之间的“距离”。默认情况下,使用支持缺失值处理的 欧几里得距离
  • 识别邻居:根据计算出的距离,选择距离目标点最近的 INLINECODE0dc89f3e 个邻居。这个 INLINECODEb8cdfe1e 是一个超参数,我们可以根据实际情况进行调整。
  • 加权填充:使用这 k 个邻居的值来估算缺失值。对于连续特征,通常使用邻居值的均值(可以是加权均值,也可以是简单平均);对于分类特征,则可能使用众数。
  • 迭代处理:对数据集中的每一个缺失值重复上述过程,直到所有缺失值被填补。

数学原理示例

为了让你更直观地理解,让我们通过一个简单的数学示例来演示这个过程。

假设我们有以下包含缺失值的小型数据集:

Observation

X1

X2

X3

1

2.0

1.0

3.0

2

3.0

2.0

4.0

3

NaN

1.5

5.0

4

5.0

3.5

6.0

5

4.0

NaN

4.5我们的目标是填补 Observation 3 中 X1 的缺失值。我们设定 n_neighbors=2

#### Step 1: 识别缺失特征

目标:观测值 3 的 X1 特征。我们需要利用其他特征(X2 和 X3)来寻找相似样本。

#### Step 2: 计算距离

我们利用欧几里得距离公式来计算观测值 3 与其他完整行(观测值 1, 2, 4)之间的距离。注意:计算距离时,我们只使用已知特征(X2 和 X3)。

公式

$$ d(a, b) = \sqrt{(a{x2} – b{x2})^2 + (a{x3} – b{x3})^2} $$

  • 与观测值 1 的距离

$$ d(3,1) = \sqrt{(1.5 – 1.0)^2 + (5.0 – 3.0)^2} = \sqrt{0.25 + 4.0} = \sqrt{4.25} \approx 2.06 $$

  • 与观测值 2 的距离

$$ d(3,2) = \sqrt{(1.5 – 2.0)^2 + (5.0 – 4.0)^2} = \sqrt{0.25 + 1.0} = \sqrt{1.25} \approx 1.12 $$

  • 与观测值 4 的距离

$$ d(3,4) = \sqrt{(1.5 – 3.5)^2 + (5.0 – 6.0)^2} = \sqrt{4.0 + 1.0} = \sqrt{5.0} \approx 2.24 $$

#### Step 3: 寻找最近邻

根据计算结果:

  • 观测值 2 (距离 1.12) – 最近
  • 观测值 1 (距离 2.06) – 次近
  • 观测值 4 (距离 2.24) – 较远

我们选择最近的 观测值 2观测值 1 作为邻居。

#### Step 4: 填充缺失值

我们取这两个邻居在 X1 列上的数值的均值来填充观测值 3 的 X1。

$$ \text{填充值} = \frac{X1{obs2} + X1{obs1}}{2} = \frac{3.0 + 2.0}{2} = 2.5 $$

因此,观测值 3 的 X1 缺失值被填充为 2.5

实战演练:Python 代码实现

理论说得再多,不如动手实践来得实在。让我们来看看如何在 Python 中使用 scikit-learn 库实现 KNN 填充器。

示例 1:基础使用

首先,我们需要导入必要的库。为了模拟真实场景,我们将使用 NumPy 和 Pandas 来创建和处理数据。

import numpy as np
import pandas as pd
from sklearn.impute import KNNImputer

# 1. 创建包含缺失值的示例数据
# 注意:我们故意在 X1 和 X2 中放入了一些 np.nan
data = {
    ‘X1‘: [2.0, 3.0, np.nan, 5.0, 4.0],
    ‘X2‘: [1.0, 2.0, 1.5, 3.5, np.nan],
    ‘X3‘: [3.0, 4.0, 5.0, 6.0, 4.5]
}
df = pd.DataFrame(data)

print("--- 原始数据 (包含 NaN) ---")
print(df)

# 2. 初始化 KNN 填充器
# n_neighbors=2 表示参考最近的 2 个数据点
# add_indicator=False 表示不添加指示缺失值的掩码矩阵
imputer = KNNImputer(n_neighbors=2)

# 3. 拟合与转换
# 这一步会计算距离并填充数据
imputed_data = imputer.fit_transform(df)

# 4. 将结果转回 DataFrame 以保持格式整洁
imputed_df = pd.DataFrame(imputed_data, columns=df.columns)

print("
--- 填充后的数据 ---")
print(imputed_df)

代码解析

  • 我们首先手动创建了一个包含 NaN 的 DataFrame。
  • INLINECODEc55e93a7 初始化了填充器。你可以尝试将 INLINECODEc369a9a3 改为 INLINECODE158aa9b3 或 INLINECODEfef10eb7,结果可能会有细微变化。
  • fit_transform 方法做了两件事:先计算每一对点之间的距离,然后执行填充操作。
  • 最终结果是一个 NumPy 数组,我们将其转回 DataFrame 以便阅读。

示例 2:调整距离度量方式

欧几里得距离是默认选择,但它不是唯一的选项。如果你的数据包含离群点,或者特征具有不同的量纲,曼哈顿距离 有时是更好的选择。

from sklearn.impute import KNNImputer

# 使用曼哈顿距离(metric=‘manhattan‘)
imputer_manhattan = KNNImputer(n_neighbors=2, metric=‘manhattan‘)

# 仅为了演示,我们重新对数据集进行转换
imputed_data_manhattan = imputer_manhattan.fit_transform(df)

df_manhattan = pd.DataFrame(imputed_data_manhattan, columns=df.columns)

print("--- 使用曼哈顿距离填充的结果 ---")
print(df_manhattan)

实用见解:当数据维度较高或存在许多无关特征时,欧几里得距离可能会受到“维度灾难”的影响。曼哈顿距离或者带权重的距离度量可能会提供更稳定的结果。

示例 3:加权距离填充

在实际应用中,我们可能希望距离越近的邻居对结果的影响越大。INLINECODE8abc06e6 允许我们开启 INLINECODE6ccbbecd 参数来实现这一点。

# 使用距离加权:近处的邻居权重更大,远处的权重更小
imputer_weighted = KNNImputer(n_neighbors=2, weights=‘distance‘)
imputed_weighted = imputer_weighted.fit_transform(df)

df_weighted = pd.DataFrame(imputed_weighted, columns=df.columns)

print("--- 使用加权距离填充的结果 ---")
print(df_weighted)

示例 4:处理多列缺失值的情况

现实世界的数据往往很混乱,可能在同一行有多个缺失值。KNN 填充器能够智能地处理这种情况。它会基于当前非缺失的特征来计算距离,从而估算缺失的值。让我们构建一个稍微复杂一点的例子:

import numpy as np
import pandas as pd
from sklearn.impute import KNNImputer

# 创建一个更复杂的数据集
complex_data = {
    ‘Height‘: [170, 180, np.nan, 160, np.nan, 175],
    ‘Weight‘: [65, np.nan, 80, 50, 70, np.nan],
    ‘Age‘: [25, 30, 35, np.nan, 28, 32]
}
df_complex = pd.DataFrame(complex_data)

print("--- 复杂缺失数据 ---")
print(df_complex)

# 这里的逻辑是:身高和体重通常相关,年龄也与两者相关
# 算法会根据 Age 和 Weight 来推断 Height,反之亦然
imputer = KNNImputer(n_neighbors=2)
imputed_complex = imputer.fit_transform(df_complex)

df_complex_filled = pd.DataFrame(imputed_complex, columns=df_complex.columns)
print("
--- 填充结果 ---")
print(df_complex_filled)

示例 5:添加缺失值指示器

有时候,知道哪些值是被填充的(原本就是缺失的)对于后续的建模非常重要。我们可以使用 add_indicator=True 来自动添加指示列。

“INLINECODEe2cf4c03`INLINECODE402c8735StandardScalerINLINECODE669afaf5MinMaxScalerINLINECODE822d912cScalerINLINECODEdc4c3f04ImputerINLINECODEb492b88fnneighborsINLINECODE3c63861dkINLINECODE573e0d56sklearn.impute.IterativeImputerINLINECODE17f909ddKNNImputerINLINECODE72326086nneighbors 和距离度量方式 metric`。

  • 预处理要求:请务必注意数值的缩放问题,以避免某些特征因数值范围过大而主导距离计算。
  • 计算开销:虽然效果更好,但它比均值填充慢,在超大数据集上需谨慎使用。

你的下一步行动

不要只停留在理论层面。打开你的 Jupyter Notebook 或 Colab,尝试加载你手头的一个包含缺失值的数据集,应用 KNN 填充器,并与均值填充的结果进行对比。观察模型性能的变化,这将是掌握这一技术的最好方式。

祝你数据分析顺利!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/50873.html
点赞
0.00 平均评分 (0% 分数) - 0