使用case_when处理不同长度向量输出时如何避免结果翻倍?
dplyr::case_when()的底层设计要求所有分支右侧的返回值长度必须与左侧条件的输入长度一致,函数会自动调用R的向量循环规则对齐所有分支的长度,该逻辑为内置固定规则,没有提供参数可关闭。
示例中分支包含长度为2的返回值c("A", "B"),当命中其他长度为1的返回分支时,短向量会被自动循环补齐到所有分支的最长长度,最终得到长度为2的输出,因此出现B+ B+的非预期结果。当a == b时刚好命中长度为2的分支,返回结果和最长分支长度匹配,因此输出符合预期。
以下方案均可实现需求,同时保留多分支逻辑的可读性,避免链式if_else维护难的问题:
方案1:将多返回值分支前置判断
不要在case_when内部直接写长度不一致的返回值,把需要返回多值的逻辑挪到case_when外单独判断,case_when内仅保留返回长度一致的单值分支:library(dplyr) library(stringr) a <- 1 b <- 2 if (a == b) { decision <- c("A", "B") } else { decision <- case_when( a > b ~ "A", TRUE ~ "B" ) } result <- decision %>% str_c("+") %>% paste(collapse = " ")该写法下各场景输出均符合预期:
a==b返回A+ B+,a>b返回A+,其余情况返回B+,不会出现自动补长的重复问题。方案2:结合
switch()拆分分支判断
如果判断分支均为标量条件(即单次调用传入的a、b均为单值,和自定义函数场景匹配),可以先用case_when生成分支标签,再用base R的switch()匹配对应返回值,switch()不会自动做跨分支长度对齐:get_decision <- function(a, b) { branch_label <- case_when( a == b ~ "equal", a > b ~ "greater", TRUE ~ "less" ) switch(branch_label, equal = c("A", "B"), greater = "A", less = "B") } a <- 1 b <- 2 decision <- get_decision(a, b) result <- decision %>% str_c("+") %>% paste(collapse = " ")方案3:自定义轻量判断函数
如果频繁用到这类分支返回长度不一致的逻辑,可以封装一个适配标量判断的简易版case_when,写法和原生函数完全一致,同时不会触发自动长度对齐:# 自定义函数仅适用于标量条件判断 scalar_case_when <- function(...) { args <- as.list(match.call()[-1]) env <- parent.frame() for (i in seq(1, length(args), by = 2)) { cond <- eval(args[[i]], envir = env) if (isTRUE(cond)) { return(eval(args[[i+1]], envir = env)) } } } # 直接替换原生case_when使用即可 a <- 1 b <- 2 decision <- scalar_case_when( a == b ~ c("A", "B"), a > b ~ "A", TRUE ~ "B" ) result <- decision %>% str_c("+") %>% paste(collapse = " ")
注意:如果需要做向量级批量判断(即传入的a、b是长度大于1的向量),R本身的向量运算逻辑要求同组返回值长度统一,这种场景下不建议混用不同长度的返回值。
内容的提问来源于stack exchange,提问作者ChrisP

