【问题标题】:How to get specific values out of a list of values passed to one argument of a UDF with tidyeval如何使用 tidyeval 从传递给 UDF 的一个参数的值列表中获取特定值
【发布时间】:2021-10-07 20:40:55
【问题描述】:

我使用tidyeval 编写了一个短函数,该函数将分组变量作为输入,对 mtcars 数据集进行分组并计算每组的出现次数:

test_function <- function(grps){
  mtcars %>% 
    group_by(across({{grps}})) %>% 
    summarise(Count = n())
}

test_function(grps = c(cyl, gear))

---

    cyl  gear Count
  <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 的小计列。那么有多少汽车有 4(6,8)个气缸?结果应该是这样的:

test_function(grps = c(cyl, gear), subtotalrows = TRUE)    ### example function execution

---

          cyl     gear Count
        <dbl>    <dbl> <int>
 1        4        3     1
 2        4        4     8
 3        4        5     2
 4        4    total    11
 5        6        3     2
 6        6        4     4
 7        6        5     1
 8        6    total     7
 9        8        3    12
10        8        5     2
11        8    total    14

在这种情况下,我正在寻找的小计列可以简单地使用相同的函数但少一个分组变量来生成:

test_function(grps = cyl)

---

    cyl Count
  <dbl> <int>
1     4    11
2     6     7
3     8    14

但由于我不想单独使用该函数(甚至不确定这在 R 中是否可行),我想采用不同的方法:据我所知,最好 (and only way)到目前为止,创建小计行是通过独立计算它们和then binding them row wise to the grouped table (i.e.: rbind, bind_rows)。在我的情况下,这意味着只取第一个分组变量,创建小计行,然后将它们绑定到表。但这里是我对 tidyeval 语法有问题的地方。这是我想在函数中执行的伪代码:

test_function <- function(grps, subtotalrows = TRUE){
  
  
  grouped_result <- mtcars %>% 
    group_by(across({{grps}})) %>% 
    summarise(Count = n())
  
  if(subtotalrows == FALSE){
    
    return(grouped_result)
    
  } else {
    
    #pseudocode
    
    group_for_subcalculation <- grps[[1]] #I want the first element of the grps argument
    
    subtotal_result <- mtcars %>% 
      group_by(across({{group_for_subcalculation}})) %>% 
      summarise(Count = n()) %>% 
      mutate(grps[[2]] := "total") %>% 
      arrange(grps[[1]], grps[[2]], Count)
  
    return(rbind(grouped_result, subtotal_result))
  }
}

所以,有两个问题:我很好奇如何提取grps 传递的第一列名称并在以下代码中使用它。其次,这个伪代码示例特定于 grps 传递的 2 列。想象一下,我什至想通过 3 或更多。你会怎么做(循环)?

【问题讨论】:

    标签: r dplyr tidyverse user-defined-functions tidyeval


    【解决方案1】:

    试试这个功能-

    library(dplyr)
    
    test_function <- function(grps, subtotalrows = TRUE){
      grouped_data <- mtcars %>%  group_by(across({{grps}}))
      groups <- group_vars(grouped_data)
      col_to_change <- groups[length(groups)] #Last value in grps
      grouped_result <- grouped_data %>% summarise(Count = n())
      
      if(!subtotalrows) return(grouped_result)
      else {
        result <- grouped_result %>%
          summarise(Count = sum(Count), 
                    !!col_to_change := 'Total') %>%
          bind_rows(grouped_result %>%
                      mutate(!!col_to_change := as.character(.data[[col_to_change]]))) %>%
          select(all_of(groups), Count) %>%
          arrange(across(all_of(groups)))
      }
      return(result)
    }
    

    测试功能-

    test_function(grps = c(cyl, gear))
    
    # A tibble: 11 x 3
    #     cyl gear  Count
    #   <dbl> <chr> <int>
    # 1     4 3         1
    # 2     4 4         8
    # 3     4 5         2
    # 4     4 Total    11
    # 5     6 3         2
    # 6     6 4         4
    # 7     6 5         1
    # 8     6 Total     7
    # 9     8 3        12
    #10     8 5         2
    #11     8 Total    14
    
    test_function(grps = c(cyl, gear), FALSE)
    
    #    cyl  gear Count
    #  <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
    

    对于 3 个变量 -

    test_function(grps = c(cyl, gear, carb))
    
    #    cyl  gear carb  Count
    #   <dbl> <dbl> <chr> <int>
    # 1     4     3 1         1
    # 2     4     3 Total     1
    # 3     4     4 1         4
    # 4     4     4 2         4
    # 5     4     4 Total     8
    # 6     4     5 2         2
    # 7     4     5 Total     2
    # 8     6     3 1         2
    # 9     6     3 Total     2
    #10     6     4 4         4
    #11     6     4 Total     4
    #12     6     5 6         1
    #13     6     5 Total     1
    #14     8     3 2         4
    #15     8     3 3         3
    #16     8     3 4         5
    #17     8     3 Total    12
    #18     8     5 4         1
    #19     8     5 8         1
    #20     8     5 Total     2
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-07
      • 1970-01-01
      • 2013-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多