【问题标题】:Writing own function using dplyr and group_by - how to continue with changed column names使用 dplyr 和 group_by 编写自己的函数 - 如何继续更改列名
【发布时间】:2017-07-31 04:07:49
【问题描述】:

我想制作一个表格以列出观察次数,并按两个变量分组。这个代码工作正常。但是,在尝试将其转换为函数时遇到了问题。

我正在使用 dplyr_0.7.2

使用 mtcars 的示例:

函数外的表格代码:这可行

library(tidyverse) 

tab1 <- mtcars %>% count(cyl) %>% rename(Total = n) 

tab2 <- mtcars %>%
  group_by(cyl, gear) %>% count %>% 
  spread(gear, n)

tab <- full_join(tab1, tab2, by = "cyl")
tab


# This is the output (which is what I want)

A tibble: 3 x 5
cyl Total   `3`   `4`   `5`
<dbl> <int> <int> <int> <int>
1     4    11     1     8     2
2     6     7     2     4     1
3     8    14    12    NA     2

试图把它放到一个函数中

tab1 的功能:这可行

count_by_two_groups_A <- function(df, var1){
  var1 <- enquo(var1)
  tab1 <- df %>% count(!!var1) %>% rename(Total = n)
  tab1
} 

count_by_two_groups_A(mtcars, cyl) 

A tibble: 3 x 2
cyl Total
<dbl> <int>
1     4    11
2     6     7
3     8    14

tab2 的第一部分的功能:它可以工作到现在,但是...

count_by_two_groups_B <- function(df, var1, var2){

  var1 <- enquo(var1)
  var2 <- enquo(var2)

  tab2 <- df %>% group_by((!!var1), (!!var2)) %>% count
  tab2
} 

count_by_two_groups_B(mtcars, cyl, gear)

A tibble: 8 x 3
Groups:   (cyl), (gear) [8]
 `(cyl)` `(gear)`     n
 <dbl>    <dbl> <int>
 1       4        3     1
 2       4        4     8
 3       4        5     2
 4       6        3     2
 5       6        4     4
 6       6        5     1
 7       8        3    12
 8       8        5     2

列名已更改为 (cyl) 和 (gear)。既然列名已更改,我似乎无法弄清楚如何继续使用 spread() 和 full_join() (或使用新列名的任何其他内容)。 IE。我不知道如何以 tidyeval 方式指定新的列名,以便能够继续。我尝试了各种方法,但都没有成功。

【问题讨论】:

    标签: r dplyr rlang tidyeval


    【解决方案1】:

    在 tidyeval 上下文中设置名称的常用方法是使用定义运算符:=。它看起来像这样:

    df %>%
      group_by(
        !! nm1 := !! var1,
        !! nm2 := !! var2
      ) %>%
      count()
    

    为此,您需要从var1 中提取nm1。不幸的是,我还没有一个简单的方法来去掉括号。我认为在即将到来的函数ensym() 中执行此操作是有意义的(它捕获符号而不是 quosures,如果您提供调用,则会发出错误)。我在这里提交了一张票:https://github.com/tidyverse/rlang/issues/223

    幸运的是,我们在这里有两个简单的解决方案。首先请注意,您不需要括号。仅当捕获的表达式中涉及其他运算符时才需要它们。例如。在这些情况下:

    (!! var) / avg
    (!! var) < value
    

    在这种情况下,如果您省略括号,!! 将尝试取消引用整个表达式,而不仅仅是一个符号。另一方面,在您的函数中没有运算符,因此您可以安全地取消引用而不包含:

    count_by_two_groups_B <- function(df, var1, var2) {
      var1 <- enquo(var1)
      var2 <- enquo(var2)
    
      df %>%
        group_by(!! var1, !! var2) %>%
        count()
    }
    

    最后,您可以通过允许可变数量的参数来使您的函数更通用。这更容易实现,因为点是转发的,所以不需要捕获和取消引用。只需将它们传递给group_by()

    count_by <- function(df, ...) {
      df %>%
        group_by(...) %>%
        count()
    }
    

    【讨论】:

      【解决方案2】:

      我可以使它与 NSE(非标准评估)一起使用。 tidyverse 无法做到这一点,因为我没有安装它,也没有安装。

      这是一个工作代码:

      library(dplyr)
      library(tidyr)
      
      count_by_two_groups_B <- function(df, var1, var2){
      
       # var1 <- enquo(var1)
       # var2 <- enquo(var2)
      
        tab2 <- df %>% group_by_(var1, var2) %>% summarise(n = n() )  %>%spread(gear, n)
      
        tab2
      } 
      
      count_by_two_groups_B(mtcars, 'cyl', 'gear')
      

      结果:

      # A tibble: 3 x 4
      # Groups:   cyl [3]
          cyl   `3`   `4`   `5`
      * <dbl> <int> <int> <int>
      1     4     1     8     2
      2     6     2     4     1
      3     8    12    NA     2
      

      【讨论】:

        【解决方案3】:

        这是使用 dplyr 或 tidyverse 似乎过度的情况之一。有一些基本功能可以做到这一点...table 并以长格式生成结果,as.dataframe

        as.data.frame( with(mtcars, table(cyl,gear)) , responseName="Total")
        #--------
          cyl gear Total
        1   4    3     1
        2   6    3     2
        3   8    3    12
        4   4    4     8
        5   6    4     4
        6   8    4     0
        7   4    5     2
        8   6    5     1
        9   8    5     2
        

        这将是一种 dplyr 方法:

        mtcars %>% group_by(cyl,gear) %>% summarise(Total=n())
        #----
        # A tibble: 8 x 3
        # Groups:   cyl [?]
            cyl  gear Total
          <dbl> <dbl> <int>
        1     4     3     1
        2     4     4     8
        3     4     5     2
        4     6     3     2
        5     6     4     4
        6     6     5     1
        7     8     3    12
        8     8     5     2
        

        如果问题是如何将其作为表格对象获取(认为这可能是您使用 spread 的目标,那么只需:

        with(mtcars, table(cyl,gear))
        

        【讨论】:

        • 我认为在脚本中保持统一的风格是有价值的。此外,如果你想让 tidyeval 工作,最好从小函数开始。由于这些原因,回答最初的问题是有意义的。
        • 我的部分问题是原始问题不够清晰。
        猜你喜欢
        • 1970-01-01
        • 2014-06-04
        • 2017-07-12
        • 2018-11-02
        • 1970-01-01
        • 2021-01-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多