ペアワイズマハラノビス距離


18

共変量のn×p行列の観測の各ペア間のRのサンプルマハラノビス距離を計算する必要があります。効率的な解決策が必要です。つまり、n(n1)/2距離のみが計算され、C / RCpp / Fortranなどで実装することが望ましいです。母共分散行列Σは未知であり、サンプル共分散を使用すると仮定しますその場所のマトリックス。

Rのペアワイズマハラノビス距離を計算するための「コンセンサス」方法がないように思われるため、この質問に特に興味distがありcluster::daisyます。つまり、関数にも関数にも実装されていません。このmahalanobis関数は、プログラマーの追加作業なしにペアワイズ距離を計算しません。

これはすでにここでRのペアワイズマハラノビス距離を求められましたが、そこでの解は間違っているようです。

これは正しいですが、ひどく非効率です(n×n距離が計算されるため):

set.seed(0)
x0 <- MASS::mvrnorm(33,1:10,diag(c(seq(1,1/2,l=10)),10))
dM = as.dist(apply(x0, 1, function(i) mahalanobis(x0, i, cov = cov(x0))))

これはCで自分でコーディングするのに十分簡単ですが、この基本的なものには既存のソリューションがあるはずです。あるの?

不足している他のソリューションがあります:n n 1 / 2個の一意の距離のみが必要な場合、n × n距離をHDMD::pairwise.mahalanobis()計算します。有望に思えますが、に依存するパッケージから関数を取得したくないので、コードを実行する他の人の能力が大幅に制限されます。この実装が完全でない限り、自分で作成したいです。この機能の経験はありますか?n×nn(n1)/2compositions::MahalanobisDist()rgl


ようこそ。あなたの質問に距離の2つの行列を印刷できますか?そして、あなたにとって「非効率」とは何ですか?
ttnphns

