dplyr中为何case_when需同时用!!与as.symbol而select无需?
为什么
case_when需要!!+as.symbol,而select只需要!!? 核心原因是两个函数的参数解析逻辑完全不同:
select的逻辑:select是为选择列设计的,基于tidyselect框架,它本身就能直接识别字符串格式的列名。当你用!!column_name时,只是把变量里存的字符串"disp"解引用出来,select拿到这个字符串后,能自动对应到数据框里的disp列,不需要额外转换类型。
其实更规范的写法是用all_of(),比如select(mpg, all_of(column_name)),这是tidyselect推荐的处理字符串列名的方式,可读性更强。case_when的逻辑:case_when是用来构建条件判断表达式的,它需要的是像disp > 300这样的语法——这里的disp是代表列的符号(symbol),不是字符串"disp"。如果直接写!!column_name > 300,相当于拿字符串"disp"和300做比较,这在R里会返回错误的逻辑结果(字符串和数字的比较规则不符合需求)。
所以必须先用as.symbol(column_name)把字符串转换成对应列的符号引用,再用!!解引用,让case_when把它识别成数据框里的disp列,才能正确执行列值和300的比较。
举个直观的错误对比:
- 错误写法
!!column_name > 300实际执行的是"disp" > 300,返回的是单个FALSE,不是每行的判断结果。 - 正确写法
!!as.symbol(column_name) > 300实际执行的是disp > 300,会对每行的disp值做判断,这才是我们要的逻辑。
另外补充:现在dplyr新版本里,推荐用.data代词来替代这种符号操作,写法更简单直观:
mtcars_small = mtcars %>% dplyr::select(mpg, all_of(column_name)) %>% mutate(comparison_condition = case_when( .data[[column_name]] > 300 ~ 'big', .data[[column_name]] < 100 ~ 'small', TRUE ~ NA_character_ ))
这种方式不需要处理符号和解引用,代码可读性更高。
内容的提问来源于stack exchange,提问作者Al Bro
相关产品推荐
相关产品推荐

