成对的马氏距离


18

我需要在协变量的矩阵中,计算每对观测值之间R中的样本马氏距离。我需要一个有效的解决方案,即仅计算距离,并最好在C / RCpp / Fortran等中实现。我假设总体协方差矩阵不知道,并使用样本协方差矩阵。Ñ ñ - 1 / 2 Σñ×pññ-1个/2Σ

我对这个问题特别感兴趣,因为似乎没有用于计算R中成对的马氏距离的“共识”方法,即,dist既未在函数中也未在cluster::daisy函数中实现。mahalanobis没有程序员的额外工作,该函数不会计算成对距离。

这里已经问过R中的成对马氏距离,但那里的解决方案似乎不正确。

这是一种正确但效率极低的方法(因为计算了距离):ñ×ñ

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语言编写自己的代码,但是我觉得这个基本的东西应该有一个预先存在的解决方案。有一个吗?

还有其他一些不足的解决方案:当仅需要唯一距离时,HDMD::pairwise.mahalanobis()计算距离。似乎很有希望,但是我不希望我的功能来自依赖于的程序包,这严重限制了其他人运行我的代码的能力。除非该实现是完美的,否则我宁愿自己编写。有人对此功能有经验吗?n n 1 / 2ñ×ñññ-1个/2compositions::MahalanobisDist()rgl


欢迎。您可以打印问题中距离的两个矩阵吗?对您来说,“低效率”是什么?
ttnphns

1
您仅使用样本协方差矩阵吗?如果是这样,则相当于1)以X为中心;2)计算居中X的SVD,例如UDV';3)计算U的行之间的成对距离
vqv

感谢您将其发布为问题。我认为您的公式不正确。请参阅下面的答案。
user603 2014年

@vqv是,样本协方差矩阵。编辑原始帖子以反映这一点。
ahfoss 2014年

另请参阅非常类似的问题stats.stackexchange.com/q/33518/3277
ttnphns 2015年

Answers:


21

从ahfoss的“简洁”解决方案开始,我用Cholesky分解代替了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()添加到该软件包中。感谢您指出了这一点。
Matteo Fasiolo 2016年

1
那太可爱了!期待它。
sheß

9

两个数据点之间的Mahalanobis距离平方的标准公式为

d12=X1个-X2ŤΣ-1个X1个-X2

其中是与观测i对应的p × 1向量。通常,协方差矩阵是从观察到的数据估计的。不计算矩阵求逆,此操作需要p 2 + p乘法和p 2 + 2 p加法,每个重复n n 1 / 2次。X一世p×1个一世p2+pp2+2pññ-1个/2

考虑以下推导:

D12=(x1x2)TΣ1(x1x2)=(x1x2)TΣ12Σ12(x1x2)=(x1TΣ12x2TΣ12)(Σ12x1Σ12x2)=(q1Tq2T)(q1q2)

其中。需要注意的是X牛逼Σ-1qi=Σ12xi。这依赖于一个事实,即Σ-1xiTΣ12=(Σ12xi)T=qiT是对称的,这是由于以下事实成立:对于任何对称的对角化矩阵A=PEPTΣ12A=PEPT

A12T=(PE12PT)T=PTTE12TPT=PE12PT=A12

如果我们让,并注意Σ - 1是对称的,我们看到的是Σ - 1A=Σ1Σ1也必须对称。如果XÑ×p的观测矩阵和QÑ×p矩阵,使得ħ的行Qq,则Q可以简洁地表述为XΣ-1Σ12Xn×pQn×pithQqiQ。这个和以前的结果暗示XΣ-1个2

唯一可以计算 n n 1 / 2次的运算是 p乘法和 2 p加法(与 p 2 + p乘法和 p 2 + 2 p相反

dķ=一世=1个pķ一世-一世2
ññ-1个/2p2pp2+pp2+2p在上述方法中添加),导致算法的计算复杂度为而不是原始O p 2 n 2Øpñ2+p2ñØp2ñ2
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)
}

有趣。抱歉,我不认识R。您能解释一下pair.diff()它的作用,并给出一个数字示例,其中包含函数各步的打印输出吗?谢谢。
ttnphns

我编辑了答案,以包括证明这些计算合理的推导,但我还发布了第二个答案,其中包含更加简洁的代码。
ahfoss

7

让我们尝试显而易见的方法。从

DiĴ=X一世-XĴΣ-1个X一世-XĴ=X一世Σ-1个X一世+XĴΣ-1个XĴ-2X一世Σ-1个XĴ

因此我们可以计算向量

ü一世=X一世Σ-1个X一世

时间和矩阵中Øp2

V=XΣ-1个X

时间中,最有可能使用内置的快速(可并行化)数组操作,然后将解决方案形成为Øpñ2+p2ñ

d=üü-2V

其中是相对于该外积+b Ĵ = 一个 + b Ĵ+一种b一世Ĵ=一种一世+bĴ

一个R实现简洁地与数学公式平行(并假定实际上是可逆的,此处用反写h表示):Σ=VarXH

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)]
}

请注意,为了与其他解决方案兼容,仅返回唯一的非对角线元素,而不是整个(对称,对角线为零)平方距离矩阵。散点图显示其结果与的结果一致fastPwMahal

在C或C ++,可重新使用的RAM和计算的飞行,从而避免需要任何的中间存储ü üüüüü

范围从335000p的范围从10100的时序研究表明,此实现比该范围内的速度快1.55倍。随着pn的增加,改进会变得更好。因此,对于较小的p,我们可以预期会更好。盈亏平衡发生围绕p = 7Ñ 100ñ335000p101001.55fastPwMahalpñfastPwMahalpp=7ñ100。此直接解决方案的相同计算优势是否适用于其他实现方式,可能取决于它们如何充分利用矢量化数组操作。


