代码之家  ›  专栏  ›  技术社区  ›  Aaron Cooley

使用dplyr的函数编程

  •  8
  • Aaron Cooley  · 技术社区  · 6 年前

    通过在使用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、SUMMARE、ggplot和geom_线组合成一个简单的函数,我可以为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)
    

    我觉得处理group_很不雅观:引用 x group 具有 enquo ,然后用 !! 在另一个引用函数内 quos ,只需用 !!! 在下一行,但这是我唯一能去工作的事情。有更好的方法吗?

    还有,有什么方法可以让ggplot采用吗 !! 而不是 substitute ? 我现在做的感觉不一致。

    2 回复  |  直到 6 年前
        1
  •  7
  •   G. Grothendieck    6 年前

    你可以直奔终点 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
  •  6
  •   alistaire    6 年前

    问题是ggplot尚未更新以处理quosure,因此您必须传递它的表达式,您可以使用从quosure创建的表达式 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)
    

    然而,请记住,ggplot几乎不可避免地会从lazyeval更新到rlang,因此虽然此接口可能会继续工作,但很快可能会有一个更简单、更一致的接口。