【问题标题】:Functional programming with dplyr使用 dplyr 进行函数式编程
【发布时间】:2018-05-04 08:22:21
【问题描述】:

寻找一种更有效/更优雅的方式将多个参数传递给一个组——通过在使用 dplyr 的函数中使用非标准评估。我不想使用 ... 运算符,而是单独指定函数。

我的具体用例是一个函数,它接受一个数据框并创建一个语法更简单的 ggplot 对象。这是我想用我的函数自动化的代码示例:

# create data frame
my_df <- data.frame(month = sample(1:12, 1000, replace = T),
                    category = sample(head(letters, 3), 1000, replace = T),
                    approved = as.numeric(runif(1000) < 0.5))

my_df$converted <- my_df$approved * as.numeric(runif(1000) < 0.5)

my_df %>%
  group_by(month, category) %>%
  summarize(conversion_rate = sum(converted) / sum(approved)) %>%
  ggplot + geom_line(aes(x = month, y = conversion_rate, group = category, 
  color = category))

我想将 group_by、summarize、ggplot 和 geom_line 组合成一个简单的函数,我可以提供 x、y 和组,并让它在后台执行所有脏活。以下是我的工作内容:

# create the function that does the grouping and plotting
plot_lines <- function(df, x, y, group) {

  x <- enquo(x)
  group <- enquo(group)
  group_bys <- quos(!! x, !! group)

  df %>%
    group_by(!!! group_bys) %>%
    my_smry %>%
    ggplot + geom_line(aes_(x = substitute(x), y = substitute(y), 
    group = substitute(group), color = substitute(group)))
}

# create a function to do the summarization
my_smry <- function(x) {
  x %>% 
    summarize(conversion_rate = sum(converted) / sum(approved))
}

# use my function
my_df %>% 
  plot_lines(x = month, y = conversion_rate, group = category)

我觉得 group_by 的处理很不优雅:用enquo 引用xgroup,然后在另一个引用函数quos 中用!! 取消引用它们,然后用@ 重新取消引用它们987654328@ 在下一行,但这是我唯一能够开始工作的事情。有没有更好的方法来做到这一点?

另外,有没有办法让 ggplot 采用 !! 而不是 substitute?我在做的事情感觉不一致。

【问题讨论】:

    标签: r ggplot2 dplyr dsl rlang


    【解决方案1】:

    你可以像这样直接eval.parent(substitute(...))。作为基础 R,它在 R 中始终如一地工作并且很容易做到。甚至可以使用普通的aes

    plot_lines <- function(df, x, y, group) eval.parent(substitute(
       df %>%
          group_by(x, group) %>%
          my_smry %>%
          ggplot + geom_line(aes(x = x, y = y, group = group, color = group))
    ))
    plot_lines(my_df, month, conversion_rate, category)
    

    【讨论】:

      【解决方案2】:

      问题是 ggplot 尚未更新以处理 quosures,因此您必须将表达式传递给它,您可以使用 rlang::quo_expr 从 quosures 创建:

      library(tidyverse)
      set.seed(47)
      
      my_df <- data_frame(month = sample(1:12, 1000, replace = TRUE),
                          category = sample(head(letters, 3), 1000, replace = TRUE),
                          approved = as.numeric(runif(1000) < 0.5),
                          converted = approved * as.numeric(runif(1000) < 0.5))
      
      plot_lines <- function(df, x, y, group) {
          x <- enquo(x)
          y <- enquo(y)
          group <- enquo(group)
      
          df %>%
              group_by(!! x, !! group) %>%
              summarise(conversion_rate = sum(converted) / sum(approved)) %>%
              ggplot(aes_(x = rlang::quo_expr(x), 
                          y = rlang::quo_expr(y), 
                          color = rlang::quo_expr(group))) + 
              geom_line()
      }
      
      my_df %>% plot_lines(month, conversion_rate, category)
      

      但是,请记住,ggplot 几乎不可避免地会从lazyeval 更新为 rlang,因此虽然这个接口可能会继续工作,但不久之后可能会出现一个更简单、更一致的接口。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-11-17
        • 2014-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-23
        • 2023-03-08
        • 1970-01-01
        相关资源
        最近更新 更多