这更像是对Roman答案的延伸评论,但我需要代码实用工具来详细阐述:
Roman正确指出if
比ifelse
更快,但我认为if
的速度提升并不特别有趣,因为它不能轻易地通过向量化来利用。也就是说,只有在cond
/test
参数的长度为1时,if
才比ifelse
优越。
考虑下面的函数,这是一种尝试将if
向量化但没有评估yes
和no
条件的副作用的薄弱方法。
ifelse2 <- function(test, yes, no){
result <- rep(NA, length(test))
for (i in seq_along(test)){
result[i] <- `if`(test[i], yes[i], no[i])
}
result
}
ifelse2a <- function(test, yes, no){
sapply(seq_along(test),
function(i) `if`(test[i], yes[i], no[i]))
}
ifelse3 <- function(test, yes, no){
result <- rep(NA, length(test))
logic <- test
result[logic] <- yes[logic]
result[!logic] <- no[!logic]
result
}
set.seed(pi)
x <- rnorm(1000)
library(microbenchmark)
microbenchmark(
standard = ifelse(x < 0, x^2, x),
modified = ifelse2(x < 0, x^2, x),
modified_apply = ifelse2a(x < 0, x^2, x),
third = ifelse3(x < 0, x^2, x),
fourth = c(x, x^2)[1L + ( x < 0 )],
fourth_modified = c(x, x^2)[seq_along(x) + length(x) * (x < 0)]
)
Unit: microseconds
expr min lq mean median uq max neval cld
standard 52.198 56.011 97.54633 58.357 68.7675 1707.291 100 ab
modified 91.787 93.254 131.34023 94.133 98.3850 3601.967 100 b
modified_apply 645.146 653.797 718.20309 661.568 676.0840 3703.138 100 c
third 20.528 22.873 76.29753 25.513 27.4190 3294.350 100 ab
fourth 15.249 16.129 19.10237 16.715 20.9675 43.695 100 a
fourth_modified 19.061 19.941 22.66834 20.528 22.4335 40.468 100 a
一些编辑: 感谢Frank和Richard Scriven指出我的不足。
正如你所见,将向量分解以适合传递给if
的过程是一个耗时的过程,最终比只运行ifelse
要慢(这可能就是为什么没有人费心实现我的解决方案)。
如果你真的急需提高速度,你可以使用上面的ifelse3
方法。或者更好的选择是Frank不太明显*但很棒的解决方案。
- 所谓“不太明显”,我的意思是,我花了两秒钟才意识到他做了什么。根据nicola下面的评论,请注意,此方法仅在
yes
和no
长度为1时有效,否则您应该坚持使用ifelse3
if
与ifelse
不是很好的比较,for (i in 1:n) { if(condition[i]){...} else {...} }
与ifelse
则可进行比较。 - Gregor Thomas