13  Группирование данных

На этой странице рассматривается группирование и агрегирование данных для описательного анализа. Для этого используется семейство пакетов tidyverse, где есть часто применяемые и простые в использовании функции.

Группирование данных - ключевой компонент управления и анализа данных. Группированные данные статистически резюмируются по группам, а также могут быть отражены на графике по группам. Функции из пакета dplyr (является частью tidyverse) существенно облегчают группирование и дальнейшие операции.

На этой странице мы рассмотрим следующие темы:

13.1 Подготовка

Загрузка пакетов

Данный фрагмент кода показывает загрузку пакетов, необходимых для анализа. В данном руководстве мы фокусируемся на использовании p_load() из пакета pacman, которая устанавливает пакет, если необходимо, и загружает его для использования. Вы можете также загрузить установленные пакеты с помощью library() из базового R. См. страницу Основы R для получения дополнительной информации о пакетах R.

pacman::p_load(
  rio,       # для импорта данных
  here,      # для расположения файла
  tidyverse, # для вычистки, работы с данными и построения графиков (включает dplyr)
  janitor)   # добавление строк и столбцов с итогами

Импорт данных

Мы импортируем набор данных о случаях имитированной эпидемии Эболы. Если вы хотите параллельно выполнять действия, кликните, чтобы скачать “чистый” построчный список (as .rds file). Набор данных импортируется с помощью функции import() из пакета rio. См. страницу Импорт и экспорт, где указаны разные способы импорта данных.

linelist <- import("linelist_cleaned.rds")

Первые 50 строк linelist:

13.2 Группирование

Функция group_by() из пакета dplyr группирует строки по уникальным значениям в указанном для функции столбце. Если указаны несколько столбцов, строки группируются по уникальным комбинациям значений по всем столбцам. Каждое уникальное значение (или комбинация значений) составляет группу. Дальнейшие изменения в наборе данных или расчетах затем можно проводить в контексте каждой группы.

Например, команда ниже берет linelist и группирует строки по уникальным значениям в столбце outcome, сохраняя выходные данные как новый датафрейм ll_by_outcome. Столбец(цы), по которым идет группирование, размещаются внутри скобок функции group_by().

ll_by_outcome <- linelist %>% 
  group_by(outcome)

Обратите внимание, что в наборе данных нет видимых изменений после выполнения group_by(), пока не будет применена другая глагольная функция dplyr, такая как mutate(), summarise(), либо arrange() к “группированному” датафрейму.

Однако вы можете “увидеть” группирование, напечатав датафрейм. Когда вы печатаете сгруппированный датафрейм, вы увидите, что он превратился в объект класса tibble который при печати отображает, какое группирование применено и сколько существует групп - это пишется над строкой заголовка.

# печать, чтобы увидеть, какие группы активны
ll_by_outcome
# A tibble: 5,888 × 30
# Groups:   outcome [3]
   case_id generation date_infection date_onset date_hospitalisation
   <chr>        <dbl> <date>         <date>     <date>              
 1 5fe599           4 2014-05-08     2014-05-13 2014-05-15          
 2 8689b7           4 NA             2014-05-13 2014-05-14          
 3 11f8ea           2 NA             2014-05-16 2014-05-18          
 4 b8812a           3 2014-05-04     2014-05-18 2014-05-20          
 5 893f25           3 2014-05-18     2014-05-21 2014-05-22          
 6 be99c8           3 2014-05-03     2014-05-22 2014-05-23          
 7 07e3e8           4 2014-05-22     2014-05-27 2014-05-29          
 8 369449           4 2014-05-28     2014-06-02 2014-06-03          
 9 f393b4           4 NA             2014-06-05 2014-06-06          
10 1389ca           4 NA             2014-06-05 2014-06-07          
# ℹ 5,878 more rows
# ℹ 25 more variables: date_outcome <date>, outcome <chr>, gender <chr>,
#   age <dbl>, age_unit <chr>, age_years <dbl>, age_cat <fct>, age_cat5 <fct>,
#   hospital <chr>, lon <dbl>, lat <dbl>, infector <chr>, source <chr>,
#   wt_kg <dbl>, ht_cm <dbl>, ct_blood <dbl>, fever <chr>, chills <chr>,
#   cough <chr>, aches <chr>, vomit <chr>, temp <dbl>, time_admission <chr>,
#   bmi <dbl>, days_onset_hosp <dbl>

Уникальные группы

