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

Posted

技术标签:

【中文标题】如何使用核心 R 操作/访问“dist”类实例的元素?【英文标题】:How do I manipulate/access elements of an instance of "dist" class using core R? 【发布时间】:2012-04-10 09:33:29 【问题描述】:

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"。也就是说,这不是一个question about how to convert 一个"dist" 实例到一个"matrix",或者其他一些已经定义了通用矩阵索引工具的类;因为这已在different SO question

中以多种方式回答

stats 包(或其他一些核心 R 包)中是否有工具专用于索引/访问 "dist" 实例的元素?

【问题讨论】:

好问题。没有答案,但请注意,在 R 中,矩阵只是一个有维度的向量。所以dist1[1:20]dist1[5] &lt;- 100 等可以正常工作也就不足为奇了。有点麻烦,你可能会写一个二维的版本,虽然我对原子的了解有限。 【参考方案1】:

不幸的是,没有标准的方法可以做到这一点。这是将一维索引转换为二维矩阵坐标的两个函数。它们并不漂亮,但它们可以工作,至少你可以在需要时使用代码来制作更好的东西。我发布它只是因为方程式不明显。

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]

【讨论】:

这是一个最有用的答案,但考虑到预期的应用,它需要一些澄清。它仅在 i j 它返回错误的答案。修改 distdex 函数以在 i == j 时返回 0 并在 i > j 时转置 i 和 j 解决了问题我将代码放在下面的响应中,以便其他人可以复制和粘贴。需要明确的是,这只是距离矩阵的非标准查询的问题,所以不是对 Christian A 的回答的挖掘只是一个澄清。 这里有更好的实现:R - How to get row & column subscripts of matched elements from a distance matrix.【参考方案2】:

我没有直接回答您的问题,但如果您使用的是欧几里得距离,请查看 fields 包中的 rdist 函数。它的实现(在 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

【讨论】:

谢谢!了解这一点很有用。知道 R 中基本的 "dist"-handling 工具相当简陋,这很有帮助。【参考方案3】:

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

【讨论】:

我的问题末尾已经提到了这个答案(最后一段,以及示例代码中的as(dist1, "matrix")。我想知道是否有一个“就地”解决方案——与这个解决方法分开—— - 由 R 中的 "dist" 类支持。感谢关于 as.dist 未检查 dist 实例有效性的评论。 我没有看到区别。矩阵式索引的“就地”解决方案dist对象上使用as.matrixas.matrix泛型调用stats:::as.matrix.dist,这是dist的方法类。 我试图澄清问题的结尾。我认为混淆源于短语“就地”的歧义,我认为它可以指(1)R中已经存在的任何工具,包括由于某种原因不受欢迎的方法,比如我想要的类触发器避免;或 (2) 专用于此目的的工具和类,不会更改我的对象的类以完成任务,即使是暂时的。【参考方案4】:

您可以使用 str() 访问任何对象的属性

对于我的一些数据 (dist1) 的“dist”对象,它看起来像这样:

> 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) 

您可以看到,对于这个特定的数据集,“Labels”属性是一个长度 = 96 的字符串,其中包含从 1 到 96 的数字作为字符。

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

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

"your.labels" 应该是一些 id。或者因子向量,大概是在原始数据中用“dist”对象制作的。

【讨论】:

【参考方案5】:

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

x = as.vector(distobject)

看到了吗? dist 用于使用索引提取特定对象对之间距离的公式。

【讨论】:

这(强制)已在其他答案中进行了描述,并在问题中注明为我试图避免的事情。还希望一些代码尝试至少以矩阵样式"[" 表示法进行某种类型的提取或赋值,作为有价值的答案。 as.vector 对我来说比 as.matrix 快​​ 30 倍【参考方案6】:

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

按列存储的距离矩阵的下三角形 矢量,说“做”。如果‘n’是观察次数,即‘n

【讨论】:

我的部分答案已经包含该公式很长时间了。检查?dist 的文档是我做的第一件事,早在将这个问题发布给 SO 之前。【参考方案7】:

这个回应实际上只是对 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 j 它返回错误的答案。修改 distdex 函数以在 i == j 时返回 0 并在 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

【讨论】:

这里有更好的实现:R - How to get row & column subscripts of matched elements from a distance matrix.【参考方案8】:

你可以这样做:

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

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

【讨论】:

谢谢泰勒。这似乎仍然是一个类触发器(尽管很聪明且有用),它也可能会杀死您原始 "dist" 实例中的一些潜在有用属性,例如 $call。我很好奇你对我刚刚在下面发布的答案的看法,其中包括一个不修改类的工作访问器,以及一个我还没有解决的不工作的替换函数。 @Paul,您的解决方案看起来不错,尽管由于某种原因它返回了对角线的错误值。不知道为什么替换功能不起作用。【参考方案9】:

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

我深入研究了核心 R 源代码中 "dist" 类的定义,它是老式 S3,在 dist.R 源文件中没有工具,就像我在这个问题中所问的那样。

dist() 函数的文档确实指出,有用的是,(我引用):

按列存储在向量中的距离矩阵的下三角形,例如do。如果n是观察的数量,即n &lt;- 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"] &lt;- 7000 中的错误:矩阵上的下标数量不正确”

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

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

【讨论】:

【参考方案10】:

转换为矩阵对我来说也是没有问题的,因为生成的矩阵将是 35K x 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 向量中的位置。我希望这是有道理的。

【讨论】:

【参考方案11】:

disto 包提供了一个在 R 中封装距离矩阵的类(内存中和核外),并且提供的功能远不止像 [ 这样的便利运算符。请在此处查看vignette。

PS:我是包的作者。

【讨论】:

【参考方案12】:

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

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

【讨论】:

以上是关于如何使用核心 R 操作/访问“dist”类实例的元素?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建可以为类提供实例数组并提供作用于所有类实例的“巫毒”实例的元类?

在 Groovy 中,实例的元类与其类的元类有啥区别

从它的元类python引用一个类的实例

如何从类属性(特别是 UIButton)访问父类实例?

为啥我的元类实现失败并出现关于无法创建 NoneType 实例的 TypeError

ThinkPHP6 核心分析之Http 类跟Request类的实例化