【问题标题】:How do I build a dplyr summarize statement programmatically?如何以编程方式构建 dplyr 汇总语句?
【发布时间】:2021-08-27 03:39:26
【问题描述】:

我正在尝试进行一些 dplyr 编程并且遇到了麻烦。我想 group_by 任意数量的变量(因此,across),然后 summarize 基于任意长度(但长度相同)的向量:

  • 要应用函数的列
  • 要应用的函数
  • 新列的名称

所以,就像在 mapapply 语句中一样,我想执行最终看起来像这样的代码:

data %>%
  group_by(group_column) %>%
  summarize(new_name_1 = function_1(column_1),
  summarize(new_name_2 = function_2(column_2))

这是我想要的一个例子,也是我迄今为止最好的镜头。我知道如果我使用cross,我可以使用names 参数来清理它们,但我不相信cross 是正确的方法。最后,我会将其应用于相当大的数据帧,因此我宁愿不计算额外的列。

想要的结果

mtcars %>%
  group_by(across(c("cyl", "carb"))) %>%
  summarise(across(c("disp", "hp"), list(mean = mean, sd = sd))) %>%
  select(cyl, carb, disp_mean, hp_sd)
#> `summarise()` regrouping output by 'cyl' (override with `.groups` argument)
#> # A tibble: 9 x 4
#> # Groups:   cyl [3]
#>     cyl  carb disp_mean hp_sd
#>   <dbl> <dbl>     <dbl> <dbl>
#> 1     4     1      91.4 16.1 
#> 2     4     2     117.  24.9 
#> 3     6     1     242.   3.54
#> 4     6     4     164.   7.51
#> 5     6     6     145   NA   
#> 6     8     2     346.  14.4 
#> 7     8     3     276.   0   
#> 8     8     4     406.  21.7 
#> 9     8     8     301   NA

我得到了什么

mtcars %>%
  group_by(across(c("cyl", "carb"))) %>%
  summarise(across(c("disp", "hp"), list(mean = mean, sd = sd)))
#> `summarise()` regrouping output by 'cyl' (override with `.groups` argument)
#> # A tibble: 9 x 6
#> # Groups:   cyl [3]
#>     cyl  carb disp_mean disp_sd hp_mean hp_sd
#>   <dbl> <dbl>     <dbl>   <dbl>   <dbl> <dbl>
#> 1     4     1      91.4   21.4     77.4 16.1 
#> 2     4     2     117.    27.1     87   24.9 
#> 3     6     1     242.    23.3    108.   3.54
#> 4     6     4     164.     4.39   116.   7.51
#> 5     6     6     145     NA      175   NA   
#> 6     8     2     346.    43.4    162.  14.4 
#> 7     8     3     276.     0      180    0   
#> 8     8     4     406.    57.8    234   21.7 
#> 9     8     8     301     NA      335   NA

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    在不同的列上有不同的功能,一个选项是使用collap from collapse

    library(collapse)
    collap(mtcars, ~ cyl + carb, custom = list(fmean = 4, fsd = 5))
    

    -输出

    cyl   disp        hp carb
    1   4  91.38 16.133815    1
    2   4 116.60 24.859606    2
    3   6 241.50  3.535534    1
    4   6 163.80  7.505553    4
    5   6 145.00        NA    6
    6   8 345.50 14.433757    2
    7   8 275.80  0.000000    3
    8   8 405.50 21.725561    4
    9   8 301.00        NA    8
    

    或者可以用match动态生成索引

    collap(mtcars, ~ cyl + carb, custom = list(fmean =
       match('disp', names(mtcars)), fsd = match('hp', names(mtcars))))
    

    使用tidyverse,一个选项是循环遍历感兴趣的列名和map2 中的函数并稍后进行连接

    library(dplyr)
    library(purrr)
    library(stringr)
    map2(c("disp", "hp"), c("mean", "sd"), ~
       mtcars %>%
          group_by(across(c('cyl', 'carb'))) %>% 
          summarise(across(all_of(.x), match.fun(.y), 
             .names = str_c("{.col}_", .y)), .groups = 'drop')) %>% 
        reduce(inner_join)
    

    -输出

    # A tibble: 9 x 4
        cyl  carb disp_mean hp_sd
      <dbl> <dbl>     <dbl> <dbl>
    1     4     1      91.4 16.1 
    2     4     2     117.  24.9 
    3     6     1     242.   3.54
    4     6     4     164.   7.51
    5     6     6     145   NA   
    6     8     2     346.  14.4 
    7     8     3     276.   0   
    8     8     4     406.  21.7 
    9     8     8     301   NA   
    

    【讨论】:

    • tidyverse-解决方案很棒。我想不通。 :-)
    • 谢谢,这是一个很好的解决方案!
    【解决方案2】:

    我在github上有一个包{dplyover}

    这可以帮助完成此类任务。在这种情况下,我们可以使用over2 同时循环两个字符向量。第一个向量包含 变量名作为字符串,这就是为什么我们必须将.x 包装在sym() 中的原因 对它应用一个函数。第二个向量包含函数名称, 我们在do.call 中用作.yover2 自动创建所需的名称。

    library(dplyr)
    library(dplyover) # https://github.com/TimTeaFan/dplyover
    
    mtcars %>%
      group_by(across(c("cyl", "carb"))) %>%
      summarise(over2(c("disp", "hp"),
                      c("mean", "sd"),
                      ~ do.call(.y, list(sym(.x)))
                      ))
    
    #> `summarise()` has grouped output by 'cyl'. You can override using the `.groups` argument.
    #> # A tibble: 9 x 4
    #> # Groups:   cyl [3]
    #>     cyl  carb disp_mean hp_sd
    #>   <dbl> <dbl>     <dbl> <dbl>
    #> 1     4     1      91.4 16.1 
    #> 2     4     2     117.  24.9 
    #> 3     6     1     242.   3.54
    #> 4     6     4     164.   7.51
    #> 5     6     6     145   NA   
    #> 6     8     2     346.  14.4 
    #> 7     8     3     276.   0   
    #> 8     8     4     406.  21.7 
    #> 9     8     8     301   NA
    

    基于相同逻辑构建的另一种方法是使用purrr::map2。然而, 在这里,我们必须付出一些努力来创建具有所需名称的向量。

    library(purrr)
    
    # setup vectors and names
    myfuns <- c("mean", "sd")
    myvars <- c("disp", "hp") %>%
      set_names(., paste(., myfuns, sep = "_"))
    
    mtcars %>%
      group_by(across(c("cyl", "carb"))) %>%
      summarise(map2(myvars,
                     myfuns,
                     ~ do.call(.y, list(sym(.x)))
                     ) %>% bind_cols()
      )
    
    #> `summarise()` has grouped output by 'cyl'. You can override using the `.groups` argument.
    #> # A tibble: 9 x 4
    #> # Groups:   cyl [3]
    #>     cyl  carb disp_mean hp_sd
    #>   <dbl> <dbl>     <dbl> <dbl>
    #> 1     4     1      91.4 16.1 
    #> 2     4     2     117.  24.9 
    #> 3     6     1     242.   3.54
    #> 4     6     4     164.   7.51
    #> 5     6     6     145   NA   
    #> 6     8     2     346.  14.4 
    #> 7     8     3     276.   0   
    #> 8     8     4     406.  21.7 
    #> 9     8     8     301   NA
    

    reprex package (v2.0.1) 于 2021-08-20 创建

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-25
      • 2010-09-10
      • 1970-01-01
      • 2012-05-05
      • 1970-01-01
      • 2017-10-20
      • 1970-01-01
      • 2010-09-07
      相关资源
      最近更新 更多