2016-12-07 16 views
1

Я пытаюсь вычислить расстояние Махаланобиса между каждым наблюдением набора данных dat, где каждая строка является наблюдением, а каждый столбец является переменной. Такое расстояние определяется как:Расстояние Махаланобиса каждой пары наблюдений

formula

Я написал функцию, которая делает это, но я чувствую, что это очень медленный процесс. Есть ли лучший способ вычислить это в R?

Для создания некоторых данных для тестирования функции:

generateData <- function(nObs, nVar){ 
    library(MASS) 
    mvrnorm(n=nObs, rep(0,nVar), diag(nVar)) 
    } 

Это функция, которую я написал до сих пор. Они работают и для моих данных (800 общ и 90 переменных), для method = "forLoop" и method = "apply" требуется приблизительно 30 и 33 секунды соответственно.

mhbd_calc2 <- function(dat, method) { #Method is either "forLoop" or "apply" 
    dat <- as.matrix(na.omit(dat)) 
    nObs <- nrow(dat) 
    mhbd <- matrix(nrow=nObs,ncol = nObs) 
    cv_mat_inv = solve(var(dat)) 

    distMH = function(x){ #Mahalanobis distance function 
    diff = dat[x[1],]-dat[x[2],] 
    diff %*% cv_mat_inv %*% diff 
    } 

    if(method=="forLoop") 
    { 
    for (i in 1:nObs){ 
     for(j in 1:i){ 
     mhbd[i,j] <- distMH(c(i,j)) 
     } 
    } 
    } 
    if(method=="apply") 
    { 
    mhbd[lower.tri(mhbd)] = apply(combn(nrow(dat),2),2, distMH) 
    } 
    result = sqrt(mhbd) 
    colnames(result)=rownames(dat) 
    rownames(result)=rownames(dat) 
    return(as.dist(result)) 
} 

NB: Я попытался с помощью outer(), но это было еще медленнее (60seconds)

ответ

2

Вы нужны математические знания.

  1. Сделайте cholesky факторизацию эмпирической ковариации, затем стандартизируйте свои наблюдения;
  2. использовать dist для вычисления евклидова расстояния по преобразованным наблюдениям.

dist.maha <- function (dat) { 
    X <- as.matrix(na.omit(dat)) ## ensure a valid matrix 
    V <- cov(X) ## empirical covariance; positive definite 
    L <- t(chol(V)) ## lower triangular factor 
    stdX <- t(forwardsolve(L, t(X))) ## standardization 
    dist(stdX) ## use `dist` 
    } 

Пример

set.seed(0) 
x <- matrix(rnorm(6 * 3), 6, 3) 

dist.maha(x) 
#   1  2  3  4  5 
#2 2.362109          
#3 1.725084 1.495655       
#4 2.959946 2.715641 2.690788     
#5 3.044610 1.218184 1.531026 2.717390   
#6 2.740958 1.694767 2.877993 2.978265 2.794879 

Результат согласуется с mhbd_calc2.

+0

Итак, если я правильно понимаю, вы dist.maha немного менее точны, но гораздо быстрее? С точностью 7 цифр, это то же самое с моими испытаниями – Oligg

+0

Возможно, я ошибаюсь, но метод choleski не проверяет, является ли матрица почти единственной. Если это так, это может дать большие значения, которые мы не хотим, нет? В то время как solve() выполняет эту проверку и возвращает ошибку, чтобы предотвратить ее. – Oligg

+0

Я думаю, что это выходит за рамки моих знаний, но я обязательно спрошу. Кроме того, не могли бы вы немного рассказать о том, как работает ваш метод, если вы не возражаете? Эта функция сэкономит мне много времени, спасибо большое :) – Oligg