使用case_when结合dplyr::filter简化嵌套ifelse的报错排查
问题描述
我正在编写一个接收4个输入的函数,使用dplyr根据输入值格式化数据框。每个输入仅能取“None”或“X”两个值,希望仅对值为“X”的字段过滤,后按这些字段分组计算汇总统计量。我原本用大量嵌套ifelse处理16种输入组合,想改用dplyr的case_when、filter和group_by简化,但当前函数报错。
数据构造代码
x1 <- c(rep(0:1, each = 8)) x2 <- c(rep(0:1, 8)) x3 <- c(rep(0:1, each = 4, times = 2)) x4 <- c(rep(0:1, each = 2, times = 4)) y <- c(seq(1:16)) df <- data.frame(x1,x2,x3,x4,y)
数据框内容
x1 x2 x3 x4 y 1 0 0 0 0 1 2 0 1 0 0 2 3 0 0 0 1 3 4 0 1 0 1 4 5 0 0 1 0 5 6 0 1 1 0 6 7 0 0 1 1 7 8 0 1 1 1 8 9 1 0 0 0 9 10 1 1 0 0 10 11 1 0 0 1 11 12 1 1 0 1 12 13 1 0 1 0 13 14 1 1 1 0 14 15 1 0 1 1 15 16 1 1 1 1 16
函数代码
exampleFX <- function(z1, z2, z3, z4) { df <- dplyr::filter(dplyr::case_when( z1 != 0 ~ x1 == z1, z2 != 0 ~ x2 == z2, z3 != 0 ~ x3 == z3, z4 != 0 ~ x4 == z4 )) return(df$y) }
错误信息
调用exampleFX(0,1,0,1)预期返回4,但出现错误:
Error in UseMethod("filter") : no applicable method for 'filter' applied to an object of class "logical"
解决方案
错误原因
dplyr::filter()的第一个参数必须是数据框,而你把case_when()的结果(一个逻辑向量)直接传给了filter,不符合函数用法,因此报错。另外case_when是按顺序匹配,只会返回第一个满足条件的结果,无法同时应用多个过滤条件(比如调用时z2和z4都不为0,需要同时过滤x2=1和x4=1)。
修正后的函数代码
library(dplyr) library(purrr) library(rlang) exampleFX <- function(z1, z2, z3, z4) { # 绑定输入参数与对应字段名 params <- list(z1 = z1, z2 = z2, z3 = z3, z4 = z4) field_names <- c("x1", "x2", "x3", "x4") names(params) <- field_names # 生成过滤条件:仅保留输入不为0的字段对应的规则 filter_conditions <- imap(params, function(val, field) { if (val != 0) { expr(!!sym(field) == !!val) } }) %>% compact() # 生成分组字段:收集输入不为0的字段名 group_fields <- names(filter_conditions) # 执行过滤+分组统计(这里以取y的第一个值为例,可按需修改统计量) result <- df %>% filter(!!!filter_conditions) %>% {if (length(group_fields) > 0) group_by(., across(all_of(group_fields))) else .} %>% summarise(y_result = first(y)) # 可替换为sum(y), mean(y)等 return(result$y_result) }
测试验证
exampleFX(0,1,0,1) # 返回4,符合预期
说明
- 用
imap遍历参数,生成对应过滤表达式,compact()移除输入为0的字段对应的空规则; !!!用于将多个过滤条件展开传入filter,实现多条件同时过滤;- 分组部分判断是否有分组字段,避免无分组时的错误;
- 统计量可根据需求修改,比如求和
sum(y)、均值mean(y)等。
内容的提问来源于stack exchange,提问作者user23356728
相关产品推荐
相关产品推荐

