当我最近重新阅读Hadley的Advanced R时,我注意到他在第6章中说`if`
可以用作函数
`if`(i == 1, print("yes"), print("no"))
(如果你手头有实体书,那就在第80页)
我们知道ifelse
很慢(Does ifelse really calculate both of its vectors every time? Is it slow?),因为它会评估所有参数。由于`if`
似乎只评估if
个参数(这只是我的假设),TRUE
会是一个很好的替代方案吗?
更新:根据@Benjamin和@Roman的答案以及@Gregor和其他许多人的评论,ifelse
似乎是矢量化计算的更好解决方案。我在这里接受@ Benjamin的回答,因为它提供了更全面的比较和社区健康。但是,这两个答案(以及评论)都值得一读。
答案 0 :(得分:16)
这更多是基于罗马答案的扩展评论,但我需要代码实用程序来解释:
罗马是正确的if
比ifelse
更快,但我认为if
的速度提升并不特别有趣,因为它不是{&1;}。通过矢量化可以很容易地利用的东西。也就是说,当if
/ ifelse
参数的长度为1时,cond
仅优于test
。
考虑以下函数,这是一个无可否认的向量化if
的弱尝试,而不会像yes
一样评估no
和ifelse
条件的副作用。< / p>
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
方法。或者更好的是,弗兰克不那么明显*但是很棒的解决方案。
yes
和no
的长度为1时才有效,否则您将坚持使用ifelse3
答案 1 :(得分:9)
if
是通过.Primitive
接口调用的原始(编译)函数,而ifelse
是R字节码,因此似乎if
会更快。运行一些快速基准
> microbenchmark(`if`(TRUE, "a", "b"), ifelse(TRUE, "a", "b"))
Unit: nanoseconds
expr min lq mean median uq max neval cld
if (TRUE) "a" else "b" 46 54 372.59 60.0 68.0 30007 100 a
ifelse(TRUE, "a", "b") 1212 1327 1581.62 1442.5 1617.5 11743 100 b
> microbenchmark(`if`(FALSE, "a", "b"), ifelse(FALSE, "a", "b"))
Unit: nanoseconds
expr min lq mean median uq max neval cld
if (FALSE) "a" else "b" 47 55 91.64 61.5 73 2550 100 a
ifelse(FALSE, "a", "b") 1256 1346 1688.78 1460.0 1677 17260 100 b
似乎如果不考虑实际分支中的代码,if
至少比ifelse
快20倍。但请注意,这并未考虑正在测试的表达式的复杂性以及可能的优化。
更新:请注意,此快速基准代表了if
vs ifelse
的一个非常简化且有些偏见的用例(正如评论中所指出的)。虽然它是正确的,但它代表了ifelse
用例,因为本杰明的答案似乎提供了更公平的比较。
答案 2 :(得分:0)
是的。我使用ifelse()花费了90分钟,并将if()改进为25min来开发152589条记录
for(i in ...){
# "Case 1"
# asesorMinimo<-( dummyAsesor%>%filter(FechaAsignacion==min(FechaAsignacion)) )[1,]
# asesorRegla<-tail(dummyAsesor%>%filter( FechaAsignacion<=dumFinClase)%>%arrange(FechaAsignacion),1)
# #Asigna Asesor
# dummyRow<-dummyRow%>%mutate(asesorRetencion=ifelse(dim(asesorRegla)[1]==0,asesorMinimo$OperadorNombreApellido,asesorRegla$OperadorNombreApellido))
# "Case 2"
asesorRegla<-tail(dummyAsesor%>%filter( FechaAsignacion<=dumFinClase)%>%arrange(FechaAsignacion),1)
asesorMinimo<-( dummyAsesor%>%filter(FechaAsignacion==min(FechaAsignacion)) )[1,]
if(dim(asesorRegla)[1]==0){
dummyRow<-dummyRow%>%mutate(asesorRetencion=asesorMinimo[1,7])
}else{
dummyRow<-dummyRow%>%mutate(asesorRetencion=asesorRegla[1,7])
}
}