您可以通过 (1) 3 维转置分子数组和 (2) 将分母数组展平为向量来避免循环和复制,这样除法运算自然会在整个转置后循环不完整的分母向量分子数组,使数据按照您想要的方式排列。然后,您必须对结果进行 3 维“反转置”以恢复原始转置。
aperm(aperm(Xbeta,c(2,3,1))/c(denom),c(3,1,2));
第一次调用aperm() 将列转置为行,将z 切片转置为列,将行转置为z 切片。 c() 对 denom 的调用将分母数组展平为向量,因为在循环时,我们不关心维度。对aperm() 的最后调用会反转原来的转置。
要详细了解此解决方案的逻辑,您的输入基本上是分子数组的每个 z 切片的除数向量,并且您希望将每个除数应用于相应 z 的每一行- 切片和列。这意味着除数向量必须首先应用于列,然后,随着每个分母 z-slice 用尽,应用于分子 z-slice。在分子数组的一整行(覆盖该行中的所有 z 切片)用尽后,分母向量的整体已经用尽,导致它循环回到分子数组的下一行的开头。
见https://stat.ethz.ch/R-manual/R-devel/library/base/html/aperm.html。
关于性能的粗略想法:
r> set.seed(1);
r> Xbeta <- array(rnorm(13*20*10,0,1), dim=c(13,20,10) );
r> denom <- array(rnorm(1*20*10,0,1), dim=c(1,20,10) );
r> robert <- function() { result <- array(NA, dim=c(13,20,10) ); for (i in 1:10) { result[,,i] <- t(t(Xbeta[,,i]) / denom[,,i]); }; };
r> andre <- function() { denom_myVersion <- array(rep(c(denom), each=13 ), c(13,20,10) ); result <- Xbeta / denom_myVersion; };
r> bgoldst <- function() { result <- aperm(aperm(Xbeta,c(2,3,1))/c(denom),c(3,1,2)); };
r> N <- 99999;
r> system.time({ replicate(N, robert() ); });
user system elapsed
25.421 0.000 25.440
r> system.time({ replicate(N, andre() ); });
user system elapsed
12.578 0.594 13.283
r> system.time({ replicate(N, bgoldst() ); });
user system elapsed
8.484 0.594 9.142
此外,作为一般建议,使用最少的样本输入来呈现这类问题(对于提问者和回答者)是有帮助的,例如:
r> n <- array(1:12,dim=c(2,3,2)); n;
, , 1
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
, , 2
[,1] [,2] [,3]
[1,] 7 9 11
[2,] 8 10 12
r> d <- array(1:6,dim=c(1,3,2)); d;
, , 1
[,1] [,2] [,3]
[1,] 1 2 3
, , 2
[,1] [,2] [,3]
[1,] 4 5 6
r> aperm(aperm(n,c(2,3,1))/c(d),c(3,1,2));
, , 1
[,1] [,2] [,3]
[1,] 1 1.5 1.666667
[2,] 2 2.0 2.000000
, , 2
[,1] [,2] [,3]
[1,] 1.75 1.8 1.833333
[2,] 2.00 2.0 2.000000