基于 R 中的某些值和索引矩阵获取数据矩阵的更有效方法?

More efficient way to get a data matrix based on some value and index matrices in R?

假设我有一个值向量,例如:

M=3;val<-rnorm(M)

和相应的索引矩阵如:

N=20;J=10;ind<-matrix(sample(1:M,N*J,replace=T),nrow=J)

我可以轻松地为值分配索引以获得数据矩阵:

x<-matrix(val[ind],J,N)

现在我有一个值矩阵,例如:

val<-matrix(rnorm(M*J),nrow=J)

并且需要逐行分配值和索引(即,val 中的一行和 ind 中的一行)以获得数据矩阵。

我可以用一个 for 循环来做到这一点:

x<-ind;
for(j in 1:J){x[j,]<-val[j,ind[j,]]}

但我想知道是否有更有效的方法来做到这一点,尤其是避免使用 for 循环?

我需要重新采样并重复分配过程数十万次。所以我担心for循环会占用很多时间。

一般来说,可以使用两列矩阵作为行和列索引来对矩阵进行子集或子集分配。所以

i_idx = rep(1:J, each = ncol(ind))
x_idx = cbind(i_idx, 1:ncol(ind))
val_idx = cbind(i_idx, as.vector(t(ind[1:J,])))

x[x_idx] = val[val_idx]

另外三种方法,一种使用sapply,一种矩阵子集和一种向量子集。矩阵和向量子集看起来比 for 循环更快,sapply 更慢。

目前

matrix(val[1:J + (ind-1)*J],J,N)

看起来是最快的方法。

M <- 3; N <- 20; J <- 10
ind <- matrix(sample(1:M,N*J,replace=T),nrow=J)
val <- matrix(rnorm(M*J),nrow=J)

x<-ind;
for(j in 1:J){x[j,]<-val[j,ind[j,]]}

identical(x, t(sapply(1:J, function(j) val[j,ind[j,]])))
#[1] TRUE

identical(x, matrix(val[matrix(c(rep(1:J, N), ind), ncol=2)],J,N))
#[1] TRUE
#Other ways for rep(1:J, N)
identical(x, matrix(val[matrix(c(row(ind), ind), ncol=2)],J,N))
#[1] TRUE
identical(x, matrix(val[matrix(c(slice.index(ind, 1), ind), ncol=2)],J,N))
#[1] TRUE

#Vector subsetting as suggested by Aaron
identical(x, matrix(val[row(ind) + (ind-1)*J],J,N))
#[1] TRUE
#Other ways
identical(x, matrix(val[1:J + (ind-1)*J],J,N))
#[1] TRUE
identical(x, matrix(val[sweep((ind-1)*J, 1, 1:J, "+")],J,N))
#[1] TRUE

速度比较:

library(microbenchmark)

f1 <- function() {
  x<-ind;
  for(j in 1:J){x[j,]<-val[j,ind[j,]]}
}
f2 <- function() {t(sapply(1:J, function(j) val[j,ind[j,]]))}
f3 <- function() {matrix(val[matrix(c(row(ind), ind), ncol=2)],J,N)}
f4 <- function() {matrix(val[row(ind) + (ind-1)*J],J,N)} #Comment from Aaron
f5 <- function() {matrix(val[1:J + (ind-1)*J],J,N)}

microbenchmark(f1(), f2(), f3(), f4(), f5(), setup=gc)
#Unit: microseconds
# expr    min      lq     mean  median      uq     max neval
# f1() 16.540 18.3595 20.11216 19.8820 20.7915  36.201   100
# f2() 43.514 46.3650 49.77573 48.0320 49.5120 113.631   100
# f3()  8.325  9.3265 10.38931  9.9425 10.4825  46.561   100
# f4()  6.934  7.8270  9.00286  8.4405  9.1355  25.840   100
# f5()  5.839  6.8730  7.71322  7.3520  8.3145  16.349   100