如何创建可传递列参数的dplyr函数?
解决dplyr函数中传递列名参数的问题
我最近尝试写一个内部调用dplyr的函数,想要把列名作为参数传递进去,结果试了各种方法都踩坑:用enquo搭配!!、尝试用!!as_label解决前一步的错误、换group_by_替代group_by,甚至试了curly operator,全都没成功。我的测试数据和代码如下:
userMaster <- structure(list(user_id = c(1, 2, 3, 4, 5), city = structure(c(5L, 5L, 8L, 9L, 10L), .Label = c("Austin", "Boise", "Boston", "Chicago", "Dallas", "Denver", "Detroit", "Kansas City", "Las Vegas", "Los Angeles", "Manhattan", "Miami", "Minneapolis", "New York City", "Oklahoma City", "Omaha", "Phoenix", "Saint Louis", "San Francisco", "Washington DC" ), class = "factor"), source = structure(c(2L, 2L, 2L, 2L, 2L ), .Label = c("Adwords", "Organic", "Search Ads"), class = "factor")), row.names = c(NA, 5L), class = "data.frame") userCount <- function(table, metric){ col_enquo <- enquo(metric) summary <- table %>% select(!! (col_enquo), source, user_id) %>% group_by_(!! (col_enquo), source) %>% summarise(users = n_distinct(user_id)) %>% left_join(table %>% group_by(source) %>% summarise(total = n_distinct(user_id))) %>% mutate(users/total) return(summary) } genderDemo <- userCount(userMaster, city)
遇到的错误包括:
Error:
quos(desire)must evaluate to column positions or names, not a list
Error in !as_label(col_enquo) : invalid argument type
Error: Quosures can only be unquoted within a quasiquotation context. # Bad: list(!!myquosure) # Good: dplyr::mutate(data, !!myquosure)
问题根源
核心问题是你混合了dplyr的旧版标准评估语法(比如group_by_这类带下划线的函数)和新版非标准评估语法(enquo+!!),这两种语法体系不能混用,会导致参数解析冲突。另外,现在dplyr的核心函数都已经原生支持非标准评估,不需要切换到旧版函数。
修正后的解决方案
现在dplyr推荐用更简洁的{{}}(curly curly)语法,它是enquo+!!的语法糖,用起来更直观。当然你也可以继续用enquo+!!的组合,只要统一语法即可。
方案1:使用{{}}语法(推荐)
userCount <- function(table, metric){ summary <- table %>% select({{metric}}, source, user_id) %>% group_by({{metric}}, source) %>% summarise(users = n_distinct(user_id), .groups = "drop") %>% left_join(table %>% group_by(source) %>% summarise(total = n_distinct(user_id), .groups = "drop")) %>% mutate(ratio = users/total) # 给计算列命名,避免默认的V1列名 return(summary) } # 调用测试 genderDemo <- userCount(userMaster, city) print(genderDemo)
方案2:使用enquo+!!语法
userCount <- function(table, metric){ col_enquo <- enquo(metric) summary <- table %>% select(!!col_enquo, source, user_id) %>% group_by(!!col_enquo, source) %>% summarise(users = n_distinct(user_id), .groups = "drop") %>% left_join(table %>% group_by(source) %>% summarise(total = n_distinct(user_id), .groups = "drop")) %>% mutate(ratio = users/total) return(summary) } genderDemo <- userCount(userMaster, city) print(genderDemo)
额外优化说明
- 给
mutate的计算列添加了ratio作为列名,避免生成默认的V1列,让结果更清晰; - 在
summarise中加入.groups = "drop",可以消除dplyr的分组残留警告,同时让输出结果更整洁; - 彻底弃用了
group_by_这类旧版函数,统一使用新版非标准评估语法,避免语法冲突。
内容的提问来源于stack exchange,提问作者Krithi07
相关产品推荐
相关产品推荐

