【问题标题】:Summarize data at different aggregate levels - R and tidyverse汇总不同聚合级别的数据 - R 和 tidyverse
【发布时间】:2019-11-04 16:06:53
【问题描述】:

我正在创建一堆基本状态报告,其中一件我觉得乏味的事情是在我的所有表格中添加一个总行。我目前正在使用 Tidyverse 方法,这是我当前代码的一个示例。我正在寻找的是一个默认包含几个不同级别的选项。

#load into RStudio viewer (not required)
iris = iris

#summary at the group level
summary_grouped = iris %>% 
       group_by(Species) %>%
       summarize(mean_s_length = mean(Sepal.Length),
                 max_s_width = max(Sepal.Width))

#summary at the overall level
summary_overall = iris %>% 
  summarize(mean_s_length = mean(Sepal.Length),
            max_s_width = max(Sepal.Width)) %>%
  mutate(Species = "Overall")

#append results for report       
summary_table = rbind(summary_grouped, summary_overall)

重复多次这样做非常乏味。我有点想要:

summary_overall = iris %>% 
       group_by(Species, total = TRUE) %>%
       summarize(mean_s_length = mean(Sepal.Length),
                 max_s_width = max(Sepal.Width))

仅供参考 - 如果您熟悉 SAS,我正在寻找可通过 proc 中的类、方式或类型语句获得的相同类型的功能,这意味着让我可以控制汇总级别并在一次调用中获得多个级别。

感谢任何帮助。我知道我可以创建自己的函数,但希望有些东西已经存在。我也更愿意坚持使用 tidyverse 编程风格,尽管我并没有这样做。

【问题讨论】:

  • 请参阅here 了解一些想法,但我所见过的几乎所有东西都非常手动且笨重。
  • janitorpivottabler 软件包提供了一些内置方法。 cran.r-project.org/web/packages/pivottabler/vignettes/…
  • 不幸的是,分组集在 dplyr github.com/tidyverse/dplyr/issues/236 中不太可能是原生的
  • 如果数据不是很大,你可以把底部的所有东西都绑定一遍,然后用它作为总摘要 - iris %>% bind_rows(., mutate(iris, Species="all")) ... - 浪费内存,但可以避免多次调用。跨度>

标签: r dplyr tidyverse group-summaries


【解决方案1】:

另一种选择:

library(tidyverse)  

iris %>% 
  mutate_at("Species", as.character) %>%
  list(group_by(.,Species), .) %>%
  map(~summarize(.,mean_s_length = mean(Sepal.Length),
                 max_s_width = max(Sepal.Width))) %>%
  bind_rows() %>%
  replace_na(list(Species="Overall"))
#> # A tibble: 4 x 3
#>   Species    mean_s_length max_s_width
#>   <chr>              <dbl>       <dbl>
#> 1 setosa              5.01         4.4
#> 2 versicolor          5.94         3.4
#> 3 virginica           6.59         3.8
#> 4 Overall             5.84         4.4

