首先,ifelse NOT 总是评估两个表达式 - 仅当测试向量中同时存在 TRUE 和 FALSE 元素时。
ifelse(TRUE, 'foo', stop('bar')) # "foo"
在我看来:
ifelse 应该不在非矢量化的情况下使用。使用ifelse 而不是if / else,总是更慢并且更容易出错:
# This is fairly common if/else code
if (length(letters) > 0) letters else LETTERS
# But this "equivalent" code will yield a very different result - TRY IT!
ifelse(length(letters) > 0, letters, LETTERS)
但在矢量化情况下,ifelse 可能是一个不错的选择 - 但请注意,结果的长度和属性可能不是您所期望的(如上所述,我认为ifelse 在这方面被破坏了)。
这是一个示例:tst 的长度为 5 并且有一个类。我希望结果的长度为 10 并且没有类,但事实并非如此 - 它得到一个不兼容的类和长度 5!
# a logical vector of class 'mybool'
tst <- structure(1:5 %%2 > 0, class='mybool')
# produces a numeric vector of class 'mybool'!
ifelse(tst, 101:110, 201:210)
#[1] 101 202 103 204 105
#attr(,"class")
#[1] "mybool"
为什么我希望长度为 10?因为 R 中的大多数函数都“循环”了较短的向量来匹配较长的向量:
1:5 + 1:10 # returns a vector of length 10.
...但是ifelse 仅循环是/否参数以匹配 tst 参数的长度。
为什么我希望类(和其他属性)不从测试对象中复制?因为返回逻辑向量的< 不会从其(通常是数字)参数中复制类和属性。它不会这样做,因为它通常是非常错误的。
1:5 < structure(1:10, class='mynum') # returns a logical vector without class
最后,“自己动手”会不会更有效率?好吧,看来ifelse 不像if 这样的原语,它需要一些特殊的代码来处理NA。如果你没有NAs,自己做会更快。
tst <- 1:1e7 %%2 == 0
a <- rep(1, 1e7)
b <- rep(2, 1e7)
system.time( r1 <- ifelse(tst, a, b) ) # 2.58 sec
# If we know that a and b are of the same length as tst, and that
# tst doesn't have NAs, then we can do like this:
system.time( { r2 <- b; r2[tst] <- a[tst]; r2 } ) # 0.46 secs
identical(r1, r2) # TRUE