如何在自定义函数的公式中正确使用quosures解引用变量?
我明白你在自定义数据处理函数里用quosures传递变量名到公式时遇到了问题——这在使用tidyeval工具时确实是个常见的小坑!让我帮你把代码修正过来,顺便解释下正确的用法。
解决Quosures在公式中解引用的问题
首先,我猜你原本的函数是想按分组变量拟合模型,再用broom处理结果对吧?你的核心问题是在公式里正确解引用quosures,其实用!!(bang-bang操作符)就可以,但要注意公式里的写法,以及结合purrr::map的正确方式。
修正后的完整函数
library(dplyr) library(broom) library(purrr) library(tidyr) foo <- function(mydata, dv, iv, group_var) { # 捕获输入的变量名作为quosures dv <- enquo(dv) iv <- enquo(iv) group_var <- enquo(group_var) mydata %>% group_by(!!group_var) %>% nest() %>% # 对每个分组的数据拟合模型,公式里解引用quosures mutate(model = map(data, ~ lm(!!dv ~ !!iv, data = .x)), # 用broom整理模型的系数结果 tidied = map(model, tidy), # 用broom整理模型的整体统计量 glanced = map(model, glance)) %>% # 展开整理后的系数结果 unnest(tidied) %>% # 提取需要的列,包括分组变量和模型统计量 select(!!group_var, term, estimate, p.value, r.squared = glanced$r.squared) }
关键步骤解释
- 捕获变量:用
enquo()把函数的参数转换成quosures,这样我们就能在后续的tidyverse函数里安全地引用这些变量名,避免命名冲突。 - 公式中的解引用:在
lm()的公式里,直接用!!dv ~ !!iv就能把quosures转换成对应的列名,R会正确识别这是数据框里的变量。 - 结合map使用:在
map()里,我们用.x指代每个分组的子集数据集,把解引用后的公式和数据一起传递给lm()即可完成分组建模。
测试示例
用mtcars数据集测试这个函数,看看效果:
test_result <- foo(mtcars, mpg, hp, cyl) print(test_result)
运行后你会得到按cyl(气缸数)分组,每个组里mpg(油耗)对hp(马力)回归的系数、p值和R平方值。
更简洁的写法(使用{{ }})
如果你用的是dplyr 1.0.0及以上版本,可以用{{ }}(curly-curly操作符)简化代码,它会自动完成quosures的捕获和解引用,不需要手动调用enquo()和!!:
foo_simpler <- function(mydata, dv, iv, group_var) { mydata %>% group_by({{ group_var }}) %>% nest() %>% mutate(model = map(data, ~ lm({{ dv }} ~ {{ iv }}, data = .x)), tidied = map(model, tidy), glanced = map(model, glance)) %>% unnest(tidied) %>% select({{ group_var }}, term, estimate, p.value, r.squared = glanced$r.squared) }
这个版本的代码更简洁,功能和之前的完全一致,推荐优先使用这种写法,减少手动处理quosures的麻烦。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

