Как мне манипулировать / получать доступ к элементам экземпляра класса dist с помощью ядра R? - PullRequest
20 голосов
/ 27 марта 2012

Базовый / общий класс в 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".Иными словами, это не вопрос о том, как преобразовать экземпляр a "dist" в "matrix" или некоторый другой класс, в котором уже определены общие инструменты индексирования матриц;поскольку на этот вопрос было дано несколько ответов в другом вопросе SO

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

Ответы [ 11 ]

8 голосов
/ 28 сентября 2012

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

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]
6 голосов
/ 27 марта 2012

У меня нет прямого ответа на ваш вопрос, но если вы используете евклидово расстояние, посмотрите на функцию rdist из пакета fields.Его реализация (в Фортране) быстрее, чем 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
4 голосов
/ 27 марта 2012

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

3 голосов
/ 09 июня 2015

Вы можете получить доступ к атрибутам любого объекта с помощью 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" должен быть некоторым идентификатором. или фактор-вектор, предположительно в исходных данных из объекта "dist".

1 голос
/ 04 июня 2015

Этот ответ на самом деле является лишь продолжением предыдущего ответа Кристиана А. Это оправдано, потому что некоторые читатели вопроса (включая меня) могут запрашивать объект 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

Функция Кристиана А работает, только если я j он возвращает неправильный ответ. Изменение функции distdex для возврата 0 при i == j и транспонирования i и 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
1 голос
/ 01 мая 2014

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

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

0 голосов
/ 02 марта 2019
Пакет

disto предоставляет класс, который переносит матрицы расстояний в R (в памяти и вне ядра) и предоставляет гораздо больше, чем вспомогательные операторы, такие как [. Пожалуйста, проверьте виньетка здесь.

PS: я автор пакета.

0 голосов
/ 29 мая 2015

Преобразование в матрицу также не подлежало сомнению, потому что результирующая матрица была бы 35K на 35K, поэтому я оставил ее как вектор (результат 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, где будет расстояние.Я надеюсь, что это имеет смысл.

0 голосов
/ 13 мая 2013

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

x = as.vector(distobject)

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

0 голосов
/ 28 марта 2012

В пакете stats, похоже, нет инструментов для этого.Спасибо @flodel за альтернативную реализацию в неосновном пакете.

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

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

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

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

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

(конечная кавычка)

Я воспользовался этим в следующем примере кода дляопределить себя "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: неверное количество подписчиков в матрице "

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

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

...