假设我有一个预先分配的数据结构,为了性能而不是随着时间的推移增长数据结构.首先,我尝试使用sapply:
set.seed(1)
count <- 5
pre <- numeric(count)
sapply(1:count, function(i) {
pre[i] <- rnorm(1)
})
pre
# [1] 0 0 0 0 0
for(i in 1:count) {
pre[i] <- rnorm(1)
}
pre
# [1] -0.8204684 0.4874291 0.7383247 0.5757814 -0.3053884
Run Code Online (Sandbox Code Playgroud)
我假设这是因为匿名函数in sapply在不同的范围内(或者它是R?中的环境),因此pre对象不一样.for循环存在于相同的范围/环境中,因此它按预期工作.
我一般都试图采用R机制进行迭代,使用apply函数和for,但我在这里看不到解决方法.有什么不同我应该做的或这种类型的操作更好的习惯用法?
如上所述,我的例子非常人为,我对正常的偏差不感兴趣.相反,我的实际代码是处理4列150万行数据帧.以前我依靠增长和合并来获得最终的数据帧,并决定尝试避免合并并根据基准测试进行预分配.
sapply并不意味着像这样使用.它已经预先分配了结果.
无论如何,for循环不太可能是性能缓慢的源头; 这可能是因为你反复对data.frame进行子集化.例如:
set.seed(21)
N <- 1e4
d <- data.frame(n=1:N, s=sample(letters, N, TRUE))
l <- as.list(d)
set.seed(21)
system.time(for(i in 1:N) { d$n[i] <- rnorm(1); d$s <- sample(letters,1) })
# user system elapsed
# 6.12 0.00 6.17
set.seed(21)
system.time(for(i in 1:N) { l$n[i] <- rnorm(1); l$s <- sample(letters,1) })
# user system elapsed
# 0.14 0.00 0.14
D <- as.data.frame(l, stringsAsFactors=FALSE)
identical(d,D)
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)
所以你应该遍历各个向量并在循环之后将它们组合成一个data.frame .
是的,您本质上是在更改pre匿名函数本地的 a ,该函数本身将返回最后一次评估的结果(长度为 1 的向量),因此sapply()返回正确的解作为向量(因为它累积了各个长度为 1 的向量)但它不会改变pre全局工作区中的。
您可以使用<<-运算符来解决此问题:
set.seed(1)
count <- 5
pre <- numeric(count)
sapply(1:count, function(i) {
pre[i] <<- rnorm(1)
})
> pre
[1] -0.6264538 0.1836433 -0.8356286 1.5952808 0.3295078
Run Code Online (Sandbox Code Playgroud)
情况已经改变pre,但我会出于各种原因避免这样做。
pre在这种情况下,我认为预分配不会有太多好处sapply()。
而且,对于这个例子来说,两者的效率都非常低。只需rnorm()生成count随机数即可。但我想这个例子只是为了说明这一点?