R Межклассовая матрица расстояний

Этот вопрос является своего рода продолжением того, как извлечь внутригрупповые и межгрупповые расстояния из матрицы расстояний? в р. В этом вопросе они сначала вычислили матрицу расстояний для всех точек, а затем просто извлекли матрицу расстояний между классами. У меня есть ситуация, когда я хотел бы пропустить начальные вычисления и пропустить право на извлечение, то есть я хочу напрямую вычислить матрицу расстояний между классами. Рисуя из связанного примера, с твиками, скажем, у меня есть некоторые данные в кадре данных под названием df:

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 элегантный и быстрый способ сделать это?

РЕДАКТИРОВАТЬ После получения хорошего решения для случая 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,

2 ответа

Решение

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

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

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-й результат, установив 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

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

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
Другие вопросы по тегам