22 votos

Distancias de Mahalanobis por pares

Necesito calcular la distancia de Mahalanobis muestral en R entre cada par de observaciones en un n×pn×p matriz de covariables. Necesito una solución que sea eficiente, es decir, sólo n(n1)/2n(n1)/2 se calculan las distancias, y preferiblemente se implementan en C/RCpp/Fortran, etc. Supongo que ΣΣ , la matriz de covarianza de la población, es desconocida y utilizar la matriz de covarianza de la muestra en su lugar.

Me interesa especialmente esta cuestión porque no parece existir un método "consensuado" para calcular por pares distancias de Mahalanobis en R, es decir, no está implementado en el dist ni en la función cluster::daisy función. En mahalanobis no calcula las distancias entre pares sin trabajo adicional por parte del programador.

Esto ya se preguntó aquí Distancia de Mahalanobis por pares en R pero las soluciones parecen incorrectas.

He aquí una solución correcta pero terriblemente ineficaz (ya que n×nn×n se calculan las distancias):

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

Esto es bastante fácil de codificar yo mismo en C, pero siento que algo tan básico debería tener una solución preexistente. ¿Existe alguna?

Hay otras soluciones que se quedan cortas: HDMD::pairwise.mahalanobis() calcula n×nn×n distancias, cuando sólo n(n1)/2n(n1)/2 se requieren distancias únicas. compositions::MahalanobisDist() parece prometedor, pero no quiero que mi función provenga de un paquete que dependa de rgl lo que limita gravemente la capacidad de los demás para ejecutar mi código. A menos que esta implementación sea perfecta, prefiero escribir la mía propia. ¿Alguien tiene experiencia con esta función?

1voto

Horst Grünbusch Puntos 2742

Tuve un problema similar resuelto escribiendo una subrutina Fortran95. Como tú, yo no quería calcular los duplicados entre los n2n2 distancias. El Fortran95 compilado es casi tan cómodo con los cálculos matriciales básicos como R o Matlab, pero mucho más rápido con los bucles. Las rutinas para descomposiciones Cholesky y sustituciones triangulares pueden utilizarse desde LAPACK.

Si sólo utiliza las características de Fortran77 en la interfaz, su subrutina seguirá siendo lo suficientemente portable para los demás.

1voto

Patrick Puntos 183

La fórmula que ha publicado no está calculando lo que usted cree que está calculando (una estadística U).

En el código que he publicado, utilizo cov(x1) como matriz de escala (es la varianza de las diferencias por pares de los datos). Está utilizando cov(x0) (esta es la matriz de covarianza de tus datos originales). Creo que se trata de un error por su parte. El objetivo de utilizar las diferencias por pares es que te libera de la suposición de que la distribución multivariante de tus datos es simétrica en torno a un centro de simetría (o de tener que estimar ese centro de simetría, ya que crossprod(x1) es proporcional a cov(x1) ). Obviamente, al utilizar cov(x0) lo pierdes.

Esto está bien explicado en el documento al que enlazaba en mi respuesta original.

1voto

divibisan Puntos 101

Hay una manera muy fácil de hacerlo utilizando el paquete R "biotools". En este caso, obtendrá una matriz de Mahalanobis de distancia al cuadrado.

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

1voto

Uri Puntos 111

Este es el ampliado con el código de mi antigua respuesta se trasladó aquí desde otro hilo .

He estado haciendo durante mucho tiempo el cálculo de una matriz simétrica cuadrada de distancias de Mahalanobis por pares en SPSS a través de un nfoque matricial mediante la resolución de un sistema de ecuaciones lineales (ya que es más rápido que invertir la matriz de covarianza).

No soy usuario de R así que sólo he intentado reproducir el de @ahfoss esta receta aquí en SPSS junto con "mi" receta, sobre unos datos de 1000 casos por 400 variables, y he encontrado mi camino considerablemente más rápido.


Una forma más rápida de calcular la matriz completa de distancias de Mahalanobis por pares es mediante matriz de sombreros HH . Es decir, si utiliza un lenguaje de alto nivel (como R) con funciones de multiplicación e inversión de matrices bastante rápidas incorporadas, no necesitará bucles en absoluto, y será más rápido que hacer bucles casewise.

Definición . En doblemente centrado matriz de distancias de Mahalanobis al cuadrado por pares es igual a H(n1)H(n1) donde la matriz de sombreros es X(XX)1X calculado a partir de datos centrados en columnas X .

Por lo tanto, centre las columnas de la matriz de datos, calcule la matriz de sombreros, multiplique por (n-1) y realice la operación opuesta al doble centrado. Se obtiene la matriz de distancias de Mahalanobis al cuadrado.

El "doble centrado" es la conversión geométricamente correcta de distancias al cuadrado (como Euclides y Mahalanobis) en productos escalares definido a partir de la geometría centroide de la nube de datos. Esta operación se basa implícitamente en la teorema del coseno . Imagina que tienes una matriz de distancias euclidianas al cuadrado entre tus puntos de datos multivariantes. Encuentre el centroide (media multivariante) de la nube y sustituya cada distancia entre pares por el correspondiente producto escalar (producto punto), basado en las distancias h s al centroide y el ángulo entre esos vectores, como se muestra en el enlace. La dirección h2 s están en la diagonal de esa matriz de productos escalares y h1h2cos son las entradas no diagonales. A continuación, utilizando directamente la fórmula del teorema del coseno, se puede volver a convertir fácilmente la matriz "doblemente centrada" en la matriz de distancias al cuadrado.

En nuestra configuración, la matriz "doblemente centrada" es específicamente la sombrero (multiplicada por n-1), no productos escalares euclidianos, y la matriz de distancias al cuadrado resultante es, por tanto, la matriz de distancias de Mahalanobis al cuadrado, no la matriz de distancias euclidianas al cuadrado.

En notación matricial: Sea H sea la diagonal de H(n1) un vector columna. Propaga la columna en la matriz cuadrada: H= {H,H,...} Entonces D2mahal=H+H2H(n1) .

A continuación se muestra el código en SPSS y la sonda de velocidad.


Este primer código corresponde a la función @ahfoss fastPwMahal de la respuesta citada . Es equivalente matemáticamente. Pero yo estoy calculando la matriz simétrica completa de distancias (mediante operaciones matriciales) mientras que @ahfoss calcula un triángulo de la matriz simétrica (elemento a elemento).

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

La siguiente es mi modificación de la misma para hacerla más rápida:

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

Por último, el "enfoque de la matriz de sombreros". Por rapidez, calculo la matriz de sombreros (primero hay que centrar los datos) X(XX)1X mediante inversa generalizada (XX)1X obtenido en el solucionador de sistemas lineales 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

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X