计算不带for循环/应用的三维数组的行和
取数组计算不带for循环/应用的三维数组的行和,r,R,取数组b_数组: set.seed(123) a_mtx = matrix(1:15,ncol=5) b_mtx = matrix(seq(1,5,length.out=30),ncol=5) b_array = array( b_mtx, dim = c( nrow(b_mtx), ncol(b_mtx), nrow(a_mtx) ) ) 如果我想计算b_数组的每个“切片”或“工作表”
b_数组:
set.seed(123)
a_mtx = matrix(1:15,ncol=5)
b_mtx = matrix(seq(1,5,length.out=30),ncol=5)
b_array =
array(
b_mtx,
dim =
c(
nrow(b_mtx),
ncol(b_mtx),
nrow(a_mtx)
)
)
如果我想计算b_数组
的每个“切片”或“工作表”的每一列的总和,我可以使用colSums
及其维度参数:
colSums(b_array, dim = 1)
# [,1] [,2] [,3]
#[1,] 8.068966 8.068966 8.068966
#[2,] 13.034483 13.034483 13.034483
#[3,] 18.000000 18.000000 18.000000
#[4,] 22.965517 22.965517 22.965517
#[5,] 27.931034 27.931034 27.931034
要对行和执行相同的操作,我不能使用行和
的维度参数,因为它的处理方式不同,因此我求助于应用
:
apply(b_array, 3, rowSums)
# [,1] [,2] [,3]
#[1,] 13.27586 13.27586 13.27586
#[2,] 13.96552 13.96552 13.96552
#[3,] 14.65517 14.65517 14.65517
#[4,] 15.34483 15.34483 15.34483
#[5,] 16.03448 16.03448 16.03448
#[6,] 16.72414 16.72414 16.72414
我希望在维度更大的数组上执行相同的计算,因此apply
和其他for-loop方法都是无效的
有没有其他真正矢量化的方法?关于边距=
(第二个)参数应用的默认想法(我相信)是,它意味着“减少的轴”(在聚合时……在这里简化以获得效果)。然而,另一种看待它的方式是,所有其他维度都保持不变
例如,colSums(ary)
的有效等价物是apply(ary,2,sum)
,意思是“保持轴1不减少”。(colSums
实际上是在内部完成的,而不是使用apply
)因此,为了扩展“除外的所有轴”逻辑,让我们为您的b_数组
实现您希望保留第一和第三个轴,这样做
apply(b_array, c(1,3), sum)
# [,1] [,2] [,3]
# [1,] 13.27586 13.27586 13.27586
# [2,] 13.96552 13.96552 13.96552
# [3,] 14.65517 14.65517 14.65517
# [4,] 15.34483 15.34483 15.34483
# [5,] 16.03448 16.03448 16.03448
# [6,] 16.72414 16.72414 16.72414
它的效率与(我认为)使用n维数组进行“列”求和的效率差不多
编辑:
@markus使用aperm
的速度更快,适用于各种矩阵大小,尽管它似乎收敛于更大的矩阵
ns <- c(10,50,100,1000)
set.seed(123)
arrays <- lapply(ns, function(n) array(runif(3*n*n), dim=c(n,n,3)))
mapply(identical,
lapply(arrays, function(a) t(colSums(aperm(a, perm = c(2, 3, 1))))),
lapply(arrays, function(a) apply(a, c(1,3), sum)))
# [1] TRUE TRUE TRUE TRUE
library(microbenchmark)
microbenchmark(
aperm10 = t(colSums(aperm(arrays[[1]], perm = c(2, 3, 1)))),
aperm50 = t(colSums(aperm(arrays[[2]], perm = c(2, 3, 1)))),
aperm100 = t(colSums(aperm(arrays[[3]], perm = c(2, 3, 1)))),
aperm1000 = t(colSums(aperm(arrays[[4]], perm = c(2, 3, 1)))),
apply10 = apply(arrays[[1]], c(1,3), sum),
apply50 = apply(arrays[[2]], c(1,3), sum),
apply100 = apply(arrays[[3]], c(1,3), sum),
apply1000 = apply(arrays[[4]], c(1,3), sum),
times=10
)
# Unit: microseconds
# expr min lq mean median uq max neval
# aperm10 19.1 25.5 46.74 39.55 59.2 105.8 10
# aperm50 55.7 77.2 96.36 94.30 115.6 149.8 10
# aperm100 231.2 247.2 267.14 258.35 295.5 301.8 10
# aperm1000 47282.5 47568.4 49235.19 49581.85 50118.4 52034.4 10
# apply10 53.7 59.1 78.42 63.15 105.6 123.5 10
# apply50 263.9 282.3 318.08 306.60 366.4 383.0 10
# apply100 637.7 686.6 712.65 710.75 741.5 799.7 10
# apply1000 40173.7 52735.7 52170.08 54349.65 55692.9 57375.9 10
ns使用aperm的另一个选项
t(colSums(aperm(b_array, perm = c(2, 3, 1))))
# [,1] [,2] [,3]
#[1,] 13.27586 13.27586 13.27586
#[2,] 13.96552 13.96552 13.96552
#[3,] 14.65517 14.65517 14.65517
#[4,] 15.34483 15.34483 15.34483
#[5,] 16.03448 16.03448 16.03448
#[6,] 16.72414 16.72414 16.72414
有趣。明天我将测试它,看看它是否能够在不崩溃的情况下处理更大的矩阵。aperm
创建了一个数组副本,我相信,在一个大数据集上,这将是非常好的。看起来这种方法在计算时间和内存使用方面更优越aperm
使用的内存减少了8.38倍,完成速度快了近30倍。您用来证明aperm
更快的数据集有多大?您在实际的基准测试中使用了什么?如果矩阵的大小不同——或者如果它真的和问题中的样本数据一样小——那么基准测试是不必要的,而且可能会产生误导。