在 group_by() %>% mutate() 函数调用中使用带引号的变量

Rob*_*Tan 4 r dplyr tidyeval

可重现的例子

cats <-
  data.frame(
    name = c(letters[1:10]),
    weight = c(rnorm(5, 10, 1), rnorm(5, 20, 3)),
    type = c(rep("not_fat", 5), rep("fat", 5))
  )

get_means <- function(df, metric, group) {
  df %>%
    group_by(.[[group]]) %>%
    mutate(mean_stat = mean(.[[metric]])) %>%
    pull(mean_stat) %>%
    unique()
}

get_means(cats, metric = "weight", group = "type")
Run Code Online (Sandbox Code Playgroud)

我试过的

我希望得到两个值,而不是我得到一个值。看来 groupby 失败了。

我尝试了所有方法,包括使用 quo()、eval() 和替换 ()、UQ()、!! 以及许多其他方法来尝试使 group_by() 中的内容起作用。

这看起来非常简单,但我无法弄清楚。

代码推理

将变量放在引号中的决定是因为我在 ggplot aes_string() 调用中使用它们。我在函数中排除了 ggplot 代码以简化代码,否则会很容易,因为我们可以使用标准评估。

eip*_*i10 5

我认为在 tidyeval 框架中执行此操作的“预期”方法是将参数作为名称(而不是字符串)输入,然后使用enquo(). ggplot2了解 tidy 评估运算符,因此这也适用ggplot2

首先,让我们调整dplyr示例中的汇总函数:

library(tidyverse)
library(rlang)

get_means <- function(df, metric, group) {

  metric = enquo(metric)
  group = enquo(group)

  df %>%
    group_by(!!group) %>%
    summarise(!!paste0("mean_", as_label(metric)) := mean(!!metric))
}

get_means(cats, weight, type)
Run Code Online (Sandbox Code Playgroud)
  type    mean_weight
1 fat            20.0
2 not_fat        10.2
Run Code Online (Sandbox Code Playgroud)
get_means(iris, Petal.Width, Species)
Run Code Online (Sandbox Code Playgroud)
  Species    mean_Petal.Width
1 setosa                0.246
2 versicolor            1.33 
3 virginica             2.03
Run Code Online (Sandbox Code Playgroud)

现在添加 ggplot:

get_means <- function(df, metric, group) {

  metric = enquo(metric)
  group = enquo(group)

  df %>%
    group_by(!!group) %>%
    summarise(mean_stat = mean(!!metric)) %>% 
    ggplot(aes(!!group, mean_stat)) + 
      geom_point()
}

get_means(cats, weight, type)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

我不确定您想到的是哪种类型的图,但您可以使用 tidy 评估绘制数据和汇总值。例如:

plot_func = function(data, metric, group) {

  metric = enquo(metric)
  group = enquo(group)

  data %>% 
    ggplot(aes(!!group, !!metric)) + 
      geom_point() +
      geom_point(data=. %>% 
                   group_by(!!group) %>%
                   summarise(!!metric := mean(!!metric)),
                 shape="_", colour="red", size=8) + 
      expand_limits(y=0) +
      scale_y_continuous(expand=expand_scale(mult=c(0,0.02)))
}

plot_func(cats, weight, type)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

仅供参考,您可以允许该函数使用...参数和enquos代替enquo(这也需要使用!!!(unquote-splice) 而不是!!(unquote))来获取任意数量的分组变量(包括无)。

get_means <- function(df, metric, ...) {

  metric = enquo(metric)
  groups = enquos(...)

  df %>%
    group_by(!!!groups) %>%
    summarise(!!paste0("mean_", quo_text(metric)) := mean(!!metric))
}
Run Code Online (Sandbox Code Playgroud)
get_means(mtcars, mpg, cyl, vs)
Run Code Online (Sandbox Code Playgroud)
    cyl    vs mean_mpg
1     4     0     26  
2     4     1     26.7
3     6     0     20.6
4     6     1     19.1
5     8     0     15.1
Run Code Online (Sandbox Code Playgroud)
get_means(mtcars, mpg)
Run Code Online (Sandbox Code Playgroud)
  mean_mpg
1     20.1
Run Code Online (Sandbox Code Playgroud)

  • 不错的答案!请注意,`quo_text()` 在这种情况下是不合适的。这是一个多行解析器。您可以使用 `as_label()` 或 `as_name()` 代替,它们保证返回单行字符串。后者检查它的输入是变量名而不是函数调用,这在许多情况下是合适的。这里`as_label()` 会很好,因为你的函数接受变量的内联转换,例如你可以传递`get_means(mtcars, mpg * 100)`。 (2认同)