Найти ближайшего соседа более питоническим способом

A - это точка, а P - список точек. Я хочу найти, какая точка P[i] является ближайшей к A, т.е. я хочу найти P[i_0] с:

i_0 = argmin_i || A - P[i]||^2

Я делаю это так:

import numpy as np

# P is a list of 4 points
P = [np.array([-1, 0, 7, 3]), np.array([5, -2, 8, 1]), np.array([0, 2, -3, 4]), np.array([-9, 11, 3, 4])]
A = np.array([1, 2, 3, 4])
distance = 1000000000     # better would be : +infinity
closest = None

for p in P:
  delta = sum((p - A)**2)
  if delta < distance:
    distance = delta
    closest = p

print closest    # the closest point to A among all the points in P

Это работает, но как сделать это более коротким / более Pythonic способом?

В более общем смысле в Python (и даже без использования Numpy), как найти k_0 такой, что D[k_0] = min D[k] ? т.е. k_0 = argmin_k D[k]

4 ответа

Решение

Более Pythonic способ реализовать тот же алгоритм, который вы используете, это заменить ваш цикл вызовом min с key функция:

closest = min(P, key=lambda p: sum((p - A)**2))

Обратите внимание, что я использую ** для возведения в степень (^ является бинарным оператором xor в Python).

Полностью векторизованный подход в NumPy. Похож на один из @MikeMüller, но использует трансляцию numpy, чтобы избежать лямбда-функций.

С данными примера:

>>> P = [np.array([-1, 0, 7, 3]), np.array([5, -2, 8, 1]), np.array([0, 2, -3, 4]), np.array([-9, 11, 3, 4])]
>>> A = np.array([1, 2, 3, 4])

И делая P двумерный массив:

>>> P = np.asarray(P)
>>> P
array([[-1,  0,  7,  3],
       [ 5, -2,  8,  1],
       [ 0,  2, -3,  4],
       [-9, 11,  3,  4]])

Это может быть вычислено в одной строке, используя numpy:

>>> P[np.argmin(np.sum((P - A)**2, axis=1))]

Обратите внимание, что P - A, с P.shape = (N, 4) а также A.shape = (4,) будет выводить вычитание на все строки P (Pi = Pi - A).

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

Версия NumPy в виде одной строки:

clostest = P[np.argmin(np.apply_along_axis(lambda p: np.sum((p - A) **2), 1, P))]

Использование встроенного min способ для этого:

import math
p1 = [1,2]
plst = [[1,3], [10,10], [5,5]]
res = min(plst, key=lambda x: math.sqrt(pow(p1[0]-x[0], 2) + pow(p1[1]-x[1], 2)))
print res
[1, 3]

Обратите внимание, что я просто использовал простые списки Python.

Другие вопросы по тегам