【问题标题】:Does ifelse really calculate both of its vectors every time? Is it slow?ifelse 真的每次都计算它的两个向量吗?慢吗?
【发布时间】:2013-04-22 21:54:28
【问题描述】:

ifelse 是否真的计算了 yesno 向量——就像每个向量的整体一样? 或者它只是从每个向量中计算一些值?

另外,ifelse 真的那么慢吗?

【问题讨论】:

    标签: r performance if-statement vectorization


    【解决方案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]
    .
    .
    

    请注意,yesno 仅在有 是test 的一些非NA 值,即TRUEFALSE(分别)。
    在这一点上 - 这是效率方面的重要部分 - 每个向量的整体 被计算出来。


    好的,但是速度会慢吗?

    让我们看看我们是否可以测试它:

    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 &amp; ok ) ) 之类的东西而不是默认的 rep(yes, length.out = length(ans))[test &amp; !nas] 来停止对 yes 的不必要评估。
    • yesno 的实际重复可以忽略不计。但只是在分配yes 时,yes 被评估,同样在分配nono 被评估。因此成本
    • 没有办法在 R 中“部分地”评估一个向量,所以实际上只有一种方法 ifelse 可以工作。
    猜你喜欢
    • 2012-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 2021-06-20
    • 1970-01-01
    相关资源
    最近更新 更多