我最近回答了一个与for
循环相关的问题。在测试我的代码速度后,我注意到在seq()
循环中使用:
而不是for
会大大降低速度。
看看这个非常简单的例子。 f1()
和f2()
之间的唯一区别是for
循环序列的变化,但f1()
的速度是f2()
的两倍。
f1 <- function() {
x <- 1:5; y <- numeric(length(x))
for(i in 1:length(x)) y[i] <- x[i]^2
y
}
f2 <- function() {
x <- 1:5; y <- numeric(length(x))
for(i in seq(x)) y[i] <- x[i]^2
y
}
library(microbenchmark)
microbenchmark(f1(), f2())
# Unit: microseconds
# expr min lq median uq max neval
# f1() 10.529 11.5415 12.1465 12.617 33.893 100
# f2() 25.052 25.5905 26.0385 28.759 78.553 100
为什么seq(x)
for
循环中的1:length(x)
比{{1}}慢得多?
答案 0 :(得分:24)
seq
是一种通用的S3方法,所以可能有一段时间丢失了调度。
seq.default
差不多100行!
您可能已经知道seq_along
,它直接调用.Primitive
并且比1:length(x)
好一点,这是我为长循环找到的最佳方法:
f3 <- function(){
x <- 1:5; y <- numeric(length(x))
for(i in seq_along(x)) y[i] <- x[i]^2
y
}
> microbenchmark(f1(), f3())
Unit: microseconds
expr min lq median uq max neval
f1() 27.095 27.916 28.327 29.148 89.495 100
f3() 26.684 27.505 27.916 28.327 36.538 100
答案 1 :(得分:9)
使用seq_len
,您获得与:
运算符几乎相同的时间:
f3 <- function(){
x <- 1:5; y <- numeric(length(x))
for(i in seq_len(length(x))) y[i] <- x[i]^2
y
}
library(microbenchmark)
microbenchmark(f1(), f2(),f3())
Unit: microseconds
expr min lq median uq max neval
f1() 9.988 10.6855 10.9650 11.245 50.704 100
f2() 23.257 23.7465 24.0605 24.445 88.140 100
f3() 10.127 10.5460 10.7555 11.175 18.857 100
内部seq
在调用:
或seq_len
之前会进行多次验证。
答案 2 :(得分:1)
更慢的具体原因:
seq(x)
会致电seq.default
*,seq.default
来电1L:x
!!
来自seq.default
:
if ((One <- nargs() == 1L) && !missing(from)) {
lf <- length(from)
return(if (mode(from) == "numeric" && lf == 1L) {
#checks validity -- more slow-down
if (!is.finite(from)) stop("'from' cannot be NA, NaN or infinite")
#boom! under the hood, seq.default is doing 1:N
1L:from
#looks like it defaults to seq_along if length(from) > 1?
} else if (lf) 1L:lf else integer())
}
*当然,除非x
为Date
或POSIXt
,否则您加载了另一个具有seq
方法的库...