看起来不错。我认为它可以更快速的通过仅计算下对角线虽然我不能离手想办法做到这一点R中不失的高速性能进行,applyouter...除了打破了Rcpp
ahfoss 2014年

与普通香草环相比,施加/外侧没有速度优势。
user603 2014年

@ user603我原则上理解这一点-但要安排时间。此外,使用这些构造的主要目的是为并行化算法提供语义帮助:它们表达方式的差异很重要。(可能值得回想起最初的问题,它寻求C / Fortran / etc。实现。)Ahfoss,我也考虑过将计算限制在较低的三角形,并同意这样做R似乎没有任何好处。
whuber

5

如果您希望计算样本马氏距离,那么可以利用一些代数技巧。它们都导致计算成对的欧几里得距离,所以假设我们可以使用dist()它。令表示n × p数据矩阵,我们假设它居中,以便其列的均值为0,并且其秩为p,以便样本协方差矩阵为非奇异的。(居中需要O n p 运算。)然后,样本协方差矩阵为S = X T X / n Xñ×ppØñp

小号=XŤX/ñ

的成对样品马氏距离是相同的成对欧几里德距离X 大号用于任何矩阵大号满足大号大号Ť = 小号- 1,例如平方根或乔列斯基因子。这是根据一些线性代数和它导致需要的计算的算法小号小号- 1,和一个Cholesky分解。最坏的情况是O n p 2 + p 3X

X大号
大号大号大号Ť=小号-1个小号小号-1个Øñp2+p3

更深入地讲,这些距离与的样本主成分之间的距离有关。令X = U D V T表示X的SVD 。然后小号= V d 2 V Ť / Ñ小号- 1 / 2 = V d - 1 V Ť Ñ 1 / 2所以X 小号- 1 / 2 = û V Ť Ñ 1XX=üdVŤX

小号=Vd2VŤ/ñ
小号-1个/2=Vd-1个VŤñ1个/2
且样本马氏距离只是U的成对欧几里德距离,缩放比例为
X小号-1个/2=üVŤñ1个/2
ü,因为欧几里得距离是旋转不变的。这导致需要计算X的SVD的算法,当n>p时,X的SVD具有最坏情况的复杂度Onp2ñXØñp2ñ>p

这是第二种方法的R实现,我无法在用于编写此答案的iPad上进行测试。

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

这是一个更为简洁的解决方案。它仍然基于包含平方根逆方差协方差矩阵的推导(请参阅我对该问题的其他回答),但仅使用基数R和stats包。它似乎稍微快一些(在我运行的某些基准测试中,大约快10%)。请注意,它返回的Mahalanobis距离,而不是平方的Maha距离。

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

该函数需要一个逆协方差矩阵,并且不会返回距离对象-但我怀疑该简化后的版本对堆栈交换用户更有用。


3
这可以通过用SQRTCholesky分解代替来改善chol(invCovMat)
vqv 2014年

1

ñ2

如果您仅在界面中使用Fortran77功能,则您的子例程对于其他人而言仍然具有足够的可移植性。


1

使用R包“ biotools”有一种非常简单的方法。在这种情况下,您将获得平方距离马氏矩阵。

#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)

您能解释一下平方距离矩阵是什么意思吗?分别:我对两个点/向量之间的距离感兴趣,那么矩阵能说明什么?

1

这是我以前的回答从另一个线程移到此处的代码的扩展。

我一直在通过使用线性方程组求解的帽子矩阵方法,对SPSS中成对的马氏距离的平方对称矩阵进行长时间的计算(因为它比协方差矩阵的求逆要快)。

我不是R用户,因此我尝试在SPSS 上将“ ah”的配方与“我的”配方一起在1000个案例中的400个变量的数据上重现,并且我发现自己的方法相当快。


H

Hñ-1个XXX-1个XX

因此,将数据矩阵的中心列,计算帽子矩阵,乘以(n-1)并执行与双中心相反的操作。您将获得平方的马氏距离矩阵。

HH2H1个H2cos

在我们的设置中,“双中心”矩阵特别是帽子矩阵(乘以n-1),而不是欧几里德标量积,因此,平方距离矩阵是平方的Mahalanobis距离矩阵,而不是欧几里德距离矩阵。

HHñ-1个H= {H,H,...}d一种H一种2=H+H-2Hñ-1个

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

XXX-1个XXX-1个Xsolve(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

您发布的公式并未计算您认为的计算(U统计量)。

在我发布的代码中,我cov(x1)用作缩放矩阵(这是数据的成对差异的方差)。您正在使用cov(x0)(这是原始数据的协方差矩阵)。我认为这是您的错误。使用成对差异的全部目的是使您摆脱以下假设:数据的多元分布围绕对称中心对称(或者由于crossprod(x1)与成正比,因此必须估算该对称中心cov(x1))。显然,通过使用cov(x0)您失去了它。

我在原始答案中链接的论文对此作了很好的解释。


1
我认为我们在这里谈论的是两件事。我的方法计算马氏距离,我已经根据其他一些公式进行了验证。我的公式现在也已通过Matteo Fasiolo和(我假设)whuber在此线程中独立验证。你的与众不同。我有兴趣了解您所计算的内容,但是它与通常定义的马氏距离明显不同。
ahfoss 2014年

@ahfoss:1)马哈拉诺比斯是X到度量中对称点的距离。在您的情况下,X是成对差分的*(n-1)/ 2矩阵,它们的对称中心是矢量0_p,其度量标准是我在代码中称为cov(X1)。2)问问自己为什么首先要使用U统计量,并且如论文所述,您将看到使用cov(x0)会达到目的。
user603 2014年

XXØp

cov(x0)小号G小号τ大号d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.