【问题标题】:is ifelse ever appropriate in a non-vectorized situation and vice-versa?ifelse 在非矢量化情况下是否合适,反之亦然?
【发布时间】:2011-11-24 03:12:25
【问题描述】:

(背景信息:ifelse 评估两个表达式,即使只返回一个。编辑:这是一个不正确的陈述。请参阅 Tommy 的回复)

有没有在非矢量化情况下使用ifelse 有意义的示例?我认为当我们不关心小的效率提升时,“可读性”可能是一个有效的答案,但除此之外,使用ifelse 时是否会更快/等效/更好的其他方式@ 987654324@ 然后else 可以完成这项工作吗?

同样,如果我有一个矢量化的情况,ifelse 总是最好使用的工具吗?对这两个表达式都求值似乎很奇怪。一个接一个循环并执行正常的if 然后else 是否更快?我猜只有在评估表达式需要很长时间时才有意义。还有其他不涉及显式循环的替代方案吗?

谢谢

【问题讨论】:

    标签: r


    【解决方案1】:

    首先,ifelse NOT 总是评估两个表达式 - 仅当测试向量中同时存在 TRUEFALSE 元素时。

    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 参数的长度。

    为什么我希望类(和其他属性)从测试对象中复制?因为返回逻辑向量的&lt; 不会从其(通常是数字)参数中复制类和属性。它不会这样做,因为它通常是非常错误的。

    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
    

    【讨论】:

    • 谢谢!我现在没有时间看这个,但我今晚晚些时候会这样做。感谢您的 cmets 和示例。
    • 请注意,ifelse 确实执行矢量回收 - 如果 yesno 变量的长度不等于 test,它们将被回收。这意味着如果所有向量的长度相同,则示例末尾的测试代码只会产生相同的结果。试试这个输入数据:tst &lt;- sample(c(TRUE, FALSE), 1e2, replace=TRUE); a &lt;- 1:100; b &lt;- -(1:50);
    • @Andrie - 谢谢,我在上面澄清了这一点。还添加了非矢量化 ifelse 给出令人惊讶的结果的示例。
    • @Andrie 好点安德里。感谢您注意到这一点。谢谢你更新,汤米。
    【解决方案2】:

    关于第二点,您如何定义“最佳”?我认为ifelse() 是更具可读性的解决方案之一,但可能并不总是最快的。具体来说,我发现写出布尔条件并将它们加在一起可以给您带来一些性能优势。这是一个简单的例子:

    > x <- rnorm(1e6)
    > system.time(y1 <- ifelse(x > 0,1,2))
       user  system elapsed 
       0.46    0.08    0.53 
    > system.time(y2 <- (x > 0) * 1 + (x <= 0) * 2)
       user  system elapsed 
       0.06    0.00    0.06 
    > identical(y1, y2)
    [1] TRUE
    

    所以,如果速度是您最关心的问题,那么布尔方法可能会更好。但是,对于我的大多数目的 - 我发现ifelse() 足够快并且很容易理解。您的里程可能会明显不同。

    【讨论】:

    • 我会羞愧地承认编写布尔版本不仅因为它很快,而且因为它是 31337 。但是,说真的,一旦你习惯了,代码就像使用 if 的代码一样容易阅读和理解。
    • 使用y3 &lt;- (x &lt;= 0) + 1 更快,因为它不包含冗余测试。
    猜你喜欢
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    相关资源
    最近更新 更多