Знаходження індексу найближчої точки в масивах Numpy з координатами x та y


83

У мене є два двовимірних масиви numpy: x_array містить позиційну інформацію в напрямку x, y_array - положення в напрямку y.

Тоді я маю довгий список точок x, y.

Для кожної точки у списку мені потрібно знайти індекс масиву розташування (зазначеного в масивах), яке є найближчим до цієї точки.

Я наївно створив якийсь код, який працює, виходячи з цього питання: Знайти найближче значення в масиві numpy

тобто

import time
import numpy

def find_index_of_nearest_xy(y_array, x_array, y_point, x_point):
    distance = (y_array-y_point)**2 + (x_array-x_point)**2
    idy,idx = numpy.where(distance==distance.min())
    return idy[0],idx[0]

def do_all(y_array, x_array, points):
    store = []
    for i in xrange(points.shape[1]):
        store.append(find_index_of_nearest_xy(y_array,x_array,points[0,i],points[1,i]))
    return store


# Create some dummy data
y_array = numpy.random.random(10000).reshape(100,100)
x_array = numpy.random.random(10000).reshape(100,100)

points = numpy.random.random(10000).reshape(2,5000)

# Time how long it takes to run
start = time.time()
results = do_all(y_array, x_array, points)
end = time.time()
print 'Completed in: ',end-start

Я роблю це над великим набором даних і дуже хотів би це трохи пришвидшити. Хтось може це оптимізувати?

Дякую.


ОНОВЛЕННЯ: РІШЕННЯ за пропозиціями @silvado та @justin (нижче)

# Shoe-horn existing data for entry into KDTree routines
combined_x_y_arrays = numpy.dstack([y_array.ravel(),x_array.ravel()])[0]
points_list = list(points.transpose())


def do_kdtree(combined_x_y_arrays,points):
    mytree = scipy.spatial.cKDTree(combined_x_y_arrays)
    dist, indexes = mytree.query(points)
    return indexes

start = time.time()
results2 = do_kdtree(combined_x_y_arrays,points_list)
end = time.time()
print 'Completed in: ',end-start

Цей код вище прискорив мій код (шукаючи 5000 точок у матрицях 100x100) у 100 разів. Цікаво, що використання scipy.spatial.KDTree (замість scipy.spatial.cKDTree ) дало порівнянні терміни моєму наївному рішенню, тому, безумовно, варто використовувати версію cKDTree ...


1
Тільки здогадка, але, можливо, допоможе дерево kd. Я не знаю, чи є у Python реалізація.
Джастін

Не потрібно створювати список і переносити "точки". Замість цього використовуйте масив і збирайте індекси.
Théo Simier

Відповіді:


48

scipy.spatialтакож має реалізацію дерева кД: scipy.spatial.KDTree.

Як правило, підхід полягає в тому, щоб спочатку використовувати точкові дані для побудови дерева kd. Складність обчислень становить близько N log N, де N - кількість точок даних. Тоді запити діапазону та пошук найближчих сусідів можуть бути виконані із складністю журналу N. Це набагато ефективніше, ніж просто їзда на велосипеді по всіх точках (складність N).

Таким чином, якщо ви повторюєте запити діапазону або найближчих сусідів, настійно рекомендується дерево kd.


1
Це виглядає дуже перспективно. Я почну читати про це і подивлюсь, чи зможу я щось заробити ...
Піт В,

1
Я все ще тестую свій код, але на початку свідчать, що використання scipy.spatial.cKDTree приблизно в 100 разів швидше, ніж мій наївний підхід. Коли завтра я отримаю більше часу, я опублікую свій остаточний код і, швидше за все, прийму цю відповідь (якщо раніше не з’явиться швидший метод!). Спасибі за вашу допомогу.
Піт Ш

Добре, використання scipy.spatial.cKDTree, здається, це шлях. Тестування з моїми тестовими даними показало, що стандартний scipy.spatial.KDTree не дає значного / жодного покращення порівняно з моїм наївним рішенням.
Піт W

76

Ось scipy.spatial.KDTreeприклад

In [1]: from scipy import spatial

In [2]: import numpy as np

In [3]: A = np.random.random((10,2))*100

In [4]: A
Out[4]:
array([[ 68.83402637,  38.07632221],
       [ 76.84704074,  24.9395109 ],
       [ 16.26715795,  98.52763827],
       [ 70.99411985,  67.31740151],
       [ 71.72452181,  24.13516764],
       [ 17.22707611,  20.65425362],
       [ 43.85122458,  21.50624882],
       [ 76.71987125,  44.95031274],
       [ 63.77341073,  78.87417774],
       [  8.45828909,  30.18426696]])

In [5]: pt = [6, 30]  # <-- the point to find

In [6]: A[spatial.KDTree(A).query(pt)[1]] # <-- the nearest point 
Out[6]: array([  8.45828909,  30.18426696])

#how it works!
In [7]: distance,index = spatial.KDTree(A).query(pt)

In [8]: distance # <-- The distances to the nearest neighbors
Out[8]: 2.4651855048258393

In [9]: index # <-- The locations of the neighbors
Out[9]: 9

#then 
In [10]: A[index]
Out[10]: array([  8.45828909,  30.18426696])

5
Дякуємо за повну відповідь на робочому (простому) прикладі, цінуємо!
johndodo

@lostCrotchet Я думаю, що так .. Я також використовую його з більш ніж парою даних. наприклад (x, y, z, i)
efirvida

5

Якщо ви можете масажувати свої дані у потрібному форматі, швидким способом є використання методів у scipy.spatial.distance:

http://docs.scipy.org/doc/scipy/reference/spatial.distance.html

Зокрема, pdistі cdistзабезпечують швидкі способи обчислення попарних відстаней.


Я це теж називаю масажем, це майже описує те, що ми робимо з даними. : D
Lorinc Nyitrai

1
Scipy.spatil.distance - чудовий інструмент, але майте на увазі, що якщо у вас багато відстаней, обчислення cKdtree набагато швидше, ніж cdist.
Losbaltica,

1
Якщо мене не зрозуміли неправильно, використання cdist () або іншого методу Numpy показано в цій відповіді codereview.stackexchange.com/a/134918/156228
Alex F
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.