Поиск евклидова расстояния между всеми точками одной выборки и всеми точками другой

нужна помощь.

Прохожу курс по машинному обучению с нуля, где алгоритмы пишутся вручную. Как в методе ближайших соседей посчитать евклидово расстояние всех точек тестовой выборки до точек обучающей выборки? Сделал такое с помощью циклов, но вычисляется очень долго, хотелось бы узнать как переделать на броадкастинг.

def euclid(self, data1, data2):
    d = []
    for i in range(len(data1)):
        d.append(sum((data2 - data1.iloc[i])**2) ** 0.5)
    return d   

def NN(self, X_test, X):
    distance = [{self.y.iloc[i]: self.euclid(X_test, X.iloc[i])} for i in range(len(X))]
    return distance

Ответы (1 шт):

Автор решения: Vitalizzare

Чтобы получить таблицу расстояний от каждой точки одной выборки к каждой точке другой, лучше перейти в numpy:

import numpy as np
import pandas as pd

def distance(data1, data2):
    x = np.asarray(data1)[..., np.newaxis]
    y = np.asarray(data2).T[np.newaxis, ...]
    data = np.sum((x - y)**2, axis=1)**0.5
    if isinstance(data1, pd.DataFrame) and isinstance(data2, pd.DataFrame):
        data = pd.DataFrame(data, index=data1.index, columns=data2.index)
    return data

Здесь мы транспонируем вторую таблицу и разводим индексы на две независимые оси (первую и третью), оставляя столбцы (значения по второй оси) в качестве объединяющей размерности.

Как результат, операция x - y вернет трехмерный массив, где крайние размерности указывают на точки исходных таблиц, а вдоль размерности между ними - векторы расстояний между точками. Координаты этих векторов мы суммируем, предварительно возведя в квадрат np.sum((x - y)**2, axis=1). После этого извлекаем квадратный корень, получив расстояния.

Замечу, что есть разные варианты, как перейти к numpy.ndarray. Например, если мы уверены, что исходные данные представлены только как pandas.DataFrame, то могли бы использовать df.to_numpy() или df.values. Использование numpy.asarray дает возможность работать с двумерными массивами разного типа, например с тем же ndarray или списком списков.

Пример:

data1 = [
    [0, 0, 1],
    [0, 1, 0],
    [1, 0, 0],
    [0, 1, 1],
    [1, 0, 1],
    [1, 1, 0],
]

data2 = [[0, 0, 0], [1, 1, 1]]

a, b = 1, 2**0.5
expected = [
    [a, b],
    [a, b],
    [a, b],
    [b, a],
    [b, a],
    [b, a],
]

assert np.isclose(distance(data1, data2), expected).all()

df1 = pd.DataFrame(data1, index=range(10, 70, 10), columns=[*'xyz'])
df2 = pd.DataFrame(data2, index=[100, 500], columns=[*'xyz'])
df_expected = pd.DataFrame(expected, df1.index, df2.index)

assert distance(df1, df2).equals(df_expected)

print(distance(df1, df2))
         100       500
10  1.000000  1.414214
20  1.000000  1.414214
30  1.000000  1.414214
40  1.414214  1.000000
50  1.414214  1.000000
60  1.414214  1.000000
→ Ссылка