为什么不使用for循环?

Pet*_*sza 8 for-loop r

我一直在网上看到很多关于如何不建议循环的数据科学家的评论.但是,我最近发现自己处于使用一个有用的情况.我想知道以下过程是否有更好的替代方案(为什么替代方案会更好):

我需要运行一系列重复测量ANOVA,并且与您在下面看到的可重现示例类似地解决问题.

[我知道有关于运行多个ANOVA模型的其他问题,并且还有其他选项可用于这些类型的分析,但是现在我只想听听for循环的使用]

作为一个例子,四个重复测量ANOVA模型 - 四个因变量,每个变量分别在三个时间测量:

set.seed(1976)
code <- seq(1:60)
time <- rep(c(0,1,2), each = 20)
DV1 <- c(rnorm(20, 10, 2), rnorm(20, 10, 2), rnorm(20, 14, 2))
DV2 <- c(rnorm(20, 10, 2), rnorm(20, 10, 2), rnorm(20, 10, 2))
DV3 <- c(rnorm(20, 10, 2), rnorm(20, 10, 2), rnorm(20, 8, 2))
DV4 <- c(rnorm(20, 10, 2), rnorm(20, 10, 2), rnorm(20, 10, 2))
dat <- data.frame(code, time, DV1, DV2, DV3, DV4)

outANOVA <- list()

for (i in names(dat)) {
  y <- dat[[i]]
  outANOVA[i] <- summary(aov(y ~ factor(time) + Error(factor(code)), 
                                  data = dat))
}

outANOVA
Run Code Online (Sandbox Code Playgroud)

Moo*_*per 13

你可以这样写它,它更紧凑:

outANOVA <-
  lapply(dat,function(y)
    summary(aov(y ~ factor(time) + Error(factor(code)),data = dat)))
Run Code Online (Sandbox Code Playgroud)

for循环不一定比应用函数慢(并且确实可以更快,因为@thc在评论中提到)但对于许多人来说它们不太容易阅读.这在某种程度上是一种品味问题.

真正的犯罪是for在矢量化函数可用时使用循环.这些向量化函数通常包含用C语言编写的for循环速度快得多(或者调用函数).

请注意,在这种情况下,我们也可以避免创建全局变量y,并且我们不必初始化列表outANOVA.

另一点,直接来自这个相关的帖子:对于R中的循环和计算速度(由Glen_b回答):

对于R中的循环并不总是比其他方法慢,例如apply - 但是有一个巨大的bugbear - •永远不会在循环内生成一个数组

相反,在循环之前使数组成为全尺寸,然后填充它们.

在你的情况下,你正在成长outANOVA,对于大循环,它可能会成为问题.

以下是microbenchmark一个简单示例的一些不同方法:

n <- 100000
microbenchmark::microbenchmark(
preallocated_vec  = {x <- vector(length=n); for(i in 1:n) {x[i] <- i^2}},
preallocated_vec2 = {x <- numeric(n); for(i in 1:n) {x[i] <- i^2}},
incremented_vec   = {x <- vector(); for(i in 1:n) {x[i] <- i^2}},
preallocated_list = {x <- vector(mode = "list", length = n); for(i in 1:n) {x[i] <- i^2}},
incremented_list  = {x <- list(); for(i in 1:n) {x[i] <- i^2}},
sapply            = sapply(1:n, function(i) i^2),
lapply            = lapply(1:n, function(i) i^2),
times=20)

# Unit: milliseconds
# expr                     min         lq       mean     median         uq        max neval
# preallocated_vec    9.784237  10.100880  10.686141  10.367717  10.755598  12.839584    20
# preallocated_vec2   9.953877  10.315044  10.979043  10.514266  11.792158  12.789175    20
# incremented_vec    74.511906  79.318298  81.277439  81.640597  83.344403  85.982590    20
# preallocated_list  10.680134  11.197962  12.382082  11.416352  13.528562  18.620355    20
# incremented_list  196.759920 201.418857 212.716685 203.485940 205.441188 393.522857    20
# sapply              6.557739   6.729191   7.244242   7.063643   7.186044   9.098730    20
# lapply              6.019838   6.298750   6.835941   6.571775   6.844650   8.812273    20
Run Code Online (Sandbox Code Playgroud)