【问题标题】:pass function arguments to both dplyr and ggplot将函数参数传递给 dplyr 和 ggplot
【发布时间】:2018-01-08 10:30:54
【问题描述】:

我对如何将函数参数传递给 dplyr 和 ggplot 代码感到困惑。 我正在使用最新版本的 dplyr 和 ggplot2 这是我生成条形图的代码(清晰度与平均价格)

diamond.plot<- function (data, group, metric) {
    group<- quo(group)
    metric<- quo(metric)
    data() %>% group_by(!! group) %>%
           summarise(price=mean(!! metric)) %>% 
           ggplot(aes(x=!! group,y=price))+
           geom_bar(stat='identity') 
}

diamond.plot(diamonds, group='clarity', metric='price')

错误:

Error in UseMethod("group_by_") : no applicable method for 'group_by_' applied to an object of class "packageIQR"

对于最新版本的 dplyr,带下划线的动词_() 已被轻微弃用。看来我们应该使用quosures。

我的问题:

  • 有人可以澄清当前的最佳实践吗?
  • 上面的代码有什么问题? (请不要使用下划线 dplyr 动词..)

  • 在 ggplot 中,我知道我们可以使用 aes_string(),但在我的例子中,aes 中只有一个参数是从函数参数传递的。

提前致谢。

【问题讨论】:

    标签: r ggplot2 dplyr rlang tidyeval


    【解决方案1】:

    ggplot2 v3.0.0 现在完全支持整洁的评估,因此不再需要使用 aes_aes_string

    library(rlang)
    library(tidyverse)
    
    diamond_plot <- function (data, group, metric) {
        quo_group  <- sym(group)
        quo_metric <- sym(metric)
    
        data %>%
            group_by(!! quo_group) %>%
            summarise(price = mean(!! quo_metric)) %>%
            ggplot(aes(x = !! quo_group, y = !! quo_metric)) +
            geom_col()
    }
    
    diamond_plot(diamonds, "clarity", "price")
    

    reprex package (v0.2.0) 于 2018 年 4 月 16 日创建。

    【讨论】:

      【解决方案2】:

      对我来说,解决这个问题的最“整洁”的方法是 quo_nameaes_string 函数的组合。避免使用像aes_ 这样的尾随下划线动词,因为它们已被弃用。

      diamond_plot <- function(data, group, metric) {
        quo_group <- enquo(group)
        str_group <- quo_name(quo_group)
      
        quo_metric <- enquo(metric)
      
        summary <- data %>%
           groupby(!!quo_group) %>%
           summarise(mean = mean(!!quo_metric))
      
        ggplot(summary) +
        geom_bar(aes_string(x = str_group, y = "mean"), stat = "identity")
      }
      
      diamond_plot(diamnonds, clarity, price)
      

      【讨论】:

        【解决方案3】:

        您可以比 Daniel 的解决方案更进一步,以便汇总变量(指标)的名称随输入而变化。

        diamond_plot <- function(data, group, metric) {
            quo_group <- rlang::sym(group)
            quo_metric <- rlang::sym(metric)
            metric_name <- rlang::sym(stringr::str_c("mean_", metric))
            data %>%
                group_by(!!quo_group) %>%
                summarize(!!metric_name := mean(!!quo_metric)) %>%
                ggplot(aes_(x = quo_group, y = metric_name)) +
                geom_bar(stat = 'identity')
        }
        diamond_plot(diamonds, "clarity", "price")
        

        【讨论】:

          【解决方案4】:

          sinQueso 的答案很有希望,但它错过了函数的目的,即适应不同的数据帧。 “price”变量在函数中编码如下:

          summarise(price=mean(!!quo_metric)) %>%
          

          所以这个函数只有在输入变量是“价格”时才会起作用。

          这是一个更好的解决方案,可以用于任何数据框:

          diamond_plot <- function (data, group, metric) {
                  quo_group <- sym(group)
                  quo_metric <- sym(metric)
                  summary <- data %>%
                          group_by(!!quo_group) %>%
                          summarise(mean=mean(!!quo_metric))
                          ggplot(summary, aes_string(x = group, y= "mean")) +
                          geom_bar(stat='identity')
          }
          diamond_plot(diamonds, "clarity", "price")
          

          【讨论】:

            【解决方案5】:

            我认为你还不能采用“正确”的方式,因为 ggplot2 不支持 tidyeval 语法,但它即将到来。

            dplyr 部分代码的最佳做法是:

            library(tidyverse)
            library(rlang)
            
            diamond_data <- function (data, group, metric) {
               quo_group <- enquo(group)
               quo_metric <- enquo(metric)
               data %>%
                 group_by(!!quo_group) %>%
                 summarise(price=mean(!!quo_metric))
            }
            diamond_data(diamonds, clarity, price)
            

            要解决 ggplot2 中不支持 tidyeval 的问题,您可以这样做(注意函数调用中变量周围的引号):

            diamond_plot <- function (data, group, metric) {
                quo_group <- parse_quosure(group)
                quo_metric <- parse_quosure(metric)
                data %>%
                    group_by(!!quo_group) %>%
                    summarise(price=mean(!!quo_metric)) %>%
                    ggplot(aes_(x = as.name(group), y=as.name(metric)))+
                    geom_bar(stat='identity')
            }
            diamond_plot(diamonds, "clarity", "price")
            

            编辑 -- 关注@lionel 的评论:

            diamond_plot <- function (data, group, metric) {
                quo_group <- sym(group)
                quo_metric <- sym(metric)
                data %>%
                    group_by(!!quo_group) %>%
                    summarise(price=mean(!!quo_metric)) %>%
                    ggplot(aes_(x = quo_group, y= quo_metric)) +
                    geom_bar(stat='identity')
            }
            diamond_plot(diamonds, "clarity", "price")
            

            【讨论】:

            • 在这种情况下,我只会使用sym()(或as.name())而不是parse_quosure()
            • 感谢@lionel 的建议,我更新了答案
            • @lionel 你能解释一下为什么你更喜欢 sym() 而不是 parse_quosure 吗?
            • 因为函数需要列名,而不是可能引用上下文对象的符号。解析还会创建表达式,而不仅仅是符号,这在此处不合适。解析任意代码通常是不好的风格。最后,符号可以传递给 dplyr 函数和 aes_() 函数,而 aes_() 不支持 quosures。使用符号而不是 quosured 符号当然假定名称是指列而不是上下文中的对象,这在这里似乎是合理的。
            猜你喜欢
            • 2015-03-14
            • 1970-01-01
            • 2017-06-06
            • 1970-01-01
            • 1970-01-01
            • 2015-07-01
            • 2018-08-05
            相关资源
            最近更新 更多