Созданные группы отображают каждую уникальную комбинацию значений по столбцам группирования.

Чтобы увидеть группы и количество строк в каждой группе, передайте группированные данные в функцию tally().Чтобы увидеть только уникальные группы без подсчета количества вы можете передать их в group_keys().

См. ниже, что существует три уникальных значения в группируемом столбце исхода outcome: “Death” (смерть), “Recover” (выздоровление), и NA (отсутствует). Вы видите, что было nrow(linelist %>% filter(outcome == "Death")) смертей, nrow(linelist %>% filter(outcome == "Recover")) выздоровевших и nrow(linelist %>% filter(is.na(outcome))) без документированного исхода.

linelist %>% 
  group_by(outcome) %>% 
  tally()
# A tibble: 3 × 2
  outcome     n
  <chr>   <int>
1 Death    2582
2 Recover  1983
3 <NA>     1323

Вы можете группировать более чем по одному столбцу. Ниже датафрейм группируется по исходу outcome и полу gender, а затем делается подсчет. Обратите внимание, что каждая уникальная комбинация исхода outcome и пола gender регистрируется как отдельная группа - включая отсутствующие значения для каждого столбца.

linelist %>% 
  group_by(outcome, gender) %>% 
  tally()
# A tibble: 9 × 3
# Groups:   outcome [3]
  outcome gender     n
  <chr>   <chr>  <int>
1 Death   f       1227
2 Death   m       1228
3 Death   <NA>     127
4 Recover f        953
5 Recover m        950
6 Recover <NA>      80
7 <NA>    f        627
8 <NA>    m        625
9 <NA>    <NA>      71

Новые столбцы

Вы можете также создать новый столбец группирования внутри утверждения group_by(). Это все равно что поставить mutate() перед group_by(). Этот стиль может подойти для быстрого подсчета, но для ясности кода попробуйте создавать этот столбец отдельным шагом mutate() и потом присоединяйте его по каналу в group_by().

# группирование данных на основе двоичного столбца, созданного *внутри* команды group_by()
linelist %>% 
  group_by(
    age_class = ifelse(age >= 18, "adult", "child")) %>% 
  tally(sort = T)
# A tibble: 3 × 2
  age_class     n
  <chr>     <int>
1 child      3618
2 adult      2184
3 <NA>         86

Добавление/удаление столбцов группирования

По умолчанию если вы выполните group_by() для данных, которые уже сгруппированы, старые группы будут удалены и будут применены новые группы. Если вы хотите добавить новые группы к существующим, включите аргумент .add = TRUE.

# Сгруппировано по исходу
by_outcome <- linelist %>% 
  group_by(outcome)

# Добавляем дополнительное группирование по полу
by_outcome_gender <- by_outcome %>% 
  group_by(gender, .add = TRUE)

Сохраняем все группы

Если вы сгруппируете по столбцу в классе фактор, могут быть уровни фактора, которых в настоящее время нет в данных. Если вы проведете группирование по этому столбцу, по умолчанию эти отсутствующие уровни будут выкинуты и не будут включены как группы. Чтобы это изменить так, чтобы все уровни появлялись как группы (даже если их нет в наличии в данных), установите в команде group_by() аргумент .drop = FALSE.

13.3 Разгруппирование

Данные, которые были сгруппированы, останутся сгруппированными, пока вы их не разгруппируете с помощью ungroup(). Если вы забудете разгруппировать, это может привести к ошибкам в расчетах! Ниже представлен пример снятия всех группирований:

linelist %>% 
  group_by(outcome, gender) %>% 
  tally() %>% 
  ungroup()

Вы можете также снять группирование только конкретных столбцов, разместив имя этого столбца внутри ungroup().

linelist %>% 
  group_by(outcome, gender) %>% 
  tally() %>% 
  ungroup(gender) # снимаем группирование по полу, сохраняем группирование по исходу

ПРИМЕЧАНИЕ: Глагол count() автоматически разгруппирует данные после подсчета.

13.4 Резюмирование

См. раздел dplyr страницы [Описательные таблицы] для получения детального описания того, как создавать суммарные таблицы с помощью summarise(). Здесь мы кратко рассматриваем то, как меняется поведение этой функции при применениии к группированным данным.

функция summarise() (или summarize()) из dplyr берет датафрейм и конвертирует его в новый сводный датафрейм, где столбцы содержат сводную статистику, которую вы зададите. В негруппированном датафрейме сводная статистика рассчитывается по всем строкам. Применение summarise() к группированным данным выдаст такую статистику для каждой группы.

