KNN K-最近邻:train_test_split 和 knn.kneighbors

Posted

技术标签:

【中文标题】KNN K-最近邻:train_test_split 和 knn.kneighbors【英文标题】:KNN K-Nearest Neighbors : train_test_split and knn.kneighbors 【发布时间】:2020-06-17 23:29:34 【问题描述】:

我向那些在使用库 sklearn 中的函数 train_test_splitKNN (K-Nearest Neighbors)方面有经验(我没有经验)的人提出以下一般性问题> 算法,尤其是 knn.kneighbors 函数。

假设您在由这些列组成的非常标准的 pandas dataframe(无索引)中有样本:

人名 功能 X1 功能 X2 功能 X3 目标 Y1

假设您有 100 行通用数据。

当您调用函数 train_test_split 时,您将具有特征的列(如 df[['X1','X2','X3']])和具有目标的列作为参数传递(如 df['Y1']),作为回报,您会得到 4 个变量 X_test、X_train、y_test、y_train 以随机方式拆分。

好的。到目前为止一切顺利。

假设在那之后,您使用一种算法 KNN 对测试数据进行预测。 所以你发出如下命令:

knn=KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train,y_train)
y_pred=knn.predict(X_test)

好的。美好的。 y_pred 包含预测。

现在,问题来了,您想查看使预测成为可能的 X_train 数据点的“邻居”。

为此,有一个名为 Knn.Kneighbors 的函数,它返回被视为每个“邻居”的 k 个点的距离和坐标数组(如 X1 X2 X3 的数组) X_train 集的数据点。

neighbors=knn.kneighbors(X=X_test)

问题是:如何将邻居变量中返回的坐标表示的数据作为特征数组与原始数据集联系起来,以了解这些坐标属于谁(=> 列“人名”)?

我的意思是:对于原始数据集的每一行,都有一个关联的“人名”。你不要把它传递给 X_train 或 X_test。那么我可以将 knn.kneighbors 函数返回的邻居数组(现在是随机混合且不参考原始数据)重新链接到原始数据集吗?

有没有简单的方法重新链接回来?我想知道 X_train 数据中点的邻居的名称到底是什么,而不仅仅是函数 knn.kneighbors 返回的匿名坐标数组。

否则我不得不在原始数据集中循环获取邻居坐标以知道他们属于谁......但我希望不要这样做。

在此先感谢大家。 安德烈亚

【问题讨论】:

"为此,有一个名为 Knn.Kneighbors 的函数,它返回一个距离数组和coordinates (like array of X1 X2 X3) k 个点,这些点被认为是每个数据点的“邻居”。 X_train 集。” 不,它们不是坐标。它们是X_train 中最近点的索引,可用于获取坐标。查看返回值here 【参考方案1】:

如果您设置return_distance=False,函数knn.kneighbors(X=X_test) 的输出将更具可读性。在这种情况下,结果数组中的每一行表示n_neighbors 中每个点(行)的最近邻数X_test 的索引。

请注意,这些索引对应于训练集 X_train 中的索引。如果要将它们映射回原始数据框中的Name 列,我认为您必须使用熊猫索引。

我希望下面的例子有意义。


创建数据集:

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier

np.random.seed(42)  # for repoducibility
df = pd.DataFrame(np.random.randn(20, 3),
                  columns=["X1", "X2", "X3"])
df["Name"] = df.index.values * 100  # assume the names are just pandas index * 100
Y = np.random.randint(0, 2, 20)  # targets

print(df)

    X1           X2         X3          Name
0   0.496714    -0.138264   0.647689    0
1   1.523030    -0.234153   -0.234137   100
2   1.579213    0.767435    -0.469474   200
3   0.542560    -0.463418   -0.465730   300
4   0.241962    -1.913280   -1.724918   400
5   -0.562288   -1.012831   0.314247    500
6   -0.908024   -1.412304   1.465649    600
7   -0.225776   0.067528    -1.424748   700
8   -0.544383   0.110923    -1.150994   800
9   0.375698    -0.600639   -0.291694   900
10  -0.601707   1.852278    -0.013497   1000
11  -1.057711   0.822545    -1.220844   1100
12  0.208864    -1.959670   -1.328186   1200
13  0.196861    0.738467    0.171368    1300
14  -0.115648   -0.301104   -1.478522   1400
15  -0.719844   -0.460639   1.057122    1500
16  0.343618    -1.763040   0.324084    1600
17  -0.385082   -0.676922   0.611676    1700
18  1.031000    0.931280    -0.839218   1800
19  -0.309212   0.331263    0.975545    1900

进行训练测试拆分:

X_train, X_test, y_train, y_test = train_test_split(df.iloc[:, :3],
                                                    Y,
                                                    random_state=24  # for reproducibility
                                                   )

注意每个数据框的索引:

print(X_train)

          X1        X2        X3
