Как я могу манипулировать/обращаться к элементам экземпляра класса "dist" с помощью ядра R?

Базовый/общий класс в R называется "dist" и является относительно эффективным представлением симметричной матрицы расстояния. Однако, в отличие от объекта "matrix", не существует поддержки для управления экземпляром "dist" по парам индексов с помощью оператора "[".

Например, следующий код ничего не возвращает, NULL или ошибка:

# First, create an example dist object from a matrix
mat1  <- matrix(1:100, 10, 10)
rownames(mat1) <- 1:10
colnames(mat1) <- 1:10
dist1 <- as.dist(mat1)
# Now try to access index features, or index values
names(dist1)
rownames(dist1)
row.names(dist1)
colnames(dist1)
col.names(dist1)
dist1[1, 2]

Между тем, следующие команды работают в некотором смысле, но не облегчают доступ к определенным значениям пары индексов/манипулируют ими:

dist1[1] # R thinks of it as a vector, not a matrix?
attributes(dist1)
attributes(dist1)$Diag <- FALSE
mat2 <- as(dist1, "matrix")
mat2[1, 2] <- 0

Обходной путь, который я хочу избежать, - сначала преобразовать объект "dist" в "matrix", обработать эту матрицу, а затем преобразовать обратно в "dist". То есть, это не вопрос о том, как преобразовать экземпляр "dist" в "matrix", или какой-либо другой класс, в котором используются стандартные инструменты индексирования матриц уже определены; поскольку на этот вопрос был дан ответ несколькими способами в другом вопросе SO

Существуют ли инструменты в пакете stats (или, возможно, некоторые другие базовые R-пакеты), предназначенные для индексирования/доступа к элементам экземпляра "dist"?

Ответ 1

У меня нет прямого ответа на ваш вопрос, но если вы используете эвклидову дистанцию, посмотрите на функцию rdist из пакета fields. Его реализация (в Fortran) выполняется быстрее, чем dist, а выход - из класса matrix. По крайней мере, это показывает, что некоторые разработчики решили отойти от этого класса dist, возможно, по той причине, о которой вы говорите. Если вы обеспокоены тем, что использование полного matrix для хранения симметричной матрицы - это неэффективное использование памяти, вы можете преобразовать ее в треугольную матрицу.

library("fields")
points <- matrix(runif(1000*100), nrow=1000, ncol=100)

system.time(dist1 <- dist(points))
#    user  system elapsed 
#   7.277   0.000   7.338 

system.time(dist2 <- rdist(points))
#   user  system elapsed 
#  2.756   0.060   2.851 

class(dist2)
# [1] "matrix"
dim(dist2)
# [1] 1000 1000
dist2[1:3, 1:3]
#              [,1]         [,2]         [,3]
# [1,] 0.0000000001 3.9529674733 3.8051198575
# [2,] 3.9529674733 0.0000000001 3.6552146293
# [3,] 3.8051198575 3.6552146293 0.0000000001

Ответ 2

Нет стандартных способов сделать это, к сожалению. Вот две функции, которые преобразуют между 1D-индексом в двумерные матричные координаты. Они не красивы, но они работают, и по крайней мере вы можете использовать код, чтобы сделать что-то приятнее, если вам это нужно. Я отправляю его только потому, что уравнения не очевидны.

distdex<-function(i,j,n) #given row, column, and n, return index
    n*(i-1) - i*(i-1)/2 + j-i

rowcol<-function(ix,n) { #given index, return row and column
    nr=ceiling(n-(1+sqrt(1+4*(n^2-n-2*ix)))/2)
    nc=n-(2*n-nr+1)*nr/2+ix+nr
    cbind(nr,nc)
}

Небольшая тестовая проводка, чтобы показать, что она работает:

dist(rnorm(20))->testd
as.matrix(testd)[7,13]   #row<col
distdex(7,13,20) # =105
testd[105]   #same as above

testd[c(42,119)]
rowcol(c(42,119),20)  # = (3,8) and (8,15)
as.matrix(testd)[3,8]
as.matrix(testd)[8,15]