Синтаксис summarise() таков, что вы задаете имя(имена) нового сводного столба(ов), знак равно, а затем статистическую функцию, которую нужно применить к данным, как показано ниже. Например, min() (минимум), max() (максимум), median() (медиана), либо sd() (стандартное отклонение). В рамках статистической функции укажите столбец, над которым нужно работать и необходимый аргумент (например, na.rm = TRUE). Вы можете использовать sum() (сумма), чтобы подсчитать количество строк, соответствующих логическому критерию (с двойным знаком равно ==).

Ниже представлен пример применения summarise() без группирования данных. Полученная статистика является результатом для всего набора данных.

# сводная статистика по разгруппированному построчному списку
linelist %>% 
  summarise(
    n_cases  = n(),
    mean_age = mean(age_years, na.rm=T),
    max_age  = max(age_years, na.rm=T),
    min_age  = min(age_years, na.rm=T),
    n_males  = sum(gender == "m", na.rm=T))
  n_cases mean_age max_age min_age n_males
1    5888 16.01831      84       0    2803

Для сравнения, ниже представлено то же самое утверждение summarise(), но применительно к группированным данным. Статистика рассчитывается для каждой группы исходов outcome. Обратите внимание, что столбцы группирования переносятся в новый датафрейм.

# сводная статистика по группированному построчному списку
linelist %>% 
  group_by(outcome) %>% 
  summarise(
    n_cases  = n(),
    mean_age = mean(age_years, na.rm=T),
    max_age  = max(age_years, na.rm=T),
    min_age  = min(age_years, na.rm=T),
    n_males    = sum(gender == "m", na.rm=T))
# A tibble: 3 × 6
  outcome n_cases mean_age max_age min_age n_males
  <chr>     <int>    <dbl>   <dbl>   <dbl>   <int>
1 Death      2582     15.9      76       0    1228
2 Recover    1983     16.1      84       0     950
3 <NA>       1323     16.2      69       0     625

СОВЕТ: Функция резюмирования работает и в британском, и в американском написании слова - summarise() и summarize() выполняют одинаковую функцию.

13.5 Подсчет с помощью count и tally

count() и tally() выполняют схожий функционал, но отличаются друг от друга. Более подробно об этих отличиях между tally() и count() можно прочитать тут

tally()

tally() - это сокращение для summarise(n = n()) и не группирует данные. Таким образом, чтбы получить группированный подсчет, перед ней должна идти команда group_by(). Вы можете добавить sort = TRUE, чтобы сначала увидеть более крупные группы.

linelist %>% 
  tally()
     n
1 5888
linelist %>% 
  group_by(outcome) %>% 
  tally(sort = TRUE)
# A tibble: 3 × 2
  outcome     n
  <chr>   <int>
1 Death    2582
2 Recover  1983
3 <NA>     1323

count()

А команда count() работает следующим образом:

  1. применяет group_by() к указанному столбцу(ам)
  2. применяет summarise() и выдает столбец n с количеством строк на группу
  3. применяет ungroup()
linelist %>% 
  count(outcome)
  outcome    n
1   Death 2582
2 Recover 1983
3    <NA> 1323

Как и в случаев с group_by() вы можете создать новый столбец внутри команды count():

linelist %>% 
  count(age_class = ifelse(age >= 18, "adult", "child"), sort = T)
  age_class    n
1     child 3618
2     adult 2184
3      <NA>   86

count() можно вызывать несколько раз, а ее функционал будет “комбинироватся”. Например, чтобы свести количество больниц, в которых есть каждый пол, выполните следующее. Обратите внимание, что имя итогового столбца меняется с имени по умолчанию “n” для большей ясности (с помощью name =).

linelist %>% 
  # создаем подсчет по уникальным группам исход-пол
  count(gender, hospital) %>% 
  # собираем строки по полу (3) и считаем количество больниц на пол (6)
  count(gender, name = "hospitals per gender" ) 
  gender hospitals per gender
1      f                    6
2      m                    6
3   <NA>                    6

Добавить подсчеты

Вместо count() и summarise() вы можете использовать add_count(), чтобы добавить новый столбец n с подсчетом строк на группу, при этом сохранив все остальные столбцы датафрейма.

