如何在dplyr非标准求值中避免重复反引用操作?
问题描述
假设我们有如下数据:
tib <- tibble::tibble(x = 1:10)
我们希望编写一个函数,接收列名作为输入,返回添加了多个转换列的tibble,示例代码如下:
library(dplyr) generate_transformations <- function(data, column){ transform <- sym(column) data %>% mutate( sqrt = sqrt(!!transform), recip = 1 / !!transform, log = log(!!transform) ) } # 使用示例: tib %>% generate_transformations('x')
运行后输出:
# A tibble: 10 x 4 x sqrt recip log <int> <dbl> <dbl> <dbl> 1 1 1 1 0 2 2 1.41 0.5 0.693 3 3 1.73 0.333 1.10 4 4 2 0.25 1.39 5 5 2.24 0.2 1.61 6 6 2.45 0.167 1.79 7 7 2.65 0.143 1.95 8 8 2.83 0.125 2.08 9 9 3 0.111 2.20 10 10 3.16 0.1 2.30
提问:有没有办法避免重复对transform变量进行反引用(!!)操作?我知道可以通过临时重命名列再改回的方式实现,但这不是我想要的。我希望能生成一个无需使用!!的变量,比如如下(无法运行的)尝试写法:
generate_transformations <- function(data, column){ transform <- !!sym(column) # 此处无法反引用 :( data %>% mutate( sqrt = sqrt(transform), recip = 1 / transform, log = log(transform) ) }
解决方案
方法1:使用.data代词直接索引
不需要创建符号变量,直接用dplyr提供的.data代词按列名索引目标列,全程不用!!:
generate_transformations <- function(data, column){ data %>% mutate( sqrt = sqrt(.data[[column]]), recip = 1 / .data[[column]], log = log(.data[[column]]) ) }
方法2:用across批量处理转换
如果你的转换逻辑可以统一整理,across是更简洁的方案,还能批量管理所有转换规则:
generate_transformations <- function(data, column){ # 定义所有转换函数,键就是新列名 transform_rules <- list( sqrt = sqrt, recip = \(x) 1/x, log = log ) data %>% mutate(across(all_of(column), transform_rules, .names = "{.fn}")) }
这个方法会自动把每个转换函数的名称作为新列名,输出结果和原需求完全一致,后续要加新转换只需要在transform_rules里加新条目就行。
方法3:用rlang::inject统一注入表达式
如果还是想先定义代表目标列的变量,可以用inject把整个mutate表达式包裹起来,这样虽然还是用到!!,但逻辑上更清晰,也不用反复写:
library(rlang) generate_transformations <- function(data, column){ transform <- sym(column) data %>% inject( mutate( sqrt = sqrt(!!transform), recip = 1 / !!transform, log = log(!!transform) ) ) }
内容的提问来源于stack exchange,提问作者Baraliuh
相关产品推荐
相关产品推荐

