web-dev-qa-db-ja.com

numpyを使用してペアワイズ相互情報を計算する最適な方法

mx n行列の場合、列のすべてのペアの相互情報を計算する最適な(最速の)方法は何ですか(nxn)?

相互情報 によって、私は意味する:

I(X、Y)= H(X) + H(Y)-H(X、Y)

ここで、H(X)[〜#〜] x [〜#〜]のシャノンエントロピーを指します。

現在、np.histogram2dnp.histogramを使用して、ジョイント(X、Y)と個別(XまたはY )カウント。特定のマトリックスA(たとえば、250000 X 1000のfloatマトリックス)に対して、ネストされたforループを実行しています。

    n = A.shape[1]
    for ix = arange(n)  
        for jx = arange(ix+1,n):
           matMI[ix,jx]= calc_MI(A[:,ix],A[:,jx])

確かにこれを行うにはより良い/より速い方法がなければなりませんか?

余談ですが、配列の列(列ごとまたは行ごとの操作)のマッピング関数も探しましたが、まだ一般的な答えは見つかりませんでした。

Wikiページ の規則に従って、ここに完全な実装を示します。

import numpy as np

def calc_MI(X,Y,bins):

   c_XY = np.histogram2d(X,Y,bins)[0]
   c_X = np.histogram(X,bins)[0]
   c_Y = np.histogram(Y,bins)[0]

   H_X = shan_entropy(c_X)
   H_Y = shan_entropy(c_Y)
   H_XY = shan_entropy(c_XY)

   MI = H_X + H_Y - H_XY
   return MI

def shan_entropy(c):
    c_normalized = c / float(np.sum(c))
    c_normalized = c_normalized[np.nonzero(c_normalized)]
    H = -sum(c_normalized* np.log2(c_normalized))  
    return H

A = np.array([[ 2.0,  140.0,  128.23, -150.5, -5.4  ],
              [ 2.4,  153.11, 130.34, -130.1, -9.5  ],
              [ 1.2,  156.9,  120.11, -110.45,-1.12 ]])

bins = 5 # ?
n = A.shape[1]
matMI = np.zeros((n, n))

for ix in np.arange(n):
    for jx in np.arange(ix+1,n):
        matMI[ix,jx] = calc_MI(A[:,ix], A[:,jx], bins)

ネストされたforループを使用する私の作業バージョンは、妥当な速度で実行しますが、Aのすべての列にcalc_MIを適用するより最適な方法があるかどうかを知りたいペアワイズ相互情報を計算します)?

私も知りたい:

  1. np.arrays(デコレータのように見えるnp.vectorizeのような)の列(または行)を操作する関数をマップする効率的な方法があるかどうか。

  2. この特定の計算(相互情報)のための他の最適な実装があるかどうか?

45
nahsivar

N *(n-1)/ 2ベクトル上の外側のループの高速計算を提案することはできませんが、scipyバージョン0.13または scikitを使用できる場合、calc_MI(x, y, bins)の実装を単純化できます。 -学ぶ

Scipy 0.13では、lambda_引数が scipy.stats.chi2_contingencyに追加されました この引数は、関数によって計算される統計を制御します。 lambda_="log-likelihood"(またはlambda_=0)を使用すると、対数尤度比が返されます。これは、GまたはGとも呼ばれます。2 統計。 2 * nの係数(nは分割表のサンプルの総数)以外、これはis相互情報です。したがって、calc_MIを次のように実装できます。

from scipy.stats import chi2_contingency

def calc_MI(x, y, bins):
    c_xy = np.histogram2d(x, y, bins)[0]
    g, p, dof, expected = chi2_contingency(c_xy, lambda_="log-likelihood")
    mi = 0.5 * g / c_xy.sum()
    return mi

これと実装の唯一の違いは、この実装では2を底とする対数の代わりに自然対数が使用されることです(したがって、「ビット」ではなく「nats」で情報を表現します)。本当にビットを好むなら、単にmiをlog(2)で割ってください。

sklearn(つまりscikit-learn)を持っている(またはインストールできる)場合、 sklearn.metrics.mutual_info_score を使用して、calc_MIを次のように実装できます。

from sklearn.metrics import mutual_info_score

def calc_MI(x, y, bins):
    c_xy = np.histogram2d(x, y, bins)[0]
    mi = mutual_info_score(None, None, contingency=c_xy)
    return mi
47