Суммируйте данные на разных совокупных уровнях - R и Tidyverse - PullRequest
6 голосов
/ 21 июня 2019

Я создаю кучу базовых отчетов о состоянии, и одной из вещей, которые я считаю утомительным, является добавление итоговой строки ко всем моим таблицам. В настоящее время я использую подход 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, что позволяет мне контролировать уровень суммирования и получать несколько уровней за один вызов.

Любая помощь приветствуется. Я знаю, что могу создать свою собственную функцию, но надеялся, что что-то уже существует. Я также предпочел бы придерживаться стиля программирования, хотя я не настроен на это.

Ответы [ 6 ]

2 голосов
/ 22 июня 2019

Вы можете написать функцию, которая делает то же самое summarize на ungroup ed тибле и связывает это до конца.

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
2 голосов
/ 22 июня 2019

Решение, в котором вам нужно применить требуемую функцию только один раз для двойного набора данных:

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

Хитрость заключается в том, чтобы передать исходный набор данных с новым идентификатором группы (т. Е. Species): mutate(iris, Species = "Overall")

1 голос
/ 25 июня 2019

Другая альтернатива:

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
1 голос
/ 21 июня 2019

Может быть, что-то вроде этого:

Поскольку вы хотите выполнять различные операции с одним и тем же входом (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
1 голос
/ 21 июня 2019

Один способ, также утомительный, но в одном более длинном канале, - поместить вторую команду суммирования в 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
1 голос
/ 21 июня 2019
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

Создан в 2019-06-21 пакетом представ (v0.3.0)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...