如何使用核心 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] <- 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.matrix
:as.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 <- 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 更好地回答了这个问题,但仍然认为这个“答案”也可能有用。
我还在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”类实例的元素?的主要内容,如果未能解决你的问题,请参考以下文章
如何创建可以为类提供实例数组并提供作用于所有类实例的“巫毒”实例的元类?