我正在使用dplyr::case_when()基于数据框中的几个变量来匹配一系列复杂的条件。作为一名防御性程序员,我想检查我是否已经考虑了所有可能的条件,但我找不到一种优雅的方法来做到这一点。
这是一个励志的例子:
library("tidyverse")
df <- expand_grid(x=c(NA_integer_, 1L, 2L), y=1L:2L)
df %>%
mutate(Case = case_when(
is.na(x) | is.na(y) ~ NA_character_,
x > y ~ "x>y",
x < y ~ "x<y"
))
Run Code Online (Sandbox Code Playgroud)
返回值混合了 NA:我只是传播输入 NA 的“好”和我忘记了可能条件 x==y 的“坏”。
从概念上讲,我想要的是:
df %>%
mutate(Case = case_when(
is.na(x) | is.na(y) ~ NA_character_,
x > y ~ "x>y",
x < y ~ "x<y",
TRUE ~ stop("Unmatched condition")
))
Run Code Online (Sandbox Code Playgroud)
但是(如 dplyr 中明确记录的那样)这将不起作用,因为每个 RHS 表达式总是被评估。以下确实有效:
library("magrittr")
df %>%
mutate(Case = case_when(
is.na(x) | is.na(y) ~ NA_character_,
x > y ~ "x>y",
x < y ~ "x<y",
TRUE ~ "UNMATCHED"
)) %T>%
with(., stopifnot(! "UNMATCHED" %in% Case))
Run Code Online (Sandbox Code Playgroud)
但这(a)感觉有点hackish,(b)用非字符返回类型更难实现。
有没有人有(更优雅的)解决方案来确保case_when()匹配> 0 的指定条件?
边注:
将此功能直接添加到 dplyr 实际上非常简单,例如:
@ -139,7 +139,7 @@
#' starwars %>%
#' mutate(type = case_character_type(height, mass, species, robots = FALSE)) %>%
#' pull(type)
case_when <- function(...) {
case_when <- function(..., require_match = FALSE) {
fs <- compact_null(list2(...))
n <- length(fs)
@ -173,6 +173,10 @@ case_when <- function(...) {
replaced <- replaced | (query[[i]] & !is.na(query[[i]]))
}
if (isTRUE(require_match) && !all(replaced)) {
abort(paste0("Unmatched cases for ", sum(!replaced), " element", if(sum(!replaced)>1) "s"))
}
out
}
Run Code Online (Sandbox Code Playgroud)
这样我们就可以做到:
df %>%
mutate(Case = case_when(
is.na(x) | is.na(y) ~ NA_character_,
x > y ~ "x>y",
x < y ~ "x<y",
require_match = TRUE
))
Run Code Online (Sandbox Code Playgroud)
我将提交此补丁的拉取请求(加上一些 roxygen 的东西),当然除非我的问题在火焰中被击落:)
| 归档时间: |
|
| 查看次数: |
210 次 |
| 最近记录: |