1
サンプル共分散行列のみを使用していますか?その場合、これは1)Xをセンタリングすることと同等です 2)中央のXのSVDを計算します(UDV 'など)。3)Uの行間のペアワイズ距離の計算
vqv 14年

これを質問として投稿していただきありがとうございます。あなたの式は正しくないと思います。以下の私の答えをご覧ください。
user603

@vqvはい、サンプル共分散行列。これを反映するために元の投稿が編集されます。
ahfoss 14年

同様の質問stats.stackexchange.com/q/33518/3277も参照してください。
ttnphns

回答:


21

ahfossの「succint」ソリューションから始めて、SVDの代わりにコレスキー分解を使用しました。

cholMaha <- function(X) {
 dec <- chol( cov(X) )
 tmp <- forwardsolve(t(dec), t(X) )
 dist(t(tmp))
}

三角システムの順方向解法は、逆共分散を使用した密行列乗算よりも高速であるため、高速になります(こちらを参照)。以下は、いくつかの設定でのahfossおよびwhuberのソリューションのベンチマークです。

 require(microbenchmark)
 set.seed(26565)
 N <- 100
 d <- 10

 X <- matrix(rnorm(N*d), N, d)

 A <- cholMaha( X = X ) 
 A1 <- fastPwMahal(x1 = X, invCovMat = solve(cov(X))) 
 sum(abs(A - A1)) 
 # [1] 5.973666e-12  Ressuring!

   microbenchmark(cholMaha(X),
                  fastPwMahal(x1 = X, invCovMat = solve(cov(X))),
                  mahal(x = X))
Unit: microseconds
expr          min       lq   median       uq      max neval
cholMaha    502.368 508.3750 512.3210 516.8960  542.806   100
fastPwMahal 634.439 640.7235 645.8575 651.3745 1469.112   100
mahal       839.772 850.4580 857.4405 871.0260 1856.032   100

 N <- 10
 d <- 5
 X <- matrix(rnorm(N*d), N, d)

   microbenchmark(cholMaha(X),
                  fastPwMahal(x1 = X, invCovMat = solve(cov(X))),
                  mahal(x = X)
                    )
Unit: microseconds
expr          min       lq    median       uq      max neval
cholMaha    112.235 116.9845 119.114 122.3970  169.924   100
fastPwMahal 195.415 201.5620 205.124 208.3365 1273.486   100
mahal       163.149 169.3650 172.927 175.9650  311.422   100

 N <- 500
 d <- 15
 X <- matrix(rnorm(N*d), N, d)

   microbenchmark(cholMaha(X),
                  fastPwMahal(x1 = X, invCovMat = solve(cov(X))),
                  mahal(x = X)
                    )
Unit: milliseconds
expr          min       lq     median       uq      max neval
cholMaha    14.58551 14.62484 14.74804 14.92414 41.70873   100
fastPwMahal 14.79692 14.91129 14.96545 15.19139 15.84825   100
mahal       12.65825 14.11171 39.43599 40.26598 41.77186   100

 N <- 500
 d <- 5
 X <- matrix(rnorm(N*d), N, d)

   microbenchmark(cholMaha(X),
                  fastPwMahal(x1 = X, invCovMat = solve(cov(X))),
                  mahal(x = X)
                    )
Unit: milliseconds
expr           min        lq      median        uq       max neval
cholMaha     5.007198  5.030110  5.115941  5.257862  6.031427   100
fastPwMahal  5.082696  5.143914  5.245919  5.457050  6.232565   100
mahal        10.312487 12.215657 37.094138 37.986501 40.153222   100

そのため、コレスキーは一様に高速であるようです。


3
+1よくやった!このソリューションの方が速い理由の説明に感謝します。
whuber

maha()は、ポイントまでの距離ではなく、ペアワイズ距離行列をどのように提供しますか?
sheß

1
あなたは正しい、そうではないので、私の編集は完全に関連していません。削除しますが、いつかはパッケージにmaha()のペアワイズバージョンを追加するかもしれません。これを指摘してくれてありがとう。
マッテオ・ファシオーロ

1
それは素敵だ!楽しみにしています。
sheß

9

2つのデータポイント間の2乗マハラノビス距離の標準式は次のとおりです。

D12=バツ1バツ2TΣ1バツ1バツ2

ここで、は観測値iに対応するp × 1ベクトルです。通常、共分散行列は観測データから推定されます。行列の反転をカウントせず、この操作にはp 2 + pの乗算とp 2 + 2 pの加算が必要で、それぞれn n 1 / 2回繰り返されます。バツp×1p2+pp2+2pnn1/2

次の派生を考慮してください。

D12=バツ1バツ2TΣ1バツ1バツ2=バツ1バツ2TΣ12Σ12バツ1バツ2=バツ1TΣ12バツ2TΣ12Σ12バツ1Σ12バツ2=q1Tq2Tq1q2

ここで、。なお、XTIΣ-1q=Σ12バツ。これは、という事実に依存しているΣ-1バツTΣ12=Σ12バツT=qTは対称です。これは、任意の対称対角化可能マトリックスA=PEPTΣ12A=PEPTに対して、

A12T=PE12PTT=PTTE12TPT=PE12PT=A12

私たちは聞かせている場合、及びそのノートΣ - 1は、左右対称であるが、我々はそのことがわかりΣ - 1A=Σ1Σ1Σ12 must also be symmetric. If バツ is the n×p matrix of observations and Q is the n×p matrix such that the th row of Q is q, then Q can be succinctly expressed as バツΣ12. This and the previous results imply that

計算された操作のみ N N - 1 / 2倍である Pの乗算と 2つのp(とは対照的に追加 P 2 + P P N 2 + P 2 N の代わりに、元の O P 2

Dk==1pQkQ2
nn1/2p2pp2+p multiplications and p2+2p additions in the above method), resulting in an algorithm that is of computational complexity order Opn2+p2nO(p2n2).
require(ICSNP) # for pair.diff(), C implementation

fastPwMahal = function(data) {

    # Calculate inverse square root matrix
    invCov = solve(cov(data))
    svds = svd(invCov)
    invCovSqr = svds$u %*% diag(sqrt(svds$d)) %*% t(svds$u)

    Q = data %*% invCovSqr

    # Calculate distances
    # pair.diff() calculates the n(n-1)/2 element-by-element
    # pairwise differences between each row of the input matrix
    sqrDiffs = pair.diff(Q)^2
    distVec = rowSums(sqrDiffs)

    # Create dist object without creating a n x n matrix
    attr(distVec, "Size") = nrow(data)
    attr(distVec, "Diag") = F
    attr(distVec, "Upper") = F
    class(distVec) = "dist"
    return(distVec)
}

Interesting. Sorry, I don't know R. Can you expain what pair.diff() does and also give a numeric example with printouts of every step of your function? Thanks.
ttnphns

I edited the answer to include the derivation justifying these calculations, but I also posted a second answer containing code that is much more concise.
ahfoss

7

Let's try the obvious. From

Dij=(xixj)Σ1(xixj)=xiΣ1xi+xjΣ1xj2xiΣ1xj

it follows we can compute the vector

ui=xiΣ1xi

O(p2)

V=XΣ1X

O(pn2+p2n)

D=uu2V

+(ab)ij=ai+bj.

An R implementation succinctly parallels the mathematical formulation (and assumes, with it, that Σ=Var(X) actually is invertible with inverse written h here):

mahal <- function(x, h=solve(var(x))) {
  u <- apply(x, 1, function(y) y %*% h %*% y)
  d <- outer(u, u, `+`) - 2 * x %*% h %*% t(x)
  d[lower.tri(d)]
}

Note, for compability with the other solutions, that only the unique off-diagonal elements are returned, rather than the entire (symmetric, zero-on-the-diagonal) squared distance matrix. Scatterplots show its results agree with those of fastPwMahal.

In C or C++, RAM can be re-used and uu computed on the fly, obviating any need for intermediate storage of uu.

Timing studies with n ranging from 33 through 5000 and p ranging from 10 to 100 indicate this implementation is 1.5 to 5 times faster than fastPwMahal within that range. The improvement gets better as p and n increase. Consequently, we can expect fastPwMahal to be superior for smaller p. The break-even occurs around p=7 for n100. Whether the same computational advantages of this straightforward solution pertain in other implementations may be a matter of how well they take advantage of vectorized array operations.


Looks good. I assume it could be made even more rapid by only calculating the lower diagonals, although I can't off-hand think of a way to do this in R without losing the speedy performance of apply and outer... except for breaking out Rcpp.
ahfoss

apply/outer have no speed advantage over plain-vanilla loops.
user603

@user603 I understand that in principle--but do the timing. Moreover, the main point of using these constructs is to provide semantic help for parallelizing the algorithm: the difference in how they express it is important. (It may be worth recalling the original question seeks C/Fortran/etc. implementations.) Ahfoss, I thought about limiting the calculation to the lower triangle too and agree that in R there seems to be nothing to gain by that.
whuber

5

If you wish to compute the sample Mahalanobis distance, then there are some algebraic tricks that you can exploit. They all lead to computing pairwise Euclidean distances, so let's assume we can use dist() for that. Let X denote the n×p data matrix, which we assume to be centered so that its columns have mean 0 and to have rank p so that the sample covariance matrix is nonsingular. (Centering requires O(np) operations.) Then the sample covariance matrix is

S=XTX/n.

The pairwise sample Mahalanobis distances of X is the same as the pairwise Euclidean distances of

XL
for any matrix L satisfying LLT=S1, e.g. the square root or Cholesky factor. This follows from some linear algebra and it leads to an algorithm requiring the computation of S, S1, and a Cholesky decomposition. The worst case complexity is O(np2+p3).

More deeply, these distances relate to distances between the sample principal components of X. Let X=UDVT denote the SVD of X. Then

S=VD2VT/n
and
S1/2=VD1VTn1/2.
So
XS1/2=UVTn1/2
and the sample Mahalanobis distances are just the pairwise Euclidean distances of U scaled by a factor of n, because Euclidean distance is rotation invariant. This leads to an algorithm requiring the computation of the SVD of X which has worst case complexity O(np2) when n>p.

Here is an R implementation of the second method which I cannot test on the iPad I am using to write this answer.

u = svd(scale(x, center = TRUE, scale = FALSE), nv = 0)$u
dist(u)
# these distances need to be scaled by a factor of n

2

This is a much more succinct solution. It is still based on the derivation involving the inverse square root covariance matrix (see my other answer to this question), but only uses base R and the stats package. It seems to be slightly faster (about 10% faster in some benchmarks I have run). Note that it returns Mahalanobis distance, as opposed to squared Maha distance.

fastPwMahal = function(x1,invCovMat) {
  SQRT = with(svd(invCovMat), u %*% diag(d^0.5) %*% t(v))
  dist(x1 %*% SQRT)
}

This function requires an inverse covariance matrix, and doesn't return a distance object -- but I suspect that this stripped-down version of the function will be more generally useful to stack exchange users.


3
This could be improved by replacing SQRT with the Cholesky decomposition chol(invCovMat).
vqv

1

I had a similar problem solved by writing a Fortran95 subroutine. As you do, I didn't want to calculate the duplicates among the n2 distances. Compiled Fortran95 is nearly as convenient with basic matrix calculations as R or Matlab, but much faster with loops. The routines for Cholesky decompositions and triangle substitutions can be used from LAPACK.

If you only use the Fortran77-features in the interface, your subroutine is still portable enough for others.


1

There a very easy way to do it using R Package "biotools". In this case you will get a Squared Distance Mahalanobis Matrix.

#Manly (2004, p.65-66)

x1 <- c(131.37, 132.37, 134.47, 135.50, 136.17)
x2 <- c(133.60, 132.70, 133.80, 132.30, 130.33)
x3 <- c(99.17, 99.07, 96.03, 94.53, 93.50)
x4 <- c(50.53, 50.23, 50.57, 51.97, 51.37)

#size (n x p) #Means 
x <- cbind(x1, x2, x3, x4) 

#size (p x p) #Variances and Covariances
Cov <- matrix(c(21.112,0.038,0.078,2.01, 0.038,23.486,5.2,2.844, 
        0.078,5.2,24.18,1.134, 2.01,2.844,1.134,10.154), 4, 4)

library(biotools)
Mahalanobis_Distance<-D2.dist(x, Cov)
print(Mahalanobis_Distance)

Can you please explain me what a squared distance matrix means? Respectively: I'm interested in the distance between two points/vectors so what does a matrix tell?
Ben

1

これは、私の古い答えが別のスレッドからここに移動したコードで拡張されています

私は長年にわたり、線形方程式系の解法を使用したハットマトリックスアプローチを介して、SPSSのペアワイズマハラノビス距離の正方対称マトリックスの計算を行ってきました(共分散マトリックスの反転よりも高速であるため)。

私はRユーザーではないので、SPSSで@ahfossのこのレシピを「私の」レシピと一緒に、変数が1000個、変数が400個のデータで再現しようとしましたが、かなり速くなりました。


ペアワイズマハラノビス距離の完全な行列を計算するより高速な方法は、ハット行列を使用することです H。つまり、非常に高速な行列乗算と反転関数が組み込まれた高水準言語(Rなど)を使用している場合、ループはまったく必要なく、ケースワイズループを実行するよりも高速になります。

定義。2乗ペアワイズマハラノビス距離の二重中心行列は、Hn1、ここで帽子行列は バツバツバツ1バツ、列中心のデータから計算 バツ

したがって、データ行列の列を中央に配置し、ハット行列を計算し、(n-1)を掛けて、二重センタリングと逆の操作を実行します。平方マハラノビス距離の行列を取得します。

「ダブルセンタリング」とは、距離2乗(ユークリッドやマハラノビスなど)を、データクラウドの幾何学的重心から定義されたスカラー積に幾何学的に正しく変換することです。この操作は、コサイン定理に暗黙的に基づいています。多変量データポイト間にユークリッド距離の2乗の行列があるとします。雲の重心(多変量平均)を見つけ、各ペアワイズ距離を対応するスカラー積(ドット積)で置き換えます。距離に基づいていますhリンクに示されているように、重心とそれらのベクトル間の角度。のh2sは、スカラー積の行列の対角線上にあり、 h1h2cos are the off-diagonal entries. Then, using directly the cosine theorem formula you easily convert the "double-centrate" matrix back into the squared distance matrix.

In our settings, the "double-centrate" matrix is specifically the hat matrix (multiplied by n-1), not euclidean scalar products, and the resultant squared distance matrix is thus the squared Mahalanobis distance matrix, not squared euclidean distance matrix.

In matrix notation: Let H be the diagonal of H(n1), a column vector. Propagate the column into the square matrix: H= {H,H,...}; then Dmahal2=H+H2H(n1).

SPSSとスピードプローブのコードは次のとおりです。


この最初のコードfastPwMahalは、引用された回答の @ahfoss関数に対応しています。数学的には同等です。しかし、@ ahfossは対称行列の三角形を(要素ごとに)計算しながら、距離の完全な対称行列を計算しています(行列演算を使用)。

matrix. /*Matrix session in SPSS;
        /*note: * operator means matrix multiplication, &* means usual, elementwise multiplication.
get data. /*Dataset 1000 cases x 400 variables
!cov(data%cov). /*compute usual covariances between variables [this is my own matrix function].
comp icov= inv(cov). /*invert it
call svd(icov,u,s,v). /*svd
comp isqrcov= u*sqrt(s)*t(v). /*COV^(-1/2)
comp Q= data*isqrcov. /*Matrix Q (see ahfoss answer)
!seuclid(Q%m). /*Compute 1000x1000 matrix of squared euclidean distances;
               /*computed here from Q "data" they are the squared Mahalanobis distances.
/*print m. /*Done, print
end matrix.

Time elapsed: 3.25 sec

以下は、高速化するための私の変更です。

matrix.
get data.
!cov(data%cov).
/*comp icov= inv(cov). /*Don't invert.
call eigen(cov,v,s2). /*Do sdv or eigen decomposition (eigen is faster),
/*comp isqrcov= v * mdiag(1/sqrt(s2)) * t(v). /*compute 1/sqrt of the eigenvalues, and compose the matrix back, so we have COV^(-1/2).
comp isqrcov= v &* (make(nrow(cov),1,1) * t(1/sqrt(s2))) * t(v). /*Or this way not doing matrix multiplication on a diagonal matrix: a bit faster .
comp Q= data*isqrcov.
!seuclid(Q%m).
/*print m.
end matrix.

Time elapsed: 2.40 sec

最後に、「帽子行列アプローチ」。速度を上げるために、ハットマトリックスを計算しています(データは最初に中央に配置する必要があります)バツバツバツ1バツ 一般化された逆経由 バツバツ1バツ線形システムソルバーで得られsolve(X'X,X')ます。

matrix.
get data.
!center(data%data). /*Center variables (columns).
comp hat= data*solve(sscp(data),t(data))*(nrow(data)-1). /*hat matrix, and multiply it by n-1 (i.e. by df of covariances).
comp ss= diag(hat)*make(1,ncol(hat),1). /*Now using its diagonal, the leverages (as column propagated into matrix).
comp m= ss+t(ss)-2*hat. /*compute matrix of squared Mahalanobis distances via "cosine rule".
/*print m.
end matrix.

[Notice that if in "comp ss" and "comp m" lines you use "sscp(t(data))",
 that is, DATA*t(DATA), in place of "hat", you get usual sq. 
 euclidean distances]

Time elapsed: 0.95 sec

0

The formula you have posted is not computing what you think you are computing (a U-statistics).

In the code I posted, I use cov(x1) as scaling matrix (this is the variance of the pairwise differences of the data). You are using cov(x0) (this is the covariance matrix of your original data). I think this is a mistake in your part. The whole point of using the pairwise differences is that it relieves you from the assumption that the multivariate distribution of your data is symmetric around a centre of symmetry (or to have to estimate that centre of symmetry for that matter, since crossprod(x1) is proportional to cov(x1)). Obviously, by using cov(x0) you lose that.

This is well explained in the paper I linked to in my original answer.


1
ここで2つの異なることについて話していると思います。私の方法はマハラノビス距離を計算しますが、これは他のいくつかの式に対して検証しました。私の式は、このスレッドでMatteo Fasioloand(私が仮定すると)によって独立して検証されましたwhuber。あなたのものは違います。私はあなたが計算しているものを理解することに興味がありますが、通常定義されているマハラノビス距離とは明らかに異なります。
ahfoss

@ahfoss:1)マハラノビスは、メトリックの対称点までのXの距離です。あなたの場合、Xはペアワイズ差のan *(n-1)/ 2行列であり、対称中心はベクトル0_pであり、メトリックはコードでcov(X1)と呼ばれています。2)そもそもU統計を使用する理由を自問してください。論文で説明されているように、cov(x0)を使用するとその目的が無効になることがわかります。
user603 14年

I think this is the disconnect. In my case the X are the rows of the observed data matrix (not distances), and I am interested in calculating the distance of every row to each other row, not the distance to a center. There are at least three "scenarios" in which Mahalanobis distance is used: [1] distance between distributions, [2] distance of observed units from the center of a distribution, and [3] distance between pairs of observed units (what I am referring to). What you describe resembles [2], except that X in your case are the pairwise distances with center Op.
ahfoss

クルーらを見た後1994あなたが引用論文、私はそれに注意しますが、それは、上記の私の記事で、彼らはシナリオ[2]である、外れ値診断の文脈におけるマハラノビス距離を議論明らかであるがcov(x0)、通常、この文脈で使用され、クルーらと一致するように思われますの使用法。この論文では、少なくとも明示的にではなく、U統計については言及していません。彼らは言及するS-、 GS-、 τ-および LQD-推定者、おそらくあなたはこれらのいずれかを参照していますか?
ahfoss
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.