理解sklearn的KNNImputer

4

我正在阅读相关文档,这里写道:

使用来自训练集中n_neighbors个最近邻居的均值填充每个样本的缺失值。如果两个样本没有缺失的特征相似,则它们是相似的。

现在,我在尝试用一个玩具数据集进行实验。

>>>X = [[1, 2, nan], [3, 4, 3], [nan, 6, 5], [8, 8, 7]]
>>>X

   [[ 1.,  2., nan],
    [ 3.,  4.,  3.],
    [nan,  6.,  5.],
    [ 8.,  8.,  7.]]

我们可以按照以下方式创建一个KNNImputer:

imputer = KNNImputer(n_neighbors=2)

问题是:当两列中存在nan时,它如何填充nan?例如,如果要填充第一行第三列的nan,它会选择哪些特征最接近,因为其中一行第一列也有nan?当我执行“imputer.fit_transform(X)”时,它会给我返回结果。
array([[1. , 2. , 4. ],
       [3. , 4. , 3. ],
       [5.5, 6. , 5. ],
       [8. , 8. , 7. ]])

这意味着填充第1行中的nan,最近的邻居是第二行和第三行。它如何计算第一行和第三行之间的欧几里得距离?
2个回答

4
它如何使用同时具有NaN的行来填充NaN?文档似乎没有提到这一点。但是通过深入挖掘源代码,发现对于每个要填充的列,都会考虑较小距离的所有捐赠者,即使他们有缺失值。处理的方式是在权重矩阵中将缺失值设置为0,该矩阵是根据所使用的距离获得的,请参见_get_weights
相关代码在_calc_impute中,其中在找到所有潜在捐赠者的距离矩阵后,然后是上述权重矩阵,进行了插补处理:
# fill nans with zeros
if weight_matrix is not None:
    weight_matrix[np.isnan(weight_matrix)] = 0.0

如果捐赠者与接收者至少有一个非 NaN 距离,则会考虑所有潜在的捐赠者。
dist_pot_donors : ndarray of shape (n_receivers, n_potential_donors)
    Distance matrix between the receivers and potential donors from
    training set. There must be at least one non-nan distance between
    a receiver and a potential donor.

我们可以通过一个玩具示例来验证这一点;在下面的矩阵中,当输入缺失值 [nan, 7., 4., 5.] 时,选择了最后一行(它也包含两个 NaN),请注意我设置了 n_neighbors=1 。这是因为相对于最后一行的距离是 0,因为与 NaN 值对应的 距离 已经被设为 0。因此,由于与第二行和第三行的差异很小,所以选择了最后一行,因为它被视为相等:
X = np.array([[np.nan,7,4,5],[2,8,4,5],[3,7,4,6],[1,np.nan,np.nan,5]])

print(X)
array([[nan,  7.,  4.,  5.],
       [ 2.,  8.,  4.,  5.],
       [ 3.,  7.,  4.,  6.],
       [ 1., nan, nan,  5.]])

from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=1)

imputer.fit_transform(X)
array([[1., 7., 4., 5.],
       [2., 8., 4., 5.],
       [3., 7., 4., 6.],
       [1., 7., 4., 5.]])

你能解释一下“仅仅因为与第2行和第3行有微小的差异,最后一行被选择,因为它被视为相等”吗? - arghhjayy
所以如果你注意到了,最后一行和第一行只共享最后一列。由于最后一行的其余值都是nan,因此仍然是用来替换第一行的行。这是因为它们之间没有任何差异。nan被视为0差异。而第二行和第三行有一些误差,虽然很小但是不同。因此选择具有最小误差的那一行。@arghhjayy - yatu
明白了。现在,假设将第一行第一列输入nan作为第一步。第二步,我们想要替换第四行第二列的nan。那么,在第二步中,第一行第一列的值将是什么?它会是nan还是第一步中的插补值? - arghhjayy
NaN值是逐列填充的,正如您在process_chunk中所看到的那样。因此,由于第一行中的nan位于左侧,它已经被插补了,因此如果我没有遗漏什么,应该考虑新值。@arghhjayy - yatu

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接