使用行和列索引从矩阵中获取索引值

28

我有一个大小为500行×335列的二维矩阵mat,以及一个包含120425行的数据框dat。数据框dat有两列IJ,用于索引mat中的行和列。我希望将mat中的值添加到dat的行中。

以下是我的概念失败:

> dat$matval <- mat[dat$I, dat$J]
Error: cannot allocate vector of length 1617278737

(我在Win32上使用R 2.13.1)。深入挖掘后,我发现我错误地使用了矩阵索引,因为看起来我只获得了mat的子矩阵,而不是我期望的单维数组值,即:

> str(mat[dat$I[1:100], dat$J[1:100]])
 int [1:100, 1:100] 20 1 1 1 20 1 1 1 1 1 ...

我原本期望得到类似 int [1:100] 20 1 1 1 20 1 1 1 1 1 ... 的内容。使用行列索引获取二维矩阵中的值的正确方式是什么?


对于一个有趣的问题点赞(这也引出了另一个问题:为什么在传递 [ 运算符 N 个向量用于 N 维矩阵时,没有选项可以将行为更改为类似这样的行为?) - Ari B. Friedman
好问题 - 我稍微编辑了一下,修复了我认为的一个打字错误(将datI改为dat$I)。如果这不是你想要的,请随意撤销... - joran
4个回答

43

几乎正确。需要将其作为一个两列矩阵提供给“ [ ”:

dat$matval <- mat[ cbind(dat$I, dat$J) ] # should do it.

需要注意的是:虽然这种方法也适用于数据框,但它们首先会被强制转换为矩阵类,如果其中有任何非数值类的元素,则整个矩阵将成为“最低公共类”。


1
+1 是为了找到 R 明确想要做的事情的方法而设立的;-) - Ari B. Friedman
那么如果IJ是唯一的列,只需要使用mat[dat]吗?还是需要强制转换为矩阵? - joran
1
似乎强制转换是必要的,因为数据框实际上是一个列表。所以你也可以使用 as.matrix(dat) - joran
3
请查看"..."下的?"["中的Arguments部分。当处理数组或矩阵时,矩阵必须具有被访问对象的维度数量相同的列数。该帮助页面上还有一些示例。 - IRTFM
如果数据框包含超出矩阵边界的I和J索引值,会发生什么?我很确定它会失败...我认为@Tommy的答案将返回NAs以处理这种情况。只是要记住这一点... - Chase
这种索引方法相当晦涩,没有在“R入门”教程中涵盖。我很好奇,阅读了文档,其中确实有涵盖它的内容 - Heisenberg

11
使用矩阵作为索引,正如DWin建议的那样,当然更加简洁。但由于某种奇怪的原因,手动使用一维索引实际上略微更快:
# Huge sample data
mat <- matrix(sin(1:1e7), ncol=1000)
dat <- data.frame(I=sample.int(nrow(mat), 1e7, rep=T), 
                  J=sample.int(ncol(mat), 1e7, rep=T))

system.time( x <- mat[cbind(dat$I, dat$J)] )     # 0.51 seconds
system.time( mat[dat$I + (dat$J-1L)*nrow(mat)] ) # 0.44 seconds

dat$I + (dat$J-1L)*nrow(m)这部分将二维索引转换为一维索引。1L 是指定整数而不是双精度值的方法,这样可以避免一些强制转换。

我还尝试了gsk3的基于apply的解决方案。但它几乎慢了500倍:

system.time( apply( dat, 1, function(x,mat) mat[ x[1], x[2] ], mat=mat ) ) # 212

1
这是一个使用apply的基于行的操作的一行代码。
> dat <- as.data.frame(matrix(rep(seq(4),4),ncol=2))
> colnames(dat) <- c('I','J')
> dat
   I  J
1  1  1
2  2  2
3  3  3
4  4  4
5  1  1
6  2  2
7  3  3
8  4  4
> mat <- matrix(seq(16),ncol=4)
> mat
     [,1] [,2] [,3] [,4]
[1,]    1    5    9   13
[2,]    2    6   10   14
[3,]    3    7   11   15
[4,]    4    8   12   16

> dat$K <- apply( dat, 1, function(x,mat) mat[ x[1], x[2] ], mat=mat )
> dat
  I J  K
1 1 1  1
2 2 2  6
3 3 3 11
4 4 4 16
5 1 1  1
6 2 2  6
7 3 3 11
8 4 4 16

-1
n <- 10
mat <- cor(matrix(rnorm(n*n),n,n))
ix <- matrix(NA,n*(n-1)/2,2)
k<-0
for (i in 1:(n-1)){
    for (j in (i+1):n){
    k <- k+1
    ix[k,1]<-i
    ix[k,2]<-j
    }
}
o <- rep(NA,nrow(ix))
o <- mat[ix]
out <- cbind(ix,o)

3
一些评论会很好,让答案更具“吸引力”。 - Johan Karlsson
1
你不能只回答一堆代码...来吧...解释一下你的答案 :) - Skizo-ozᴉʞS ツ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接