使用dplyr进行功能编程

Aar*_*ley 8 dsl r ggplot2 dplyr rlang

通过使用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))
Run Code Online (Sandbox Code Playgroud)

我想将group_by,summarize,ggplot和geom_line组合成一个简单的函数,我可以提供x,y和group,并让它执行所有脏工作.这就是我的工作:

# 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)
Run Code Online (Sandbox Code Playgroud)

我觉得group_by处理非常不优雅:引用xgroup使用enquo,然后!!在另一个引用函数中取消引用它们quos,只是!!!在下一行重新引用它们,但这是我唯一能够工作的东西.有一个更好的方法吗?

另外,有没有办法让ggplot !!代替substitute?我正在做的事情感觉不一致.

G. *_*eck 7

你可以直接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)
Run Code Online (Sandbox Code Playgroud)


ali*_*ire 6

问题是ggplot还没有更新来处理quosures,所以你必须传递它的表达式,你可以使用以下命令创建它们rlang::quo_expr:

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)
Run Code Online (Sandbox Code Playgroud)

但是,请记住,ggplot几乎不可避免地会从lazyeval更新为rlang,所以虽然这个界面可能会继续工作,但很快就会有一个更简单,更一致的界面.