X ve y koordinatlarının numpy dizilerinde en yakın noktanın dizinini bulma


83

İki adet 2d uyuşmuş dizim var: x_array, x yönünde konumsal bilgi içerir, y_array y yönündeki konumları içerir.

O halde uzun bir x, y noktaları listem var.

Listedeki her nokta için, o noktaya en yakın olan konumun dizi indeksini (dizilerde belirtilen) bulmam gerekiyor.

Bu soruya dayanarak saf bir şekilde çalışan bir kod ürettim: Numpy dizisindeki en yakın değeri bulun

yani

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

Bunu büyük bir veri kümesi üzerinden yapıyorum ve gerçekten biraz hızlandırmak istiyorum. Herkes bunu optimize edebilir mi?

Teşekkürler.


GÜNCELLEME: @silvado ve @justin (aşağıda) önerilerini takip eden ÇÖZÜM

# 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

Yukarıdaki kod, kodumu (100x100 matrislerde 5000 nokta arayarak) 100 kat hızlandırdı. İlginç bir şekilde scipy.spatial.KDTree kullanmak ( scipy.spatial.cKDTree yerine ) benim saf çözümüme benzer zamanlamalar verdi, bu yüzden kesinlikle cKDTree sürümünü kullanmaya değer ...


1
Sadece bir tahmin ama belki bir kd ağacı yardımcı olabilir. Python'un bir uygulaması olup olmadığını bilmiyorum.
Justin

Bir liste oluşturmaya ve 'noktaları' aktarmaya gerek yok. Bunun yerine bir dizi kullanın ve dizinleri çözün.
Théo Simier

Yanıtlar:


48

scipy.spatialAyrıca kd ağaç uygulaması vardır: scipy.spatial.KDTree.

Yaklaşım genellikle bir kd ağacı oluşturmak için ilk olarak nokta verilerini kullanmaktır. Bunun hesaplama karmaşıklığı N log N mertebesindedir, burada N, veri noktalarının sayısıdır. Aralık sorguları ve en yakın komşu aramaları daha sonra log N karmaşıklığı ile yapılabilir. Bu, tüm noktalardan (karmaşıklık N) geçmekten çok daha etkilidir.

Bu nedenle, aralık veya en yakın komşu sorgularını tekrarladıysanız, bir kd ağacı şiddetle tavsiye edilir.


1
Bu çok umut verici görünüyor. Bunun hakkında okumaya başlayacağım ve işe
Pete W

1
Hala kodumu test ediyorum, ancak erken belirtiler scipy.spatial.cKDTree kullanmanın benim saf yaklaşımımdan yaklaşık 100 kat daha hızlı olduğudur. Yarın daha fazla zamanım olduğunda, son kodumu göndereceğim ve büyük olasılıkla bu cevabı kabul edeceğim (bundan önce daha hızlı bir yöntem gelmedikçe!). Yardımınız için teşekkürler.
Pete W

Tamam, scipy.spatial.cKDTree'yi kullanmak gitmenin yolu gibi görünüyor. Test verilerimle testler, standart scipy.spatial.KDTree'nin saf çözümüme göre çok fazla / herhangi bir gelişme sağlamadığını gösterdi.
Pete W

76

İşte bir scipy.spatial.KDTreeörnek

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
Çalışan (basit) bir örnekle eksiksiz bir yanıt için teşekkür ederiz, takdir edin!
johndodo

@lostCrotchet Sanırım öyle .. Bir çift veriden daha fazlasıyla da kullandım. örneğin (x, y, z, i)
efirvida

5

Verilerinizi doğru biçime aktarabiliyorsanız, hızlı bir şekilde aşağıdaki yöntemleri kullanmaktır scipy.spatial.distance:

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

Özellikle pdistve cdistikili mesafeleri hesaplamanın hızlı yollarını sağlar.


Buna da masaj diyorum, verilerle ne yaptığımızı hemen hemen açıklıyor. : D
Lorinc Nyitrai

1
Scipy.spatil.distance harika bir araçtır, ancak cKdtree'yi hesaplamak için çok fazla mesafeniz varsa, cdist'ten çok daha hızlı olduğunu unutmayın.
Losbaltica

1
Yanlış anlaşılmadıysam, cdist () veya başka bir Numpy yönteminin kullanılması bu yanıtta gösterilir codereview.stackexchange.com/a/134918/156228
Alex F
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.