矢量化 if_else 或 case_when 哪个不热切地评估其潜在输出?

Vectorized if_else or case_when which doesn't eagerly evaluate its potential outputs?

dplyr 具有矢量化条件 if_elsecase_when.

然而,这两个都急切地评估他们可能的输出(true/false for if_else,公式的 RHS for case_when):

suppressPackageStartupMessages({
  library(dplyr)
})

if_else(c(T, T, T), print(1), print(2))
#> [1] 1
#> [1] 2
#> [1] 1 1 1

case_when(
  c(T, T, T) ~ print(1),
  c(F, F, F) ~ print(2)
)
#> [1] 1
#> [1] 2
#> [1] 1 1 1

reprex package (v0.3.0)

于 2020-02-05 创建

在这里我们可以明显地看到 false 个案例被评估,即使它们从未被使用过。我正在寻找一种方法来避免这种情况,因为我的

是否有不这样做的替代方案?

我知道,另一种选择实际上是 base::ifelse:

ifelse(c(T, T, T), print(1), print(2))
#> [1] 1
#> [1] 1 1 1

然而,base::ifelse 是出了名的低效,因此最好有更好的替代方案。话虽这么说,我对 case_when 的替代方案特别感兴趣,当我否则需要使用一系列 ifelse 时,我会使用它。

我已经看过 data.table::fifelse,但它遇到了同样的问题:

suppressPackageStartupMessages({
  library(data.table)
})

fifelse(c(T, T, T), print(1), print(2))
#> [1] 1
#> [1] 2
#> [1] 1 1 1

那么,if_elsecase_when 是否有一个不急于评估其未使用案例的替代方案?

如果您从 GitHub 安装 data.table 的开发版本,您可以使用 fcase,它与 dplyr::case_when 类似,但具有惰性评估:

data.table::fcase(c(TRUE, TRUE, TRUE), print(1L), c(FALSE, FALSE, FALSE), print(2L))

[1] 1
[1] 1 1 1

您可以只依赖原生 R 对参数传递的惰性评估,并使用 all 来筛选 FALSE 不存在的情况:

lazy_if_else <- function(logical_test, value_if_true, value_if_false)
{
  if(all(logical_test)) return(rep(value_if_true, length.out = length(logical_test)))
  if_else(logical_test, value_if_true, value_if_false)
}

这优于 ifelseif_else

microbenchmark::microbenchmark(ifelse(c(T, T, T), 0, Sys.sleep(0.1)),
                               if_else(c(T, T, T), 0, Sys.sleep(0.1)),
                               lazy_if_else(c(T, T, T), 0, Sys.sleep(0.1)))
#> Unit: microseconds
#>                                         expr        min         lq         mean
#>        ifelse(c(T, T, T), 0, Sys.sleep(0.1))     12.662     13.689     25.47675
#>       if_else(c(T, T, T), 0, Sys.sleep(0.1)) 102723.054 109145.897 109678.33523
#>  lazy_if_else(c(T, T, T), 0, Sys.sleep(0.1))      4.791      5.476     10.80378
#>       median         uq        max neval cld
#>      15.3995     34.904     74.255   100  a 
#>  110036.0945 110176.049 116619.936   100   b
#>       6.5030     16.768     26.008   100  a