9

我在 R 和 MATLAB 上都使用多维数组,这些数组有五个维度(总共 1450 万个元素)。我必须删除一个对其应用算术平均值的维度,我发现使用这两个软件的性能存在惊人的差异。

MATLAB:

>> a = rand([144  73  10   6  23]);
>> tic; b = mean(a,3); toc
Elapsed time is 0.014454 seconds.

回复:

> a = array(data = runif(144*73*6*23*10), dim = c(144,73,10,6,23))
> start <- Sys.time (); b = apply(a, c(1,2,4,5), mean); Sys.time () - start
Time difference of 1.229083 mins

我知道 apply 函数很慢,因为它类似于通用函数,但我不知道如何处理这个问题,因为这种性能差异对我来说确实是一个很大的限制。我试图搜索 colMeans/rowMeans 函数的概括,但没有成功。

编辑 我将展示一个小样本矩阵:

> dim(a)
[1] 2 4 3
> dput(aa)
structure(c(7, 8, 5, 8, 10, 11, 9, 9, 6, 12, 9, 10, 12, 10, 14, 
12, 7, 9, 8, 10, 10, 9, 8, 6), .Dim = c(2L, 4L, 3L))
a_mean = apply(a, c(2,3), mean)
> a_mean
     [,1] [,2] [,3]
[1,]  7.5  9.0  8.0
[2,]  6.5  9.5  9.0
[3,] 10.5 11.0  9.5
[4,]  9.0 13.0  7.0

编辑(2):

我发现应用 sum 函数然后除以删除维度的大小肯定更快:

> start <- Sys.time (); aaout = apply(aa, c(1,2,4,5), sum); Sys.time () - start
Time difference of 5.528063 secs
4

2 回答 2

23

在 R 中,apply不是完成任务的正确工具。如果您有一个矩阵并且需要行或列均值,您将使用更快的向量化rowMeanscolMeans. 您仍然可以将这些用于多维数组,但您需要有点创意:

假设您的数组有n维度,并且您想沿维度计算均值i

  1. 用于aperm将尺寸移动i到最后一个位置n
  2. rowMeans与_dims = n - 1

同样,您可以:

  1. 用于aperm将尺寸移动i到第一个位置
  2. colMeans与_dims = 1

a <- array(data = runif(144*73*6*23*10), dim = c(144,73,10,6,23))

means.along <- function(a, i) {
  n <- length(dim(a))
  b <- aperm(a, c(seq_len(n)[-i], i))
  rowMeans(b, dims = n - 1)
}

system.time(z1 <- apply(a, c(1,2,4,5), mean))
#    user  system elapsed 
#  25.132   0.109  25.239 
system.time(z2 <- means.along(a, 3))
#    user  system elapsed 
#   0.283   0.007   0.289 

identical(z1, z2)
# [1] TRUE
于 2013-09-05T10:04:26.207 回答
5

mean由于 S3 方法分派,速度特别慢。这更快:

set.seed(42)
a = array(data = runif(144*73*6*23*10), dim = c(144,73,10,6,23))

system.time({b = apply(a, c(1,2,4,5), mean.default)})
# user  system elapsed 
#16.80    0.03   16.94

如果你不需要处理NAs 你可以使用内部函数:

system.time({b1 = apply(a, c(1,2,4,5),  function(x) .Internal(mean(x)))})
# user  system elapsed 
# 6.80    0.04    6.86

为了比较:

system.time({b2 = apply(a, c(1,2,4,5),  function(x) sum(x)/length(x))})
# user  system elapsed 
# 9.05    0.01    9.08 

system.time({b3 = apply(a, c(1,2,4,5),  sum)
             b3 = b3/dim(a)[[3]]})
# user  system elapsed 
# 7.44    0.03    7.47

(请注意,所有时间都只是近似值。正确的基准测试需要反复运行,例如,使用其中一个基准测试包。但我现在对此没有足够的耐心。)

使用 Rcpp 实现可能会加快速度。

于 2013-09-05T09:01:26.003 回答