x座標とy座標のnumpy配列で最も近い点のインデックスを見つける


83

2つの2dnumpy配列があります。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

上記のこのコードは、私のコード(100x100行列で5000ポイントを検索)を100倍高速化しました。興味深いことに、使用scipy.spatial.KDTree(代わりのscipy.spatial.cKDTreeは)ので、それはcKDTreeバージョンを使用して、間違いなく価値がある、私の素朴なソリューションに匹敵するタイミングを与えました...


1
推測ですが、おそらくkdツリーが役立つでしょう。Pythonに実装があるかどうかはわかりません。
ジャスティン

リストを作成したり、「ポイント」を転置したりする必要はありません。代わりに配列を使用して、インデックスをラヴェルします。
テオSimier

回答:


48

scipy.spatialkdツリーの実装もありますscipy.spatial.KDTree

アプローチは通常、最初にポイントデータを使用してkdツリーを構築することです。その計算の複雑さは、N log Nのオーダーです。ここで、Nはデータポイントの数です。範囲クエリと最近傍検索は、logNの複雑さで実行できます。これは、単にすべてのポイントを循環するよりもはるかに効率的です(複雑さN)。

したがって、範囲クエリまたは最近傍クエリを繰り返した場合は、kdツリーを強くお勧めします。


1
これは非常に有望に見えます。私はそれについて読み始め、何かが機能するかどうかを確認します...
ピートW

1
私はまだコードをテストしていますが、初期の兆候は、scipy.spatial.cKDTreeの使用が私の素朴なアプローチよりも約100倍速いことです。明日時間が増えたら、最終的なコードを投稿し、おそらくこの回答を受け入れます(それ以前にもっと速い方法が出てこない限り!)。ご協力いただきありがとうございます。
ピートW

OK、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 2017

@lostCrotchetそう思います。私も2つ以上のデータで使用しています。例えば(X、Y、Z、I)
efirvida

5

データを適切な形式にマッサージできる場合は、次の方法を使用するのが手っ取り早い方法ですscipy.spatial.distance

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

特にpdistcdistペアワイズ距離を計算するための高速な方法を提供します。


私はそれをマッサージとも呼んでいます、それは私たちがデータで何をするかをほとんど説明しています。:D
Lorinc Nyitrai 2016年

1
Scipy.spatil.distanceは優れたツールですが、距離が多い場合、cKdtreeはcdistよりもはるかに高速であることに注意してください。
ロスバルティカ2017年

1
私が誤解されていない場合は、cdist()または他のNumpyメソッドの使用がこの回答に示されていますcodereview.stackexchange.com/a/134918/156228
Alex F
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.