Это означает, что подсчитанное количество по группе в новом столбце n будет напечатано в каждой строке группы. Для демонстрации мы добавим этот столбец, а затем переупорядочим столбцы для облегчения просмотра. См. раздел ниже фильтр по размеру группы, где приведен еще один пример.

linelist %>% 
  as_tibble() %>%                   # конвертируем в таблицу tibble для более красивой печати 
  add_count(hospital) %>%           # добавляем столбец n с подсчетами по больнице
  select(hospital, n, everything()) # переупорядочиваем для демонстрации
# A tibble: 5,888 × 31
   hospital                       n case_id generation date_infection date_onset
   <chr>                      <int> <chr>        <dbl> <date>         <date>    
 1 Other                        885 5fe599           4 2014-05-08     2014-05-13
 2 Missing                     1469 8689b7           4 NA             2014-05-13
 3 St. Mark's Maternity Hosp…   422 11f8ea           2 NA             2014-05-16
 4 Port Hospital               1762 b8812a           3 2014-05-04     2014-05-18
 5 Military Hospital            896 893f25           3 2014-05-18     2014-05-21
 6 Port Hospital               1762 be99c8           3 2014-05-03     2014-05-22
 7 Missing                     1469 07e3e8           4 2014-05-22     2014-05-27
 8 Missing                     1469 369449           4 2014-05-28     2014-06-02
 9 Missing                     1469 f393b4           4 NA             2014-06-05
10 Missing                     1469 1389ca           4 NA             2014-06-05
# ℹ 5,878 more rows
# ℹ 25 more variables: date_hospitalisation <date>, date_outcome <date>,
#   outcome <chr>, gender <chr>, age <dbl>, age_unit <chr>, age_years <dbl>,
#   age_cat <fct>, age_cat5 <fct>, lon <dbl>, lat <dbl>, infector <chr>,
#   source <chr>, wt_kg <dbl>, ht_cm <dbl>, ct_blood <dbl>, fever <chr>,
#   chills <chr>, cough <chr>, aches <chr>, vomit <chr>, temp <dbl>,
#   time_admission <chr>, bmi <dbl>, days_onset_hosp <dbl>

Добавление Итого

Чтобы легко добавить итоговую сумму по строкам или столбцам после применения tally() или count(), см. раздел janitor на странице Описательные таблицы. Этот пакет предлагает другие функции, такие как adorn_totals() и adorn_percentages(), чтобы складывать итоговые показатели и конвертировать для отображения в виде процентов. Ниже приведен короткий пример:

linelist %>%                                  # построчный список случаев
  tabyl(age_cat, gender) %>%                  # кросс-табуляция подсчета по двум столбцам
  adorn_totals(where = "row") %>%             # добавляем строку итого
  adorn_percentages(denominator = "col") %>%  # конвертируем в долю со знаменателем столбца
  adorn_pct_formatting() %>%                  # конвертируем долю в проценты
  adorn_ns(position = "front") %>%            # отображаем как: "count (percent)"
  adorn_title(                                # корректируем заголовки
    row_name = "Age Category",
    col_name = "Gender")
                      Gender                            
 Age Category              f              m          NA_
          0-4   640  (22.8%)   416  (14.8%)  39  (14.0%)
          5-9   641  (22.8%)   412  (14.7%)  42  (15.1%)
        10-14   518  (18.5%)   383  (13.7%)  40  (14.4%)
        15-19   359  (12.8%)   364  (13.0%)  20   (7.2%)
        20-29   468  (16.7%)   575  (20.5%)  30  (10.8%)
        30-49   179   (6.4%)   557  (19.9%)  18   (6.5%)
        50-69     2   (0.1%)    91   (3.2%)   2   (0.7%)
          70+     0   (0.0%)     5   (0.2%)   1   (0.4%)
         <NA>     0   (0.0%)     0   (0.0%)  86  (30.9%)
        Total 2,807 (100.0%) 2,803 (100.0%) 278 (100.0%)

Чтобы добавить более сложные строки Итого, которые требуют сводной статистики, а не просто суммы, см. этот раздел страницы Описательные таблицы.

13.6 Группировка по дате

При группировании данных по дате, у вас должен быть (или вам нужно создать) столбец с интересующей единицей даты - например, “день”, “эпиднеделя”, “месяц” и т.п. Вы можете создать этот столбец, используя floor_date() из lubridate, как объясняется в разделе Эпидемиологические недели страницы [Работа с датами]. Как только у вас будет такой столбец, вы можете использовать count() из dplyr, чтобы сгруппировать строки по этим уникальным значениям даты и получить агрегированный подсчет.

