web-dev-qa-db-ja.com

距離行列を使用したPandasデータフレームの行間の距離計算

私は次のPandas DataFrame:

In [31]:
import pandas as pd
sample = pd.DataFrame({'Sym1': ['a','a','a','d'],'Sym2':['a','c','b','b'],'Sym3':['a','c','b','d'],'Sym4':['b','b','b','a']},index=['Item1','Item2','Item3','Item4'])
In [32]: print(sample)
Out [32]:
      Sym1 Sym2 Sym3 Sym4
Item1    a    a    a    b
Item2    a    c    c    b
Item3    a    b    b    b
Item4    d    b    d    a

そして、この距離行列に従って各Item間の距離を取得するためのエレガントな方法を見つけたいと思います。

In [34]:
DistMatrix = pd.DataFrame({'a': [0,0,0.67,1.34],'b':[0,0,0,0.67],'c':[0.67,0,0,0],'d':[1.34,0.67,0,0]},index=['a','b','c','d'])
print(DistMatrix)
Out[34]:
      a     b     c     d
a  0.00  0.00  0.67  1.34
b  0.00  0.00  0.00  0.67
c  0.67  0.00  0.00  0.00
d  1.34  0.67  0.00  0.00 

たとえば、Item1Item2と比較すると、aaab-> accbが比較されます。距離行列を使用すると、これは0+0.67+0.67+0=1.34になります。

理想的な出力:

       Item1   Item2  Item3  Item4
Item1      0    1.34     0    2.68
Item2     1.34    0      0    1.34
Item3      0      0      0    2.01
Item4     2.68  1.34   2.01    0
11
cmiller8

これは必要な2倍の作業を実行しますが、技術的には非対称距離行列に対しても機能します(それが意味するものは何でも)

pd.DataFrame ( { idx1: { idx2:sum( DistMatrix[ x ][ y ]
                                  for (x, y) in Zip( row1, row2 ) ) 
                         for (idx2, row2) in sample.iterrows( ) } 
                 for (idx1, row1 ) in sample.iterrows( ) } )

分割して書くことで、より読みやすくすることができます。

# a helper function to compute distance of two items
dist = lambda xs, ys: sum( DistMatrix[ x ][ y ] for ( x, y ) in Zip( xs, ys ) )

# a second helper function to compute distances from a given item
xdist = lambda x: { idx: dist( x, y ) for (idx, y) in sample.iterrows( ) }

# the pairwise distance matrix
pd.DataFrame( { idx: xdist( x ) for ( idx, x ) in sample.iterrows( ) } )
2
behzad.nouri

これは古い質問ですが、これを行うScipy関数があります。

from scipy.spatial.distance import pdist, squareform

distances = pdist(sample.values, metric='euclidean')
dist_matrix = squareform(distances)

pdistはNumpy行列で動作し、DataFrame.valuesは、データフレームの基礎となるNumpyNDarray表現です。 metric引数を使用すると、いくつかの組み込み距離メトリックの1つを選択できます。または、任意のバイナリ関数を渡してカスタム距離を使用することもできます。それは非常に強力で、私の経験では非常に高速です。結果は、対角線を含まず(常に0であるため)、距離行列の上三角のみで構成される「フラット」配列です(対称であるため)。次に、squareformは、この平坦化された形式を完全な行列に変換します。

docs には、多くの組み込み距離関数の数学的要約など、より多くの情報があります。

22
shadowtalker

大きなデータの場合、これを行うための高速な方法を見つけました。データがすでにnp.array形式であり、という名前が付けられていると仮定します。

from sklearn.metrics.pairwise import euclidean_distances
dist = euclidean_distances(a, a)

以下は、2つのアプローチに必要な時間を比較するための実験です。

a = np.random.Rand(1000,1000)
import time 
time1 = time.time()
distances = pdist(a, metric='euclidean')
dist_matrix = squareform(distances)
time2 = time.time()
time2 - time1  #0.3639109134674072

time1 = time.time()
dist = euclidean_distances(a, a)
time2 = time.time()
time2-time1  #0.08735871315002441
5
Michelle Owen