【讨论】:

    【解决方案2】:

    您可以编写一个函数,在 ungrouped tibble 上执行相同的 summarize 并将其绑定到末尾。

    summarize2 <- function(df, ...){
     bind_rows(summarise(df, ...), summarize(ungroup(df), ...))
    }
    
    iris %>% 
      group_by(Species) %>%
      summarize2(
        mean_s_length = mean(Sepal.Length),
        max_s_width = max(Sepal.Width)
      )
    
    # # A tibble: 4 x 3
    #   Species    mean_s_length max_s_width
    #   <fct>              <dbl>       <dbl>
    # 1 setosa              5.01         4.4
    # 2 versicolor          5.94         3.4
    # 3 virginica           6.59         3.8
    # 4 NA                  5.84         4.4
    

    如果需要,您可以为“总体”组的名称添加一些逻辑

    summarize2 <- function(df, ...){
      s1 <- summarise(df, ...)
      s2 <- summarize(ungroup(df), ...)
      for(v in group_vars(s1)){
        if(is.factor(s1[[v]]))
          s1[[v]] <- as.character(s1[[v]])
        if(is.character(s1[[v]])) 
         s2[[v]] <- 'Overall'
        else if(is.numeric(s1[[v]])) 
         s2[[v]] <- -Inf
      }
      bind_rows(s1, s2)
    }
    
    
    iris %>% 
      group_by(Species, g = Petal.Length %/% 1) %>%
      summarize2(
        mean_s_length = mean(Sepal.Length),
        max_s_width = max(Sepal.Width)
      )
    
    # # Groups:   Species [4]
    #   Species        g mean_s_length max_s_width
    #   <chr>      <dbl>         <dbl>       <dbl>
    # 1 setosa         1          5.01         4.4
    # 2 versicolor     3          5.35         2.9
    # 3 versicolor     4          6.09         3.4
    # 4 versicolor     5          6.35         3  
    # 5 virginica      4          5.85         3  
    # 6 virginica      5          6.44         3.4
    # 7 virginica      6          7.43         3.8
    # 8 Overall     -Inf          5.84         4.4
    

    【讨论】:

      【解决方案3】:
      library(dplyr)
      
      iris %>% 
        group_by(Species) %>%
        summarize(mean_s_length = mean(Sepal.Length),
                  max_s_width = max(Sepal.Width)) %>%
        ungroup() %>% 
        mutate_at(vars(Species), as.character) %>% 
        {rbind(.,c("Overal",mean(.$mean_s_length),max(.$max_s_width)))} %>%
        mutate_at(vars(-Species), as.double) %>% 
        mutate_at(vars(Species), as.factor)
      #> # A tibble: 4 x 3
      #>   Species    mean_s_length max_s_width
      #>   <fct>              <dbl>       <dbl>
      #> 1 setosa              5.01         4.4
      #> 2 versicolor          5.94         3.4
      #> 3 virginica           6.59         3.8
      #> 4 Overal              5.84         4.4
      

      reprex package (v0.3.0) 于 2019 年 6 月 21 日创建

      【讨论】:

        【解决方案4】:

        一种方法,也是乏味但在一个较长的管道中,是将第二个汇总指令放在bind_rows 中。
        as.character 调用避免了警告:

        警告信息:
        1:在 bind_rows_(x, .id) 中:
        绑定因子和字符向量,强制转换为字符向量
        2:在 bind_rows_(x, .id) 中:
        绑定字符和因子向量,强制转换成字符向量

        library(tidyverse)
        
        summary_grouped <- iris %>% 
          mutate(Species = as.character(Species)) %>%
          group_by(Species) %>%
          summarize(mean_s_length = mean(Sepal.Length),
                    max_s_width = max(Sepal.Width)) %>%
          bind_rows(iris %>% 
                      summarize(mean_s_length = mean(Sepal.Length),
                                max_s_width = max(Sepal.Width)) %>%
                      mutate(Species = "Overall"))
        ## A tibble: 4 x 3
        #  Species    mean_s_length max_s_width
        #  <chr>              <dbl>       <dbl>
        #1 setosa              5.01         4.4
        #2 versicolor          5.94         3.4
        #3 virginica           6.59         3.8
        #4 Overall             5.84         4.4
        

        【讨论】:

          【解决方案5】:

          可能是这样的:

          由于您想对同一输入(iris)执行不同的操作,最好将map 过不同的汇总函数并应用于数据。 map_dfr 使用 bind_rows 组合列表输出

          library(dplyr)
          library(purrr)
          
          pipe <- . %>%
            group_by(Species) %>%
            summarize(
              mean_s_length = mean(Sepal.Length),
              max_s_width   = max(Sepal.Width))
          
          map_dfr(
            list(pipe, . %>% mutate(Species = "Overall") %>% pipe),
            exec, 
            iris)
          #> Warning in bind_rows_(x, .id): binding factor and character vector,
          #> coercing into character vector
          #> Warning in bind_rows_(x, .id): binding character and factor vector,
          #> coercing into character vector
          #> # A tibble: 4 x 3
          #>   Species    mean_s_length max_s_width
          #>   <chr>              <dbl>       <dbl>
          #> 1 setosa              5.01         4.4
          #> 2 versicolor          5.94         3.4
          #> 3 virginica           6.59         3.8
          #> 4 Overall             5.84         4.4
          

          【讨论】:

            【解决方案6】:

            只需要在双数据集上应用一次想要的函数的解决方案:

            library(tidyverse)
            iris %>%
              rbind(mutate(., Species = "Overall")) %>%
              group_by(Species) %>%
              summarize(
                mean_s_length = mean(Sepal.Length),
                max_s_width = max(Sepal.Width)
              )
            
            # A tibble: 4 x 3
              Species    mean_s_length max_s_width
              <chr>              <dbl>       <dbl>
            1 Overall             5.84         4.4
            2 setosa              5.01         4.4
            3 versicolor          5.94         3.4
            4 virginica           6.59         3.8
            

            技巧是使用新组 ID(即Species)传递原始数据集:mutate(iris, Species = "Overall")

            【讨论】:

            • 这会复制整个数据集,本质上是添加一个整体类别?这适用于所示的用例,但如果我有多个分组变量,则稍微复杂一些。谢谢!
            猜你喜欢
            • 2014-01-24
            • 1970-01-01
            • 2018-06-25
            • 2019-03-25
            • 1970-01-01
            • 2023-01-24
            • 2017-08-14
            • 2021-06-10
            • 1970-01-01
            相关资源
            最近更新 更多