矢量化 if_else 或 case_when 哪个不热切地评估其潜在输出?
Vectorized if_else or case_when which doesn't eagerly evaluate its potential outputs?
dplyr
具有矢量化条件 if_else
和 case_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_else
和 case_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)
}
这优于 ifelse
和 if_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
dplyr
具有矢量化条件 if_else
和 case_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_else
和 case_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)
}
这优于 ifelse
和 if_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