web-dev-qa-db-ja.com

numpyでのベクトル化された行列マンハッタン距離

マンハッタン距離行列を作成するために、効率的なベクトル化されたnumpyを実装しようとしています。私は次のようにドット積を使用して効率的なユークリッド距離行列を作成するために使用される構成に精通しています。

A = [[1, 2]   
     [2, 1]]

B = [[1, 1],
     [2, 2],
     [1, 3],
     [1, 4]]

def euclidean_distmtx(X, X):
    f = -2 * np.dot(X, Y.T)
    xsq = np.power(X, 2).sum(axis=1).reshape((-1, 1))
    ysq = np.power(Y, 2).sum(axis=1)
    return np.sqrt(xsq + f + ysq)

私は同様のものを実装したいが、代わりにマンハッタン距離を使用したい。これまでのところ、私は近づきましたが、絶対的な違いを整理しようとして不足しました。マンハッタンの距離は

\sum_i |x_i - y_i| = |x_1 - y_1| + |x_2 - y_2| + ...

絶対関数がまったく適用されず、この等価性が得られないかどうかを検討することでこれを解決しようとしました

\sum_i x_i - y_i = \sum_i x_i - \sum_i y_i

これは私に次のベクトル化を与えます

def manhattan_distmtx(X, Y):
    f = np.dot(X.sum(axis=1).reshape(-1, 1), Y.sum(axis=1).reshape(-1, 1).T)
    return f / Y.sum(axis=1) - Y.sum(axis=1)

私は正しい道だと思いますが、各ベクトル要素間の差の周りの絶対関数を削除せずに値を移動することはできません。おそらく2乗値のnp.sqrtを使用するなどして、絶対値の周りに巧妙なトリックがあると確信していますが、それを実現できないようです。

ここでは要素ごとの乗算が行われていないため、ここではBLASベースの行列乗算を利用できないと思います。ただし、代替手段はほとんどありません。

アプローチ#1

Scipy's cdist を使用できます。これには、オプションのメトリック引数を'cityblock'-

from scipy.spatial.distance import cdist

out = cdist(A, B, metric='cityblock')

アプローチ#2-A

broadcastingを利用することもできますが、より多くのメモリが必要です-

np.abs(A[:,None] - B).sum(-1)

アプローチ#2-B

これは、2つのcolsを持つ入力配列のスライスと合計を使用して、より少ないメモリを使用するように書き直すことができます-

np.abs(A[:,0,None] - B[:,0]) + np.abs(A[:,1,None] - B[:,1])

アプローチ#2-C

broadcasting module -を使用したabsoluteバージョンの移植によるnumexpr計算の高速化

import numexpr as ne
A3D = A[:,None]
out = ne.evaluate('sum(abs(A3D-B),2)')
10
Divakar