web-dev-qa-db-ja.com

Numpyを使用したユークリッド距離行列の効率的な計算

2次元空間に一連のポイントがあり、各ポイントから他の各ポイントまでの距離を計算する必要があります。

ポイントの数は比較的少なく、せいぜい100個です。しかし、これらの移動ポイント間の関係を判断するために頻繁かつ迅速にそれを行う必要があり、ポイントを反復することは同じくらい悪いことを知っているのでO(n ^ 2)の複雑さとして、numpyのマトリックスマジック(またはscipy)を利用する方法を探しています。

私のコードでは、各オブジェクトの座標はそのクラスに格納されています。ただし、クラス座標を更新するときに、numpy配列で更新することもできます。

class Cell(object):
    """Represents one object in the field."""
    def __init__(self,id,x=0,y=0):
        self.m_id = id
        self.m_x = x
        self.m_y = y

重複を防ぐためにユークリッド距離行列を作成することは私にはありますが、おそらくあなたはより巧妙なデータ構造を持っています。

私も気の利いたアルゴリズムへのポインタを開いています。

また、ユークリッド距離とnumpyを扱う同様の質問がありますが、完全な距離行列に効率的に入力するというこの質問に直接対応する質問は見つかりませんでした。

16
Wes Modes

complexタイプを利用できます:

# build a complex array of your cells
z = np.array([complex(c.m_x, c.m_y) for c in cells])

最初の解決策

# mesh this array so that you will have all combinations
m, n = np.meshgrid(z, z)
# get the distance via the norm
out = abs(m-n)

第二の解決策

メッシングが主なアイデアです。ただし、numpyは賢いので、mnを生成する必要はありません。 zの転置バージョンを使用して、差を計算するだけです。メッシュは自動的に行われます:

out = abs(z[..., np.newaxis] - z)

第三の解決策

zが2次元配列として直接設定されている場合、奇妙なz.Tの代わりにz[..., np.newaxis]を使用できます。最後に、コードは次のようになります。

z = np.array([[complex(c.m_x, c.m_y) for c in cells]]) # notice the [[ ... ]]
out = abs(z.T-z)

>>> z = np.array([[0.+0.j, 2.+1.j, -1.+4.j]])
>>> abs(z.T-z)
array([[ 0.        ,  2.23606798,  4.12310563],
       [ 2.23606798,  0.        ,  4.24264069],
       [ 4.12310563,  4.24264069,  0.        ]])

補足として、後で上三角形を取り、重複を削除することもできます。

>>> np.triu(out)
array([[ 0.        ,  2.23606798,  4.12310563],
       [ 0.        ,  0.        ,  4.24264069],
       [ 0.        ,  0.        ,  0.        ]])

いくつかのベンチマーク

>>> timeit.timeit('abs(z.T-z)', setup='import numpy as np;z = np.array([[0.+0.j, 2.+1.j, -1.+4.j]])')
4.645645342274779
>>> timeit.timeit('abs(z[..., np.newaxis] - z)', setup='import numpy as np;z = np.array([0.+0.j, 2.+1.j, -1.+4.j])')
5.049334864854522
>>> timeit.timeit('m, n = np.meshgrid(z, z); abs(m-n)', setup='import numpy as np;z = np.array([0.+0.j, 2.+1.j, -1.+4.j])')
22.489568296184686
28
Kiwi

Numpyを使用してこれを行う方法は次のとおりです。

import numpy as np

x = np.array([0,1,2])
y = np.array([2,4,6])

# take advantage of broadcasting, to make a 2dim array of diffs
dx = x[..., np.newaxis] - x[np.newaxis, ...]
dy = y[..., np.newaxis] - y[np.newaxis, ...]
dx
=> array([[ 0, -1, -2],
          [ 1,  0, -1],
          [ 2,  1,  0]])

# stack in one array, to speed up calculations
d = np.array([dx,dy])
d.shape
=> (2, 3, 3)

これで、0軸に沿ってL2ノルムを計算するだけになりました( here )。

(d**2).sum(axis=0)**0.5
=> array([[ 0.        ,  2.23606798,  4.47213595],
          [ 2.23606798,  0.        ,  2.23606798],
          [ 4.47213595,  2.23606798,  0.        ]])
7
shx2

完全な距離行列が必要ない場合は、kd-treeを使用することをお勧めします。 scipy.spatial.cKDTreeまたはsklearn.neighbors.KDTree。これは、kdツリーkanがO(n log n)時間でkに最も近い近傍を検出するため、すべてのn x n距離を計算するO(n ** 2)の複雑さを回避するためです。

5
Sturla Molden

Jake Vanderplasは、Python Data Science Handbookでのブロードキャストを使用してこの例を提供します。これは@ shx2が提案したものと非常に似ています。

import numpy as np
Rand = random.RandomState(42)
X = Rand.rand(3, 2)  
dist_sq = np.sum((X[:, np.newaxis, :] - X[np.newaxis, :, :]) ** 2, axis = -1)

dist_sq
array([[0.        , 0.18543317, 0.81602495],
       [0.18543317, 0.        , 0.22819282],
       [0.81602495, 0.22819282, 0.        ]])
3
Rich Pauloo