ifelse 真的每次都计算它的两个向量吗?慢吗?
Posted
技术标签:
【中文标题】ifelse 真的每次都计算它的两个向量吗?慢吗?【英文标题】:Does ifelse really calculate both of its vectors every time? Is it slow? 【发布时间】:2013-04-22 21:54:28 【问题描述】:ifelse
是否真的计算了 yes
和 no
向量——就像每个向量的整体一样?
或者它只是从每个向量中计算一些值?
另外,ifelse
真的那么慢吗?
【问题讨论】:
【参考方案1】:是的。 (有例外)
ifelse
计算其yes
值和no
值。除非test
条件是全部TRUE
或全部FALSE
。
我们可以通过生成随机数并观察实际生成了多少数字来看到这一点。 (通过恢复seed
)。
# TEST CONDITION, ALL TRUE
set.seed(1)
dump <- ifelse(rep(TRUE, 200), rnorm(200), rnorm(200))
next.random.number.after.all.true <- rnorm(1)
# TEST CONDITION, ALL FALSE
set.seed(1)
dump <- ifelse(rep(FALSE, 200), rnorm(200), rnorm(200))
next.random.number.after.all.false <- rnorm(1)
# TEST CONDITION, MIXED
set.seed(1)
dump <- ifelse(c(FALSE, rep(TRUE, 199)), rnorm(200), rnorm(200))
next.random.number.after.some.TRUE.some.FALSE <- rnorm(1)
# RESET THE SEED, GENERATE SEVERAL RANDOM NUMBERS TO SEARCH FOR A MATCH
set.seed(1)
r.1000 <- rnorm(1000)
cat("Quantity of random numbers generated during the `ifelse` statement when:",
"\n\tAll True ", which(r.1000 == next.random.number.after.all.true) - 1,
"\n\tAll False ", which(r.1000 == next.random.number.after.all.false) - 1,
"\n\tMixed T/F ", which(r.1000 == next.random.number.after.some.TRUE.some.FALSE) - 1
)
给出以下输出:
Quantity of random numbers generated during the `ifelse` statement when:
All True 200
All False 200
Mixed T/F 400 <~~ Notice TWICE AS MANY numbers were
generated when `test` had both
T & F values present
我们也可以在源码本身中看到:
.
.
if (any(test[!nas]))
ans[test & !nas] <- rep(yes, length.out = length(ans))[test & # <~~~~ This line and the one below
!nas]
if (any(!test[!nas]))
ans[!test & !nas] <- rep(no, length.out = length(ans))[!test & # <~~~~ ... are the cluprits
!nas]
.
.
请注意,yes
和 no
仅在有
是test
的一些非NA
值,即TRUE
或FALSE
(分别)。
在这一点上 - 这是效率方面的重要部分 - 每个向量的整体 被计算出来。
好的,但是速度会慢吗?
让我们看看我们是否可以测试它:
library(microbenchmark)
# Create some sample data
N <- 1e4
set.seed(1)
X <- sample(c(seq(100), rep(NA, 100)), N, TRUE)
Y <- ifelse(is.na(X), rnorm(X), NA) # Y has reverse NA/not-NA setup than X
这两个语句产生相同的结果
yesifelse <- quote(sort(ifelse(is.na(X), Y+17, X-17 ) ))
noiflese <- quote(sort(c(Y[is.na(X)]+17, X[is.na(Y)]-17)))
identical(eval(yesifelse), eval(noiflese))
# [1] TRUE
但一个比另一个快两倍
microbenchmark(eval(yesifelse), eval(noiflese), times=50L)
N = 1,000
Unit: milliseconds
expr min lq median uq max neval
eval(yesifelse) 2.286621 2.348590 2.411776 2.537604 10.05973 50
eval(noiflese) 1.088669 1.093864 1.122075 1.149558 61.23110 50
N = 10,000
Unit: milliseconds
expr min lq median uq max neval
eval(yesifelse) 30.32039 36.19569 38.50461 40.84996 98.77294 50
eval(noiflese) 12.70274 13.58295 14.38579 20.03587 21.68665 50
【讨论】:
我 +1 是因为我认为你已经做了非常彻底的调查工作,即使我认为你在比较两个不同的东西! 顺便说一句,我不是在抨击ifelse
。事实上,我一直都在使用它,除非我需要效率。
我现在更好地理解了这一点。如果可以的话,我会给+2。我明白你的意思了。 ifelse
最好使用 rep(yes, length.out = length(ans) - sum(! test & ok ) )
之类的东西而不是默认的 rep(yes, length.out = length(ans))[test & !nas]
来停止对 yes
的不必要评估。
yes
和 no
的实际重复可以忽略不计。但只是在分配yes
时,yes
被评估,同样在分配no
时no
被评估。因此成本
没有办法在 R 中“部分地”评估一个向量,所以实际上只有一种方法 ifelse
可以工作。以上是关于ifelse 真的每次都计算它的两个向量吗?慢吗?的主要内容,如果未能解决你的问题,请参考以下文章