在函数中传递输入参数 | R

写函数时经常遇到需要传递外部变量到已知函数的情况,如下:

> 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
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容