Ответ 3

as.matrix(d) превратит объект dist d в матрицу, а as.dist(m) превратит матрицу m обратно в объект dist. Обратите внимание, что последнее на самом деле не проверяет, что m является допустимой матрицей расстояний; он просто извлекает нижнюю треугольную часть.

Ответ 4

Вы можете найти это полезное [from?? dist]:

Нижний треугольник матрицы расстояния, сохраненный столбцами в вектор, скажите "сделай. Если" n "- число наблюдений, то есть" n < attr (do, "Size" ), то для я < j <= n, несходство между (строка) я и j - это do [n * (i-1) - я * (i-1)/2 + j-i]. Длина вектор n * (n-1)/2, т.е. порядка n ^ 2.

Ответ 5

Этот ответ на самом деле просто расширенный подход к христианскому раннему ответу. Это оправданно, потому что некоторые читатели вопроса (включая меня) могут запрашивать объект dist так, как если бы он был симметричным (не только (7,13), как ниже, но также и (13,7). У меня нет прав на редактирование, а более ранний ответ был правильным, если пользователь обрабатывал объект dist как объект dist, а не разреженную матрицу, поэтому у меня есть отдельный ответ, а не редактирование. Прогоните Кристиана А за тяжелую работу, если этот ответ полезен, Оригинальный ответ с моими изменениями, вставленными в:

distdex<-function(i,j,n) #given row, column, and n, return index
    n*(i-1) - i*(i-1)/2 + j-i

rowcol<-function(ix,n) { #given index, return row and column
    nr=ceiling(n-(1+sqrt(1+4*(n^2-n-2*ix)))/2)
    nc=n-(2*n-nr+1)*nr/2+ix+nr
    cbind(nr,nc)
}
#A little test harness to show it works:

dist(rnorm(20))->testd
as.matrix(testd)[7,13]   #row<col
distdex(7,13,20) # =105
testd[105]   #same as above

Но...

distdex(13,7,20) # =156
testd[156]   #the wrong answer

Функция Christian A действует только тогда, когда я < к. Для я = j и i > j он возвращает неверный ответ. Изменение функции distdex для возврата 0 при я == j и транспонирования я и j, когда i > j решает проблему, поэтому:

distdex2<-function(i,j,n){ #given row, column, and n, return index
  if(i==j){0
  }else if(i > j){
    n*(j-1) - j*(j-1)/2 + i-j
  }else{
    n*(i-1) - i*(i-1)/2 + j-i  
  }
}

as.matrix(testd)[7,13]   #row<col
distdex2(7,13,20) # =105
testd[105]   #same as above
distdex2(13,7,20) # =105
testd[105]   #the same answer

Ответ 6

Вы можете получить атрибуты любого объекта с помощью str()

для объекта "dist" некоторых моих данных (dist1), он выглядит так:

> str(dist1)
Class 'dist'  atomic [1:4560] 7.3 7.43 7.97 7.74 7.55 ...
  ..- attr(*, "Size")= int 96
  ..- attr(*, "Labels")= chr [1:96] "1" "2" "3" "4" ...
  ..- attr(*, "Diag")= logi FALSE
  ..- attr(*, "Upper")= logi FALSE
  ..- attr(*, "method")= chr "euclidean"
  ..- attr(*, "call")= language dist(x = dist1) 

вы можете видеть, что для этого конкретного набора данных атрибут "Ярлыки" является символьной строкой длиной = 96 с номерами от 1 до 96 в качестве символов.

вы можете напрямую изменить эту строку символов:

> attr(dist1,"Labels") <- your.labels

"your.labels" должен быть некоторым id. или вектора факторов, предположительно в исходных данных из объекта "dist".

Ответ 7

Вы можете сделать это:

d <- function(distance, selection){
  eval(parse(text = paste("as.matrix(distance)[",
               selection, "]")))
}

`d<-` <- function(distance, selection, value){
  eval(parse(text = paste("as.matrix(distance)[",
               selection, "] <- value")))
  as.dist(distance)
}

Это позволит вам сделать это:

 mat <- matrix(1:12, nrow=4)
 mat.d <- dist(mat)
 mat.d
        1   2   3
    2 1.7        
    3 3.5 1.7    
    4 5.2 3.5 1.7

 d(mat.d, "3, 2")
    [1] 1.7
 d(mat.d, "3, 2") <- 200
 mat.d
          1     2     3
    2   1.7            
    3   3.5 200.0      
    4   5.2   3.5   1.7

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

Ответ 8

В этом пакете stats нет инструментов. Благодаря @flodel для альтернативной реализации в неосновном пакете.

Я выкопал определение класса "dist" в базовом R-источнике, который является старой школьной S3 без инструментов в исходном файле dist.R, как то, о чем я прошу в этом вопросе.

Документация функции dist() указывает, с пользой, что (и я цитирую):

Нижний треугольник матрицы расстояния, сохраненный столбцами в векторе, например do. Если n - число наблюдений, т.е. n <- attr(do, "Size"), то для я < j ≤ n, разница между (рядом) i и j равна:

do[n*(i-1) - i*(i-1)/2 + j-i]

Длина вектора n*(n-1)/2, т.е. порядка n^2.

(конечная цитата)

Я воспользовался этим в следующем примере кода для аксессуара define-yourself "dist". Обратите внимание: этот пример может возвращать только одно значение за раз.

################################################################################
# Define dist accessor
################################################################################
setOldClass("dist")
getDistIndex <- function(x, i, j){
    n <- attr(x, "Size")
    if( class(i) == "character"){ i <- which(i[1] == attr(x, "Labels")) }
    if( class(j) == "character"){ j <- which(j[1] == attr(x, "Labels")) }
    # switch indices (symmetric) if i is bigger than j
    if( i > j ){
        i0 <- i
        i  <- j
        j  <- i0
    }
    # for i < j <= n
    return( n*(i-1) - i*(i-1)/2 + j-i )
}
# Define the accessor
"[.dist" <- function(x, i, j, ...){
    x[[getDistIndex(x, i, j)]]
}
################################################################################

И это, кажется, работает нормально, как и ожидалось. Тем не менее, у меня возникли проблемы с работой функции замены.

################################################################################
# Define the replacement function
################################################################################
"[.dist<-" <- function(x, i, j, value){
    x[[get.dist.index(x, i, j)]] <- value
    return(x)
}
################################################################################

Пробный запуск этого нового оператора присваивания

dist1["5", "3"] <- 7000

Возвращает:

"R > Ошибка в dist1["5", "3"] <- 7000: неправильное количество индексов на матрице"

As-ask, я думаю, что @flodel лучше ответил на вопрос, но все же думал, что этот "ответ" также может быть полезен.

Я также нашел несколько хороших примеров S4 для определения квадратов в квадратных скобках и замены в пакете Matrix который можно было бы адаптировать из этого текущего примера довольно легко.

Ответ 9

Похоже, что объекты dist обрабатываются почти так же, как простые векторные объекты. Насколько я могу видеть его вектор с атрибутами. Итак, чтобы получить значения:

x = as.vector(distobject)

См? dist для формулы для извлечения расстояния между определенной парой объектов с использованием их индексов.

Ответ 10

Преобразование в матрицу также было непостижимо для меня, потому что результирующая матрица была бы 35 К на 35 К, поэтому я оставил ее как вектор (результат dist) и написал функцию, чтобы найти место в векторе, где расстояние должно быть:

distXY <- function(X,Y,n){
  A=min(X,Y)
  B=max(X,Y)

  d=eval(parse(text=
               paste0("(A-1)*n  -",paste0((1:(A-1)),collapse="-"),"+ B-A")))

  return(d)

}

Где вы предоставляете X и Y, исходные строки элементов в матрице, из которых вы рассчитали dist, а n - общее количество элементов в этой матрице. Результатом является положение в векторе dist, где будет расстояние. Надеюсь, это имеет смысл.