KNN K-Ближайшие соседи: train_test_split и knn.kneighbors - PullRequest
0 голосов
/ 05 марта 2020

Я задаю следующий общий вопрос тем, у кого есть опыт (которого у меня нет) в использовании функции train_test_split в библиотеке sklearn вместе с KNN (K-Nearest Neighbours) алгоритмом и, в частности, knn.kneighbors функция.

Предположим, у вас есть выборки в очень стандартном pandas фрейме данных (без индексации), составленном из этих столбцов:

  • Имя человека
  • Функция X1
  • Функция X2
  • Функция X3
  • Цель Y1

Итак, у вас есть, скажем, 100 строк обобщенных c данных.

Когда вы вызываете функцию 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 , которая возвращает массив расстояний и координат (например, массив X1 X2 X3) из k точек, которые считались «соседями» для каждого точки данных набора X_train.

neighbors=knn.kneighbors(X=X_test)

Вопрос в том, как связать данные, представленные координатами, возвращенными в переменную соседей, в виде массива объектов с исходным набором данных, чтобы понять, кто (=> столбец «Имя человека»). ) эти координаты принадлежат?

Я имею в виду: для каждой строки исходного набора данных было связано «Имя человека». Вы не передаете это X_train или X_test. Так можно ли повторно связать массив соседей, возвращенный функцией knn.kneighbors (которые теперь произвольно смешаны и не ссылаются на исходные данные), с исходным набором данных?

Есть ли простой способ вернуть ссылку обратно? В конце я хотел бы знать, как зовут соседей точек в данных X_train, а не только анонимный массив координат, который возвращает функция knn.kneighbors.

В противном случае я вынужден возьмите координаты соседей в al oop в исходном наборе данных, чтобы узнать, кому они принадлежали ... но я надеюсь не делать этого.

Спасибо всем заранее. Andrea

1 Ответ

1 голос
/ 05 марта 2020

Вывод функции knn.kneighbors(X=X_test) более читабелен, если вы установите return_distance=False. В этом случае каждая строка в результирующем массиве представляет индексы n_neighbors числа ближайших соседей для каждой точки (строки) в X_test.

Обратите внимание, что эти индексы соответствуют индексам в обучающем наборе X_train. Если вы хотите отобразить их обратно в столбец Name в исходном фрейме данных, я думаю, что вы должны использовать индексы pandas.

Надеюсь, приведенный ниже пример имеет смысл.


Создание набора данных:

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)

Чтобы упростить задачу, давайте получим ближайших соседей в одну точку (такое же объяснение применимо для несколько точек).

Получение двух ближайших соседей для указанного c пункта 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

Это означает, что строки 0 и 13 в X_train являются ближайшими к точке [ 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, чтобы получить больше ближайших соседей. По умолчанию он будет работать для значения, используемого при подгонке модели .

Редактировать:

Для всего 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
...