2016-08-22 7 views
2

Этот вопрос является продолжением how to extract intragroup and intergroup distances from a distance matrix? in R. В этом вопросе они сначала вычислили матрицу расстояний для всех точек, а затем просто извлекли межклассовую матрицу расстояний. У меня есть ситуация, когда я хотел бы обойти начальные вычисления и пропустить право на извлечение, т. Е. Я хочу напрямую вычислить межклассовую матрицу расстояний. Исходя из связанных, например, с ухищрениями, скажем, у меня есть некоторые данные в dataframe называются df:R Межклассовая матрица расстояний

values<-c(0.002,0.3,0.4,0.005,0.6,0.2,0.001,0.002,0.3,0.01) 
class<-c("A","A","A","B","B","B","B","A","B","A") 
df<-data.frame(values, class) 

То, что я хотел бы это расстояние матрица:

1 2 3 8 10 
4 .003 .295 .395 .003 .005 
5 .598 .300 .200 .598 .590 
6 .198 .100 .200 .198 .190 
7 .001 .299 .399 .001 .009 
9 .298 .000 .100 .298 .290 

ли уже существует в R элегантный и быстрый способ сделать это?

EDIT После получения хорошего решения для 1D выше случае, я думал о бонусной вопрос: что о многомерном случае, скажем, если вместо df выглядит следующим образом:

values1<-c(0.002,0.3,0.4,0.005,0.6,0.2,0.001,0.002,0.3,0.01) 
values2<-c(0.001,0.1,0.1,0.001,0.1,0.1,0.001,0.001,0.1,0.01) 
class<-c("A","A","A","B","B","B","B","A","B","A") 
df<-data.frame(values1, values2, class) 

И я Интересно снова получить матрицу евклидова расстояния между точками в классе B с очками в классе A.

ответ

3

Для общего n-мерного евклидова расстояния, мы можем использовать уравнение (не R, а алгебра):

square_dist(b,a) = sum_i(b[i]*b[i]) + sum_i(a[i]*a[i]) - 2*inner_prod(b,a) 

где суммы над размерами векторов a и b для i=[1,n]. Здесь a и b представляют собой одну пару от A и B. Ключевым моментом здесь является то, что это уравнение можно записать как матричное уравнение для всех пар в A и B.

В коде:

## First split the data with respect to the class 
n <- 2 ## the number of dimensions, for this example is 2 
tmp <- split(df[,1:n], df$class) 

d <- sqrt(matrix(rowSums(expand.grid(rowSums(tmp$B*tmp$B),rowSums(tmp$A*tmp$A))), 
       nrow=nrow(tmp$B)) - 
      2. * as.matrix(tmp$B) %*% t(as.matrix(tmp$A))) 

Примечания:

  1. Внутренний rowSums вычислить sum_i(b[i]*b[i]) и sum_i(a[i]*a[i]) для каждого b в B и a в A, соответственно. Затем образуется все пары между B и A.
  2. Внешние rowSums вычисляет sum_i(b[i]*b[i]) + sum_i(a[i]*a[i]) для всех этих пар.
  3. Этот результат затем преобразуется в matrix. Обратите внимание, что количество строк этой матрицы - это количество точек класса B по вашему запросу.
  4. Затем вычтите два раза скалярное произведение всех пар. Это внутреннее произведение можно записать в виде матрицы умножить на tmp$B %*% t(tmp$A), где я оставил без исключения принуждение к матрице.
  5. Наконец, возьмите квадратный корень.

Используя этот код с данными:

print(d) 
##   1   2   3   8   10 
##4 0.0030000 0.3111688 0.4072174 0.0030000 0.01029563 
##5 0.6061394 0.3000000 0.2000000 0.6061394 0.59682493 
##6 0.2213707 0.1000000 0.2000000 0.2213707 0.21023796 
##7 0.0010000 0.3149635 0.4110985 0.0010000 0.01272792 
##9 0.3140143 0.0000000 0.1000000 0.3140143 0.30364453 

Обратите внимание, что этот код будет работать для любого n > 1. Мы можем восстановить свой предыдущий результат 1-D, установив n в 1 и не выполнять внутреннюю rowSums (потому что теперь только один столбец в tmp$A и tmp$B):

n <- 1 ## the number of dimensions, set this now to 1 
tmp <- split(df[,1:n], df$class) 

d <- sqrt(matrix(rowSums(expand.grid(tmp$B*tmp$B,tmp$A*tmp$A)), 
       nrow=length(tmp$B)) - 
      2. * as.matrix(tmp$B) %*% t(as.matrix(tmp$A))) 
print(d) 
##  [,1] [,2] [,3] [,4] [,5] 
##[1,] 0.003 0.295 0.395 0.003 0.005 
##[2,] 0.598 0.300 0.200 0.598 0.590 
##[3,] 0.198 0.100 0.200 0.198 0.190 
##[4,] 0.001 0.299 0.399 0.001 0.009 
##[5,] 0.298 0.000 0.100 0.298 0.290 
+0

Спасибо за ваш невероятно подробный ответ! Это прекрасно работает. – itf

+0

Это работает, но я нахожу проблему: с гораздо большим набором данных по 1000 баллов в каждом классе, я получаю NaN в 'd'. Я проверяю записи 'tmp $ A' и' tmp $ B', используя 'a <- which (is.nan (d), arr.ind = T)', а затем делает, например, 'tmp $ A [a [1],] 'и' tmp $ A [a [nrow (a) + 1],] ', и я обнаружил, что NaN, похоже, возникают, когда векторы обоих классов идентичны. Это точная вещь? Извините, я не могу привести конкретный пример, поскольку мой набор данных слишком велик. – itf

+0

Это может быть. Единственная причина для NaN в вычислении состоит в том, что квадратное расстояние оказывается отрицательным, что может быть связано с точностью. Чтобы проверить, удалите квадратный корень и посмотрите, есть ли в полученной матрице отрицательные числа. Они должны быть небольшими. Если это проблема, то просто порог до нуля, прежде чем принимать квадратный корень. Если это не проблема, сообщите мне. – aichao

2

Вот попытка с помощью генерации каждой комбинации, а затем просто взять разницу из каждого значения:

abs(matrix(Reduce(`-`, expand.grid(split(df$values, df$class))), nrow=5, byrow=TRUE)) 
#  [,1] [,2] [,3] [,4] [,5] 
#[1,] 0.003 0.295 0.395 0.003 0.005 
#[2,] 0.598 0.300 0.200 0.598 0.590 
#[3,] 0.198 0.100 0.200 0.198 0.190 
#[4,] 0.001 0.299 0.399 0.001 0.009 
#[5,] 0.298 0.000 0.100 0.298 0.290 
+0

Это идеальный вариант! Простой вопрос: каковы возможные типы функций, которые могут выполнять функции Reduce()? Например, можно взять n-мерное евклидово расстояние, а не только одномерное расстояние (и здесь это взломано, если принять абсолютное значение абсолютной разности), в приведенном выше примере имеется только один столбец значений, но что, если бы у меня было три столбца разных значений, например 'value1',' value2' и 'value3'? – itf

+1

@itf - Я не думаю, что это действительно доступно для> 1D расстояний, к сожалению. Мне нужно было бы еще больше подумать. – thelatemail

+0

Нет проблем, это моя вина, что я не задал вопрос о более высоком расстоянии. Я отредактирую свой вопрос соответствующим образом и посмотрю, появятся ли новые решения. Спасибо за ваше решение 1D! – itf