Еще один дополнительный шаг, часто встречающийся в ситуации с датами - “заполнение” дат в последовательности, которые не присутствуют в данных. Используйте complete() из tidyr, чтобы агрегированный ряд дат был полным, включая все возможные единицы дат в диапазоне. Без этого шага неделя, в которой не было зарегистрировано случаев, может не появиться в ваших данных!

В рамках complete() вы переопределяете ваш столбец дата как последовательность дат seq.Date() от минимальной до максимальной - таким образом расширяются даты. По умолчанию значения подсчета случаев в любых новых “расширенных” строках будут NA. Вы можете их установить на 0, используя аргумент fill = в complete(), который ожидает именованного списка (если ваш столбец подсчет назван n, задайте fill = list(n = 0). См. ?complete для получения более детальной информации, а также пример на странице Работа с датами.

Построчный список случаев по дням

Здесь приведен пример группирования случаев по дням без использования complete(). Обратите внимание, что первые строки пропускают даты без случаев.

daily_counts <- linelist %>% 
  drop_na(date_onset) %>%        # удаляем те, в которых отсутствует дата заболевания (date_onset)
  count(date_onset)              # считаем количество строк на уникальную дату

Ниже мы добавляем команду complete(), чтобы убедиться, что каждый день в диапазоне представлен.

daily_counts <- linelist %>% 
  drop_na(date_onset) %>%                 # удаляем случаи, где отсутствует дата заболевания (date_onset)
  count(date_onset) %>%                   # считаем количество строк на уникальную дату
  complete(                               # чтобы появились все даты, даже без случаев
    date_onset = seq.Date(                # переопределяем столбец дата как ежедневную последовательность дат
      from = min(date_onset, na.rm=T), 
      to = max(date_onset, na.rm=T),
      by = "day"),
    fill = list(n = 0))                   # задаем отображение 0 во всех новых заполненных строках в столбце n (вместо NA как отображается по умолчанию) 

Построчный список случаев по неделям

Тот же принцип можно применить к неделям. Сначала создаем новый столбец - неделя случая, используя floor_date() с аргументом unit = "week". Затем используем count(), как показано выше, чтобы получить понедельное количество случаев. Затем закончите с помощью complete(), чтобы убедиться, что присутствуют все недели, даже если в них не было случаев.

# Создаем набор данных с недельным количеством случаев
weekly_counts <- linelist %>% 
  drop_na(date_onset) %>%                 # удаляем случаи с отсутствующей датой заболевания (date_onset)
  mutate(week = lubridate::floor_date(date_onset, unit = "week")) %>%  # новый столбец недели заболевания
  count(week) %>%                         # группируем данные по неделе и считаем количество строк на группу
  complete(                               # чтобы появились все дни, даже без случаев
    week = seq.Date(                      # переопределяем столбец дата как ежедневную последовательность дат
      from = min(week, na.rm=T), 
      to = max(week, na.rm=T),
      by = "week"),
    fill = list(n = 0))                   #  задаем отображение 0 во всех новых заполненных строках в столбце n (вместо NA как отображается по умолчанию)

Вот первые 50 строк получившегося датафрейма:

###Построчный список случаев по месяцам {.unnumbered}

Чтобы агрегировать случаи по месяцам, снова используйте floor_date() из пакета lubridate, но с аргументом unit = "months". Это округлит дату вниз до 1го числа месяца. Выходные данные будут в классе Дата. Обратите внимание, что в шаге complete(), мы также используем аргумент by = "months".

# Создаем набор данных ежемесячного количества случаев
monthly_counts <- linelist %>% 
  drop_na(date_onset) %>% 
  mutate(month = lubridate::floor_date(date_onset, unit = "months")) %>%  # новый столбец, 1е число месяца заболевания
  count(month) %>%                          # подсчет количества случаев по месяцу
  complete(
    month = seq.Date(
      min(month, na.rm=T),     # включает все месяцы, где не было зарегистрировано случаев
      max(month, na.rm=T),
      by="month"),
    fill = list(n = 0))

Ежедневное количество в недельное

Чтобы агрегировать ежедневное количество случаев в недельное количество, используйте floor_date() как показано выше. Однако, используйте group_by() и summarize() вместо count(), поскольку вам нужна сумма sum() ежедневного количества случаев, а не просто подсчет количества строк в неделю.

Ежедневное количество в месячное

Чтобы агрегировать ежедневное количество случаев в месячное количество, используйте floor_date() с аргументом unit = "month", как показано выше. Однако, используйте group_by() и summarize() вместо count(), поскольку вам нужна сумма sum() ежедневного количества случаев, а не просто подсчет количества строк в месяц.

13.7 Упорядочивание сгруппированных данных

Использование глагольной функции arrange() из dplyr, чтобы упорядочить строки в датафрейме, даст вам такое же поведение, как и при группировании данных, если только вы не зададите аргумент .by_group =TRUE. В таком случае строки будут сначала упорядочены по группированным столбцам, которые вы укажете в аргументе arrange().

13.8 Фильтр по группированным данным

filter()

При применении в сочетании с функциями, которые оценивают датафрейм (например, max(), min(), mean()), эти функции теперь будут применяться к группам. Например, если вы хотите фильтровать и сохранить те строки, где пациенты старше медианного возраста, это теперь будет применяться по группам - фильтр, чтобы сохранить строки выше медианного возраста группы.

Выбор строк в группе

Функция slice() из dplyr, которая фильтрует строки на основе их положения в данных, может также применяться к группе. Помните, что нужно учесть сортировку данных внутри каждой группы, чтобы получить желаемый “срез”.

Например, чтобы извлечь только последние 5 госпитализаций из каждой больницы:

  1. Сгруппируйте построчный список по столбцу hospital
  2. Упорядочьте записи от самой последней к самой ранней дате госпитализации date_hospitalisation внутри каждой группы больниц
  3. Используйте slice, чтобы извлечь первые 5 строк для каждой больницы
linelist %>%
  group_by(hospital) %>%
  arrange(hospital, date_hospitalisation) %>%
  slice_head(n = 5) %>% 
  arrange(hospital) %>%                            # для отображения
  select(case_id, hospital, date_hospitalisation)  # для отображения
# A tibble: 30 × 3
# Groups:   hospital [6]
   case_id hospital          date_hospitalisation
   <chr>   <chr>             <date>              
 1 20b688  Central Hospital  2014-05-06          
 2 d58402  Central Hospital  2014-05-10          
 3 b8f2fd  Central Hospital  2014-05-13          
 4 acf422  Central Hospital  2014-05-28          
 5 275cc7  Central Hospital  2014-05-28          
 6 d1fafd  Military Hospital 2014-04-17          
 7 974bc1  Military Hospital 2014-05-13          
 8 6a9004  Military Hospital 2014-05-13          
 9 09e386  Military Hospital 2014-05-14          
10 865581  Military Hospital 2014-05-15          
# ℹ 20 more rows

slice_head() - выбирает n строк сверху
slice_tail() - выбирает n строк снизу
slice_sample() - случайно выбирает n строк
slice_min() - выбирает n строк с наибольшими значениями в столбце order_by =, используйте with_ties = TRUE, чтобы сохранить связи
slice_max() - выбирает n строк с наименьшими значениями в столбце order_by =, используйте with_ties = TRUE, чтобы сохранить связи

См. страницу [Дедупликация], где представлено больше примеров и деталей по slice().

Фильтр по размеру группы

Фукнция add_count() добавляет столбец n к оригинальным данным, указывая количество строк в этой группе строк.

Ниже показано, как add_count() применяется к столбцу hospital, так что значения в новом столбце n отражают количество строк в группе больниц этой строки. Обратите внимание, что значения в столбце n повторяются. В примере ниже, столбец n можно изменить, используя name = внутри add_count(). Для демонстрационных целей мы переупорядочиваем столбцы с помощью select().

linelist %>% 
  as_tibble() %>% 
  add_count(hospital) %>%          # добавляем "количество строк госпитализированных в ту же больницу, что и эта строка" 
  select(hospital, n, everything())
# A tibble: 5,888 × 31
   hospital                       n case_id generation date_infection date_onset
   <chr>                      <int> <chr>        <dbl> <date>         <date>    
 1 Other                        885 5fe599           4 2014-05-08     2014-05-13
 2 Missing                     1469 8689b7           4 NA             2014-05-13
 3 St. Mark's Maternity Hosp…   422 11f8ea           2 NA             2014-05-16
 4 Port Hospital               1762 b8812a           3 2014-05-04     2014-05-18
 5 Military Hospital            896 893f25           3 2014-05-18     2014-05-21
 6 Port Hospital               1762 be99c8           3 2014-05-03     2014-05-22
 7 Missing                     1469 07e3e8           4 2014-05-22     2014-05-27
 8 Missing                     1469 369449           4 2014-05-28     2014-06-02
 9 Missing                     1469 f393b4           4 NA             2014-06-05
10 Missing                     1469 1389ca           4 NA             2014-06-05
# ℹ 5,878 more rows
# ℹ 25 more variables: date_hospitalisation <date>, date_outcome <date>,
#   outcome <chr>, gender <chr>, age <dbl>, age_unit <chr>, age_years <dbl>,
#   age_cat <fct>, age_cat5 <fct>, lon <dbl>, lat <dbl>, infector <chr>,
#   source <chr>, wt_kg <dbl>, ht_cm <dbl>, ct_blood <dbl>, fever <chr>,
#   chills <chr>, cough <chr>, aches <chr>, vomit <chr>, temp <dbl>,
#   time_admission <chr>, bmi <dbl>, days_onset_hosp <dbl>

Затем становится легче фильтровать по строкам случаев, которые были госпитализированы в “маленькую” больницу, например, в больницу, куда госпитализировано менее 500 пациентов:

linelist %>% 
  add_count(hospital) %>% 
  filter(n < 500)

13.9 Функция mutate для сгруппированных данных

Чтобы сохранить все столбцы и строки (не обобщать) и добавить новый столбец, содержащий стастистику группы, используйте mutate() после group_by() вместо summarise().

Это полезно, если вы хотите получить статистику группы в оригинальном наборе данных с сохранением всех остальных столбцов - например, для расчетов, которые сравнивают одну строку с ее группой.

Например, код ниже рассчитывает разницу между задержкой госпитализации строки и медианной задержкой по этой больнице. Выполняются следующие шаги:

  1. Группируем данные по больнице
  2. Используем столбец days_onset_hosp (задержка госпитализации), чтобы создать новый столбец, содержащий среднюю задержку в больнице из этой строки
  3. Рассчитываем разницу между двумя столбцами

Мы выбираем только некоторые столбцы для отображения с помощью select() для целей демонстрации.

linelist %>% 
  # группируем данные по больнице (еще не изменяем построчный список)
  group_by(hospital) %>% 
  
  # новые столбцы
  mutate(
    # среднее количество дней до госпитализации по больнице (округляется до 1 десятичного знака)
    group_delay_admit = round(mean(days_onset_hosp, na.rm=T), 1),
    
    # разница между задержкой строки и средней задержкой в этой больнице (округляется до 1 десятичного знака)
    diff_to_group     = round(days_onset_hosp - group_delay_admit, 1)) %>%
  
  # выбираем только определенные строки для демонстрации/просмотра
  select(case_id, hospital, days_onset_hosp, group_delay_admit, diff_to_group)
# A tibble: 5,888 × 5
# Groups:   hospital [6]
   case_id hospital              days_onset_hosp group_delay_admit diff_to_group
   <chr>   <chr>                           <dbl>             <dbl>         <dbl>
 1 5fe599  Other                               2               2             0  
 2 8689b7  Missing                             1               2.1          -1.1
 3 11f8ea  St. Mark's Maternity…               2               2.1          -0.1
 4 b8812a  Port Hospital                       2               2.1          -0.1
 5 893f25  Military Hospital                   1               2.1          -1.1
 6 be99c8  Port Hospital                       1               2.1          -1.1
 7 07e3e8  Missing                             2               2.1          -0.1
 8 369449  Missing                             1               2.1          -1.1
 9 f393b4  Missing                             1               2.1          -1.1
10 1389ca  Missing                             2               2.1          -0.1
# ℹ 5,878 more rows

13.10 Функция select для сгруппированных данных

Глагольная функция select() работает на сгруппированных данных, но столбцы группирования всегда включаются (даже если не упомянуты в select()). Если вам не нужны эти столбцы группирования, используйте сначала ungroup().

13.11 Ресурсы

Вот некоторые полезные ресурсы с дополнительной информацией:

Вы можете применять любую резюмирующую функцию к группированным данным; см. Шпаргалку по преобразованию данных в RStudio

Страница Data Carpentry в dplyr
Справочная страница tidyverse по group_by() и группированию

Страница по Манипуляциям с данными

Обобщение с условиями в dplyr