For 循环的最优构造

为了说明 good for loop 构造的效果,我们将以四种不同的方式计算每列的平均值:

  1. 使用优化不佳的 for 循环
  2. 使用优化的 for 循环
  3. 使用*apply 系列功能
  4. 使用 colMeans 功能

这些选项中的每一个都将以代码显示; 将显示执行每个选项的计算时间的比较; 最后将讨论差异。

环路优化不佳

column_mean_poor <- NULL
for (i in 1:length(mtcars)){
  column_mean_poor[i] <- mean(mtcars[[i]])
}

针对循环进行了优化

column_mean_optimal <- vector("numeric", length(mtcars))
for (i in seq_along(mtcars)){
  column_mean_optimal <- mean(mtcars[[i]])
}

vapply 功能

column_mean_vapply <- vapply(mtcars, mean, numeric(1))

colMeans 功能

column_mean_colMeans <- colMeans(mtcars)

效率比较

对这四种方法进行基准测试的结果如下所示(代码未显示)

Unit: microseconds
     expr     min       lq     mean   median       uq     max neval  cld
     poor 240.986 262.0820 287.1125 275.8160 307.2485 442.609   100    d
  optimal 220.313 237.4455 258.8426 247.0735 280.9130 362.469   100   c 
   vapply 107.042 109.7320 124.4715 113.4130 132.6695 202.473   100 a   
 colMeans 155.183 161.6955 180.2067 175.0045 194.2605 259.958   100  b

请注意,优化的 for 循环消除了构造不良的 for 循环。构造不良的 for 循环不断增加输出对象的长度,并且在每次改变长度时,R 重新评估对象的类。

通过在开始循环之前声明输出对象的类型及其长度,优化的 for 循环消除了一些此开销负担。

然而,在这个例子中,vapply 函数的使用使计算效率提高了一倍,主要是因为我们告诉 R 结果必须是数字(如果任何一个结果不是数字,则会返回错误)。

使用 colMeans 功能比 vapply 功能慢。这种差异可归因于在 colMeans 中执行的一些错误检查,主要是由于 vapply 函数中未执行的 as.matrix 转换(因为 mtcarsdata.frame)。