8   0.375698 -0.600639 -0.291694
14 -0.115648 -0.301104 -1.478522
16  0.343618 -1.763040  0.324084
7  -0.225776  0.067528 -1.424748
10 -0.601707  1.852278 -0.013497
12  0.208864 -1.959670 -1.328186
19 -0.309212  0.331263  0.975545
18  1.031000  0.931280 -0.839218
15 -0.719844 -0.460639  1.057122
11 -1.057711  0.822545 -1.220844
4   0.241962 -1.913280 -1.724918
1   1.523030 -0.234153 -0.234137
0   0.496714 -0.138264  0.647689
3   0.542560 -0.463418 -0.465730
2   1.579213  0.767435 -0.469474

print(X_test)

          X1        X2        X3
13  0.196861  0.738467  0.171368
6  -0.908024 -1.412304  1.465649
17 -0.385082 -0.676922  0.611676
5  -0.562288 -1.012831  0.314247
9   0.375698 -0.600639 -0.291694

由于我们通过设置随机种子确保了可重复性,让我们进行更改以帮助我们理解knn.kneighbors(X=X_test) 的结果。我将X_train 的第一行设置为与X_test 的最后一行相同。由于这两点是相同的,当我们查询X_test.loc[[9]](或X_test.iloc[4, :])时,它应该返回自己作为最近的点。

注意索引为 8 的第一行已更改,等于 X_test 的最后一行:

X_train.loc[8]  = X_test.loc[9]
print(X_train)

          X1        X2        X3
8   0.375698 -0.600639 -0.291694
14 -0.115648 -0.301104 -1.478522
16  0.343618 -1.763040  0.324084
7  -0.225776  0.067528 -1.424748
10 -0.601707  1.852278 -0.013497
12  0.208864 -1.959670 -1.328186
19 -0.309212  0.331263  0.975545
18  1.031000  0.931280 -0.839218
15 -0.719844 -0.460639  1.057122
11 -1.057711  0.822545 -1.220844
4   0.241962 -1.913280 -1.724918
1   1.523030 -0.234153 -0.234137
0   0.496714 -0.138264  0.647689
3   0.542560 -0.463418 -0.465730
2   1.579213  0.767435 -0.469474

训练 KNN 模型:

knn = KNeighborsClassifier(n_neighbors=2)
knn.fit(X_train, y_train)

为简单起见,让我们获取一个点的最近邻(同样的解释适用于多个点)。

获取特定点 X_test.loc[[9]] = [ 0.375698 -0.600639 -0.291694] 的两个最近邻,我们在上面使用它来更改 X_train):

nn_indices = knn.kneighbors(X=X_test.loc[[9]], return_distance=False)
print(nn_indices)
[[ 0 13]]

分别是:

print(X_train.iloc[np.squeeze(nn_indices)])

         X1        X2        X3
8  0.375698 -0.600639 -0.291694  < - Same point in X_train
3  0.542560 -0.463418 -0.465730  < - Second closest point in X_train

这意味着X_train 中的行013 最接近点[ 0.375698 -0.600639 -0.291694]

为了将它们映射到名称,您可以使用:

print(df["Name"][np.squeeze(X_train.index.values[nn_indices])])

8    800
3    300
Name: Name, dtype: int64

如果你没有设置return_distance=False,你会注意到第一个距离值为零(到恰好是它自己的点的距离为零)

nn_distances, nn_indices = knn.kneighbors(X=X_test.loc[[9]])
print(nn_distances)

[[0.         0.27741858]] 

您还可以使用n_neighbors 参数来找出更多最近的邻居。 By default it will run for value used while fitting the model.

编辑:

对于整个X_test,你可以这样做:

nn_indices = knn.kneighbors(X=X_test, return_distance=False)
pd.DataFrame(nn_indices).applymap(lambda x: df["Name"][X_train.index.values[x]])

    0       1
0   1900    0
1   1500    1600
2   1500    0
3   1500    1600
4   800     300

【讨论】:

阿基拉谢谢。现在我明白问题出在哪里了:当你写 print(nn_indices) [[ 0 13]] 时,我错误地查看了 为 0 的行和 为 13 的行(即不是 X_train 集顺便说一句)。相反,您写信让我查看存在的 0 和 13 并实际返回索引 8 和 3,然后您可以使用它们来获得名称(在您的情况下为 800 和 300)。 @AndreaGrianti 不客气!是的,这就是我提到这里必须使用 pandas 索引的原因。不过可能还有其他我不知道的方法。

以上是关于KNN K-最近邻:train_test_split 和 knn.kneighbors的主要内容,如果未能解决你的问题,请参考以下文章

KNN K-最近邻:train_test_split 和 knn.kneighbors

KNN近邻算法

knn是啥意思

KNN(最近邻)分类算法

k-近邻(KNN) 算法预测签到位置

K-近邻算法(KNN)