::p_load(
pacman# импорт данных
rio, # группирование и вычистка данных
data.table, # позволяет использовать функцию оператора канала (%>%) в этой главе
tidyverse,
here )
50 Таблица данных
Данное руководство фокусируется на “глагольных” функциях dplyr и операторе канала из пакета magrittr %>%
как методах вычистки и группирования данных, но пакетdata.table предлагает альтернативный метод, с которым вы можете столкнуться в ходе своей работы с R.
50.1 Введение в таблицы данных
Таблица данных - это двумерная структура данных, подобная датафрейму, позволяющая выполнять сложные операции группирования. Синтаксис data.table построен таким образом, что операции могут выполняться для строк, столбцов и групп.
Структура - DT[i, j, by], разделенная на 3 части; аргументы i, j и by. Аргумент i позволяет взять подмножество требуемых строк, аргумент j позволяет работать со столбцами, а аргумент by позволяет работать со столбцами по группам.
На этой странице мы рассмотрим следующие темы:
- Импорт данных и использование
fread()
иfwrite()
- Выбор и фильтрация строк, используя аргумент i
- Использование функций-помощников
%like%
,%chin%
,%between%
- Выбор и расчет по столбцам, используя аргумент j
- Расчет по группам, используя аргумент by
- Добавление и обновление данных в таблицах данных с помощью
:=
50.2 Загрузка пакетов и импорт данных
Загрузка пакетов
Используя функцию p_load()
из pacman, мы загружаем (и устанавливаем при необходимости) пакеты, требуемые для данного анализа.
Импорт данных
На данной странице мы рассмотрим некоторые ключевые функции data.table, используя построчный список, на который мы ссылались в других главах этого руководства.
Мы импортируем набор данных о случаях из смоделированной эпидемии лихорадки Эбола. Если вы хотите загрузить данные, чтобы выполнять действия параллельно, см. инструкции в разделе Скачивание руководства и данных. Набор данных импортируется с помощью функции import()
из пакета rio. См. страницу Импорт и экспорт, где приведены разные способы импорта данных. С этого момента мы будем использовать data.table()
, чтобы конвертировать датафрейм в таблицу данных.
<- rio::import(here("data", "linelist_cleaned.xlsx")) %>% data.table() linelist
Функция fread()
используется напрямую для импорта обычных файлов с разделителями, таких как файлы .csv, напрямую в формат таблицы данных. Эта функция и ее парная функция, fwrite()
, используемая для записи таблиц данных в виде обычных файлов с разделителями, являются очень быстрыми и вычислительно эффективными вариантами для больших баз данных.
Первыу 20 строк linelist
:
Команды базового R, такие как dim()
, которые используются для датафреймов, могут также использоваться для таблиц данных
dim(linelist) #дает количество строк и столбцов в таблице данных
[1] 5888 30
50.3 Аргумент i: выбор и фильтрация строк
Вспоминая структуру DT[i, j, by], мы можем фильтровать строки, используя либо номера строк, либо логические выражения. Аргумент i является первым, поэтому можно использовать синтаксис DT[i] или DT[i,].
В первом примере извлекаются первые 5 строк таблицы данных, во втором примере подмножество случаев 18 лет и старше, в третьем примере подмножество случаев 18 лет и старше, но не диагностированных в Central Hospital:
1:5] #выдает с 1й по 5ю строки
linelist[>= 18] #подмножество случаев, равных или старше 18 лет
linelist[age >= 18 & hospital != "Central Hospital"] #подмножество случаев, равных или старше 18 лет, но не диагностированных в Central Hospital linelist[age
Использование .N в аргументе i означает общее количество строк в таблице данных. Это может быть использовано для подмножества по номерам строк:
#выдает последнюю строку
linelist[.N] 15:.N] #выдает с 15й по последнюю строки linelist[
Использование функций-помощников для фильтра
В таблице данных используются вспомогательные функции, которые упрощают выделение подмножества строк. Функция %like%
испольузется, чтобы сопоставить набор символов в столбце, %chin%
используется для сопоставления по конкретному знаку, а функция-помощник %between%
используется, чтобы сопоставить числовые столбцы с заранее указанным диапазоном.
В следующем примере мы: * фильтруем строки, где в переменной больницы есть “Hospital” * фильтруем строки, где исход “Recover” (выздоровел) или “Death” (смерть) * фильтруем строки в возрастном диапазоне 40-60
%like% "Hospital"] #фильтруем строки, где в переменной больницы есть “Hospital”
linelist[hospital %chin% c("Recover", "Death")] #фильтруем строки, где исход “Recover” (выздоровел) или “Death” (смерть)
linelist[outcome %between% c(40, 60)] #фильтруем строки в возрастном диапазоне 40-60
linelist[age
#%between% должен принять вектор длины 2, а %chin% может принять векторы длины >= 1
50.4 Аргумент j: выбор и расчет по столбцам
Используя структуру DT[i, j, by], мы можем выбирать столбцы с помощью чисел или имен. Аргумент j является вторым, поэтому используется синтаксис DT[, j]. Для облегчения вычислений над аргументом j столбец оборачивается с помощью либо list()
, либо .()
.
Выбор столбцов
Первый пример извлекает первый, третий и пятый столбцы таблицы данных, второй пример выбирает все столбцы, кроме столбцов рост, вес и пол. В третьем примере используется обертка .()
, чтобы выбрать столбцы case_id и outcome.
c(1,3,5)]
linelist[ , -c("gender", "age", "wt_kg", "ht_cm")]
linelist[ , list(case_id, outcome)] #linelist[ , .(case_id, outcome)] работает столь же хорошо linelist[ ,
Расчет по столбцам
Комбинируя аргументы i и j, можно фильтровать строки и вычислять по столбцам. Использование .N в аргументе j также представляет общее количество строк в таблице данных и может быть полезно для возврата количества строк после фильтрации строк.
В следующем примере мы: * Считаем количества случаев пребывания в стационаре более 7 дней * Считаем средний возраст больных, умерших в military hospital * Считаем стандартное отклонение, медиану, средний возраст пациентов, выздоровевших в central hospital
> 7 , .N] linelist[days_onset_hosp
[1] 189
%like% "Military" & outcome %chin% "Death", .(mean(age, na.rm = T))] #na.rm = T удаляет значения N/A linelist[hospital
V1
<num>
1: 15.9084
== "Central Hospital" & outcome == "Recover",
linelist[hospital mean_age = mean(age, na.rm = T),
.(median_age = median(age, na.rm = T),
sd_age = sd(age, na.rm = T))] #Этот синтаксис не использует функции-помощники, но работает так же хорошо
mean_age median_age sd_age
<num> <num> <num>
1: 16.85185 14 12.93857
Помните, что использование обертки .() в аргументе j упрощает расчет, выдает таблицу данных и позволяет именовать столбцы.
50.5 Аргумент by: расчет по группам
Аргумент by - третий аргумент в структуре DT[i, j, by]. Аргумент by принимает и синтаксис текстового вектора, и list()
или .()
. Использование синтаксиса .()
в аргументе by позволяет на ходу переименовывать столбцы.
В следующем примере мы: * группируем количество случаев по больнице * в случаях 18 лет и старше, рассчитываем средний рост и вес случаев по полу и в зависимости от того, они выздоровели или умерли * в случаях госпитализации, продолжавшейся более 7 дней, подсчитывается количество случаев в зависимости от месяца госпитализации и больницы, в которую они были госпитализированы
#количество случаев по больнице linelist[, .N, .(hospital)]
hospital N
<char> <int>
1: Other 885
2: Missing 1469
3: St. Mark's Maternity Hospital (SMMH) 422
4: Port Hospital 1762
5: Military Hospital 896
6: Central Hospital 454
> 18, .(mean_wt = mean(wt_kg, na.rm = T),
linelist[age mean_ht = mean(ht_cm, na.rm = T)), .(gender, outcome)] #NA представляют категории, где данные отсутствуют
gender outcome mean_wt mean_ht
<char> <char> <num> <num>
1: m Recover 71.90227 178.1977
2: f Death 63.27273 159.9448
3: m Death 71.61770 175.4726
4: f <NA> 64.49375 162.7875
5: m <NA> 72.65505 176.9686
6: f Recover 62.86498 159.2996
7: <NA> Recover 67.21429 175.2143
8: <NA> Death 69.16667 170.7917
9: <NA> <NA> 70.25000 175.5000
> 7, .N, .(month = month(date_hospitalisation), hospital)] linelist[days_onset_hosp
month hospital N
<num> <char> <int>
1: 5 Military Hospital 3
2: 6 Port Hospital 4
3: 7 Port Hospital 8
4: 8 St. Mark's Maternity Hospital (SMMH) 5
5: 8 Military Hospital 9
6: 8 Other 10
7: 8 Port Hospital 10
8: 9 Port Hospital 28
9: 9 Missing 27
10: 9 Central Hospital 10
11: 9 St. Mark's Maternity Hospital (SMMH) 6
12: 10 Missing 2
13: 10 Military Hospital 3
14: 3 Port Hospital 1
15: 4 Military Hospital 1
16: 5 Other 2
17: 5 Central Hospital 1
18: 5 Missing 1
19: 6 Missing 7
20: 6 St. Mark's Maternity Hospital (SMMH) 2
21: 6 Military Hospital 1
22: 7 Military Hospital 3
23: 7 Other 1
24: 7 Missing 2
25: 7 St. Mark's Maternity Hospital (SMMH) 1
26: 8 Central Hospital 2
27: 8 Missing 6
28: 9 Other 9
29: 9 Military Hospital 11
30: 10 Port Hospital 3
31: 10 Other 4
32: 10 St. Mark's Maternity Hospital (SMMH) 1
33: 10 Central Hospital 1
34: 11 Missing 2
35: 11 Port Hospital 1
36: 12 Port Hospital 1
month hospital N
Data.table также позволяет связывать выражения в цепочку следующим образом:
order(-N)][1:3] #1-я выбирает все случаи по больницам, 2-я упорядочивает случаи в порядке убывания, 3-я выделяет 3 больницы с наибольшим количеством случаев linelist[, .N, .(hospital)][
hospital N
<char> <int>
1: Port Hospital 1762
2: Missing 1469
3: Military Hospital 896
В этих примерах мы исходим из того, что строка в таблице данных равна новому случаю, поэтому для представления количества строк в таблице данных можно использовать .N. Другой полезной функцией для представления количества уникальных случаев является uniqueN()
, которая выдает количество уникальных значений при заданных входных данных. Это показано ниже:
uniqueN(gender))] #помните .() в аргументе j выдаст таблицу данных linelist[, .(
V1
<int>
1: 3
Ответом будет 3, так как уникальные значения в столбце пол - это m, f и N/A. Сравните с функцией базового R unique()
, которая выдает все уникальные значения в заданных входных данных:
unique(gender))] linelist[, .(
V1
<char>
1: m
2: f
3: <NA>
Чтобы найти количество уникальных случаев в заданном месяце, мы напишем следующее:
uniqueN(case_id)), .(month = month(date_hospitalisation))] linelist[, .(
month V1
<num> <int>
1: 5 62
2: 6 100
3: 7 198
4: 8 509
5: 9 1170
6: 10 1228
7: 11 813
8: 12 576
9: 1 434
10: 2 310
11: 3 290
12: 4 198
50.6 Добавление и обновление таблиц данных
Оператор :=
используется, чтобы добавить или обновить данные в таблице данных. Добавление столбцов в вашу таблицу данных можно сделать следующим образом:
:= age >= 18] #добавляет один столбец
linelist[, adult c("child", "wt_lbs") := .(age < 18, wt_kg*2.204)] #для добавления нескольких столбцов требуется синтаксис c("") и list() или .()
linelist[, `:=` (bmi_in_range = (bmi > 16 & bmi < 40),
linelist[, no_infector_source_data = is.na(infector) | is.na(source))] #этот метод использует := в качестве функционального оператора `:=`
:= NULL] #удаляет столбец linelist[, adult
Дальнейшие сложные агрегации выходят за рамки этой вводной главы, но идея состоит в том, чтобы предоставить популярную и жизнеспособную альтернативу dplyr для группирования и вычистки данных. Пакет data.table является замечательным пакетом, который позволяет использовать удобный и читаемый код.
50.7 Ресурсы
Вот некоторые полезные ресурсы для информации: * https://cran.r-project.org/web/packages/data.table/vignettes/datatable-intro.html * https://github.com/Rdatatable/data.table * https://s3.amazonaws.com/assets.datacamp.com/img/blog/data+table+cheat+sheet.pdf * https://www.machinelearningplus.com/data-manipulation/datatable-in-r-complete-guide/ * https://www.datacamp.com/community/tutorials/data-table-r-tutorial
Вы можете выполнить любую резюмирующую функцию для группированных данных; см. шпаргалку для получения дополнительной информации: https://s3.amazonaws.com/assets.datacamp.com/blog_assets/datatable_Cheat_Sheet_R.pdf