写函数时经常遇到需要传递外部变量到已知函数的情况,如下:
> my_func <- function(data, x, y, group) {
ggplot(data) +
geom_boxplot(aes(x = x, y = y, group = group))
}
> my_func(mtcars, x = "cyl", y = "mpg", group = "cyl")
这时候出的图和你想象中不太一样吧,准确来讲就是错的

odd
在之前选择数据框中的列的时候就有类似的烦恼,但我没深究,曲线救国直接
[]取的子集,但函数不行啊,还是得探个究竟解决办法如下:
> my_func <- function(data, x, y, group) {
ggplot(data) +
geom_boxplot(aes(x = !!sym(x), y = !!sym(y), group = !!sym(group)))
}
> my_func(mtcars, x = "cyl", y = "mpg", group = "cyl")

正常了吧
不放心的话,可以看看正常代码出的图:
> ggplot(mtcars) + geom_boxplot(aes(x = cyl, y = mpg, group = cyl))

一模一样
实质上是变量引用的问题,之前以为是引号导致的,用
noquote()去掉之后也不行,as.symbol()它也不行,{{}}它也不行。。stackoverflow上找到了答案,用!!sym()它的详细用法如下:
nse-force {rlang} R Documentation
Force parts of an expression
Description
It is sometimes useful to force early evaluation of part of an expression before it gets fully evaluated. The tidy eval framework provides several forcing operators for different use cases.
The bang-bang operator !! forces a single object. One common case for !! is to substitute an environment-variable (created with <-) with a data-variable (inside a data frame).
library(dplyr)
# The environment variable `var` refers to the data-variable
# `height`
var <- sym("height")
# We force `var`, which substitutes it with `height`
starwars %>%
summarise(avg = mean(!!var, na.rm = TRUE))
The big-bang operator !!! forces-splice a list of objects. The elements of the list are spliced in place, meaning that they each become one single argument.
vars <- syms(c("height", "mass"))
# Force-splicing is equivalent to supplying the elements separately
starwars %>% select(!!!vars)
starwars %>% select(height, mass)
The curly-curly operator {{ }} for function arguments is a bit special because it forces the function argument and immediately defuses it. The defused expression is substituted in place, ready to be evaluated in another context, such as the data frame.
In practice, this is useful when you have a data-variable in an env-variable (such as a function argument).
# Force-defuse all function arguments that might contain
# data-variables by embracing them with {{ }}
mean_by <- function(data, by, var) {
data %>%
group_by({{ by }}) %>%
summarise(avg = mean({{ var }}, na.rm = TRUE))
}
# The env-variables `by` and `var` are forced but defused.
# The data-variables they contain are evaluated by dplyr later on
# in data context.
iris %>% mean_by(by = Species, var = Sepal.Width)
Use qq_show() to experiment with forcing operators. qq_show() defuses its input, processes all forcing operators, and prints the result with expr_print() to reveal objects inlined in the expression by the forcing operators.
在写公式的时候,有时候单纯用paste连接不行,需要as.formula()
vegan::adonis(as.formula(paste("t(otuTab)", "~", var)), sub_design, method = method, permutations = 999) # Adonis test