如何使用核心R操纵/访问“dist”类实例的元素?

时间:2012-03-26 20:48:07

标签: r class matrix indexing distance

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"实例到stats,或其他类已经定义了常见的矩阵索引工具;因为在question about how to convert

中已经以多种方式回答了这个问题

"dist"包中(或者某些其他核心R包)中的工具是否专用于{{1}}实例的索引/访问元素?

12 个答案:

答案 0 :(得分:8)

遗憾的是,没有标准的方法可以做到这一点。这是两个函数,它们将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]

答案 1 :(得分:5)

我对您的问题没有直接的答案,但如果您使用欧几里德距离,请查看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 :(得分:4)

as.matrix(d)会将dist对象d转换为矩阵,而as.dist(m)会将矩阵m变回dist个对象。请注意,后者实际上并未检查m是否为有效距离矩阵;它只是提取下三角形部分。

答案 3 :(得分:3)

您可以使用str()

访问任何对象的属性

对于&#34; dist&#34;我的一些数据(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) 

你可以看到,对于这个特定的数据集,&#34;标签&#34; attribute是一个长度= 96的字符串,数字从1到96作为字符。

您可以直接更改该字符串:

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

&#34; your.labels&#34;应该是一些id。或者因子矢量,可能是来自&#34; dist&#34;的原始数据。对象是。

答案 4 :(得分:1)

您可能会发现这有用[来自?? dist]:

  

由a中的列存储的距离矩阵的下三角形   矢量,说'做'。如果'n'是观察的数量,即'n&lt; -   attr(do,“Size”)',然后是i&lt; j&lt; = n,之间的差异   (行)i和j是'do [n *(i-1) - i *(i-1)/ 2 + j-i]'。的长度   向量是n *(n-1)/ 2,即n ^ 2阶。

答案 5 :(得分:1)

这一回应实际上只是对Christian A早期回应的延伸。这是有道理的,因为问题的一些读者(包括我自己)可能会将dist对象视为对称(不仅仅是(7,13)如下,还有(13,7)。我没有编辑权限和早期的答案是正确的,只要用户将dist对象视为dist对象而不是稀疏矩阵,这就是为什么我有一个单独的响应而不是编辑。如果这个答案很有用,请投票给Christian A进行繁重的工作。 。 我编辑的原始答案粘贴在:

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的功能只有在i&lt;学家对于i = j和i&gt; j它返回错误的答案。当i == j时修改distdex函数以返回0并且当i> =时转换i和j。 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 :(得分:0)

你可以这样做:

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

但是,您对对角线或上三角形所做的任何更改都将被忽略。这可能是也可能不是正确的做法。如果不是,您需要为这些案例添加某种健全性检查或适当的处理。还有其他人。

答案 7 :(得分:0)

stats包中似乎没有工具。感谢@flodel在非核心软件包中的替代实现。

我挖掘了核心R源代码中"dist"类的定义,这是一个老式的S3,在dist.R源文件中没有工具,就像我在这个问题中所询问的那样

dist()函数的文档确实指出了(并且我引用):

距离矩阵的下三角形,由向量中的列存储,例如do。如果n是观察的数量,即n <- attr(do, "Size"),那么对于i&lt; j≤n,(行)ij之间的差异是:

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

返回:

dist1["5", "3"] <- 7000中的“R&gt;错误:矩阵”

上的下标数量不正确

根据问题,我认为@flodel更好地回答了这个问题,但仍然认为这个“答案”也可能有用。

我还在Matrix package中找到了方括号访问器和替换定义的一些不错的S4示例,可以很容易地从当前示例中进行调整。

答案 8 :(得分:0)

似乎dist对象的处理方式与简单的矢量对象几乎相同。据我所知,它的属性为矢量。所以要取出价值:

x = as.vector(distobject)

请参阅? dist用于公式,使用它们的索引提取特定对象对之间的距离。

答案 9 :(得分:0)

转换为矩阵对我来说也是不可能的,因为得到的矩阵将是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矢量中的位置。我希望这是有道理的。

答案 10 :(得分:0)

disto包提供了一个类,该类将距离矩阵包装在R中(内存中和内核外),并且提供了比[这样的便利运算符更多的功能。请在此处检查vignette

PS:我是包裹的作者。

答案 11 :(得分:0)

这是我通过名称从dist对象获取值的实用解决方案。是否希望将第9项作为值的向量?

as.matrix(mat1)[grepl("9", labels(mat1))]