基于 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
假设我有一个值向量,例如:
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