Пакет data.table. Вступ. Джерело.

library(data.table)
library(dplyr)
library(plotly)

Завантажимо дані по польотах flights14 використовуючи функцію fread().

address <- "https://raw.githubusercontent.com/wiki/arunsrinivasan/flights/NYCflights14/flights14.csv"
flights <- fread(address, showProgress = T)

Подивимось на дані:

glimpse(flights)
## Observations: 253,316
## Variables: 17
## $ year      (int) 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014...
## $ month     (int) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
## $ day       (int) 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
## $ dep_time  (int) 914, 1157, 1902, 722, 1347, 1824, 2133, 1542, 1509, ...
## $ dep_delay (int) 14, -3, 2, -8, 2, 4, -2, -3, -1, -2, -5, 7, 3, 142, ...
## $ arr_time  (int) 1238, 1523, 2224, 1014, 1706, 2145, 37, 1906, 1828, ...
## $ arr_delay (int) 13, 13, 9, -26, 1, 0, -18, -14, -17, -14, -17, -5, 1...
## $ cancelled (int) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ carrier   (chr) "AA", "AA", "AA", "AA", "AA", "AA", "AA", "AA", "AA"...
## $ tailnum   (chr) "N338AA", "N335AA", "N327AA", "N3EHAA", "N319AA", "N...
## $ flight    (int) 1, 3, 21, 29, 117, 119, 185, 133, 145, 235, 172, 177...
## $ origin    (chr) "JFK", "JFK", "JFK", "LGA", "JFK", "EWR", "JFK", "JF...
## $ dest      (chr) "LAX", "LAX", "LAX", "PBI", "LAX", "LAX", "LAX", "LA...
## $ air_time  (int) 359, 363, 351, 157, 350, 339, 338, 356, 161, 349, 16...
## $ distance  (int) 2475, 2475, 2475, 1035, 2475, 2454, 2475, 2475, 1089...
## $ hour      (int) 9, 11, 19, 7, 13, 18, 21, 15, 15, 18, 16, 17, 12, 19...
## $ min       (int) 14, 57, 2, 22, 47, 24, 33, 42, 9, 48, 55, 52, 53, 7,...

Структура таблиці data.table: DT [ i , j , by ] . i - робота із рядками; j - що обчислити; by - як згрупувати.

1. Використання i.

Виберемо всі рейси із початковою точкою (аеропортом) “JFK” за червень:

ans <- flights[origin == "JFK" & month == 6]
head(ans, 5)
##    year month day dep_time dep_delay arr_time arr_delay cancelled carrier
## 1: 2014     6   1      851        -9     1205        -5         0      AA
## 2: 2014     6   1     1220       -10     1522       -13         0      AA
## 3: 2014     6   1      718        18     1014        -1         0      AA
## 4: 2014     6   1     1024        -6     1314       -16         0      AA
## 5: 2014     6   1     1841        -4     2125       -45         0      AA
##    tailnum flight origin dest air_time distance hour min
## 1:  N787AA      1    JFK  LAX      324     2475    8  51
## 2:  N795AA      3    JFK  LAX      329     2475   12  20
## 3:  N784AA      9    JFK  LAX      326     2475    7  18
## 4:  N791AA     19    JFK  LAX      320     2475   10  24
## 5:  N790AA     21    JFK  LAX      326     2475   18  41

У порівнянні із data.frame не потрібно використовувати символ $ (DT$colA), тобто до стовбців таблиці звертаємось як до змінних.

Вибрати перші два рядки таблиці:

ans <- flights[1:2]
ans
##    year month day dep_time dep_delay arr_time arr_delay cancelled carrier
## 1: 2014     1   1      914        14     1238        13         0      AA
## 2: 2014     1   1     1157        -3     1523        13         0      AA
##    tailnum flight origin dest air_time distance hour min
## 1:  N338AA      1    JFK  LAX      359     2475    9  14
## 2:  N335AA      3    JFK  LAX      363     2475   11  57

Відсортувати flights по колонці origin в порядку збільшення, а потім dest в порядку зменшення:

ans <- flights[order(origin, -dest)] 
head(ans)
##    year month day dep_time dep_delay arr_time arr_delay cancelled carrier
## 1: 2014     1   5      836         6     1151        49         0      EV
## 2: 2014     1   6      833         7     1111        13         0      EV
## 3: 2014     1   7      811        -6     1035       -13         0      EV
## 4: 2014     1   8      810        -7     1036       -12         0      EV
## 5: 2014     1   9      833        16     1055         7         0      EV
## 6: 2014     1  13      923        66     1154        66         0      EV
##    tailnum flight origin dest air_time distance hour min
## 1:  N12175   4419    EWR  XNA      195     1131    8  36
## 2:  N24128   4419    EWR  XNA      190     1131    8  33
## 3:  N12142   4419    EWR  XNA      179     1131    8  11
## 4:  N11193   4419    EWR  XNA      184     1131    8  10
## 5:  N14198   4419    EWR  XNA      181     1131    8  33
## 6:  N12157   4419    EWR  XNA      188     1131    9  23

Відбувається автоматичне переформатування функції base::order() в forder, яка працює в рази швидше:

t.1 <- system.time(flights[base::order(origin)])
t.2 <- system.time(flights[order(origin)])
t.1; t.2
##    user  system elapsed 
##   1.376   0.010   1.386
##    user  system elapsed 
##   0.030   0.009   0.039

2. Вибір стовбців в j.

Вибрати стовбець arr_delay і повернути його як vector:

ans <- flights[, arr_delay]
head(ans)
## [1]  13  13   9 -26   1   0

Вибрати стовбець arr_delay і повернути як data.table- потрібно обернути змінну в list() або .(). (.() - це псевдонім list()):

ans <- flights[, .(arr_delay)]
head(ans)
##    arr_delay
## 1:        13
## 2:        13
## 3:         9
## 4:       -26
## 5:         1
## 6:         0

Важливо! data.table i data.frame - це списки із стовбцями рівної довжини і вказаними класами (типами даних)

Вибрати стовбці arr_delay i dep_delay:

ans <- flights[, .(arr_delay, dep_delay)]
head(ans, 5)
##    arr_delay dep_delay
## 1:        13        14
## 2:        13        -3
## 3:         9         2
## 4:       -26        -8
## 5:         1         2

Вибрати стобці arr_delay i dep_delay, переіменувати їх в delay_arr i delay_dep:

ans <- flights[, .(delay_arr = arr_delay, delay_dep = dep_delay)]
head(ans)
##    delay_arr delay_dep
## 1:        13        14
## 2:        13        -3
## 3:         9         2
## 4:       -26        -8
## 5:         1         2
## 6:         0         4

Скільки рейсів мали загальну затримку delay < 0 ?:

ans <- flights[, sum((arr_delay + dep_delay) < 0)]
ans
## [1] 141814

Розрахувати середню затримку прильоту і відльоту для всіх рейсів з початковою точкою JFK за червень:

ans <- flights[i = origin == "JFK" & month == 6,
               j = .(m.arr = mean(arr_delay), m.dep = mean(dep_delay))]
ans
##       m.arr    m.dep
## 1: 5.839349 9.807884

Скільки рейсів було зроблено в 2014 році із аеропорту JFK за червень:

ans <- flights[i = year == 2014 & origin == "JFK" & month == 6,
               j = length(dest)]
ans
## [1] 8422

Можна використати .N для визначення кількості даних в піднаборі (особливо корисно при використанні із by):

ans.1 <- flights[i = year == 2014 & origin == "JFK" & month == 6,
                 j = .N]   # vector 
ans.2 <- flights[i = year == 2014 & origin == "JFK" & month == 6,
                 j = .(count = .N)]   # data.table

ans.1; ans.2
## [1] 8422
##    count
## 1:  8422

Вибрати стовбці arr_delay і dep_delay як в data.frame (треба поставити аргумент with = FALSE - заборона посилання на стовбці як на змінні):

ans <- flights[, c("arr_delay", "dep_delay"), with = FALSE]
head(ans)
##    arr_delay dep_delay
## 1:        13        14
## 2:        13        -3
## 3:         9         2
## 4:       -26        -8
## 5:         1         2
## 6:         0         4
ans <- flights[, year:day, with = FALSE]
head(ans)
##    year month day
## 1: 2014     1   1
## 2: 2014     1   1
## 3: 2014     1   1
## 4: 2014     1   1
## 5: 2014     1   1
## 6: 2014     1   1

3. Агрегація. by.

Визначити кількість рейсів по кожному аеропорту:

ans <- flights[, .(count = .N), by = origin]
ans
##    origin count
## 1:    JFK 81483
## 2:    LGA 84433
## 3:    EWR 87400

Розрахувати кількість рейсів із кожного аеропорту перевізника АА:

ans <- flights[i  = carrier == "AA",
               j  = .(count = .N),
               by = origin]
ans
##    origin count
## 1:    JFK 11923
## 2:    LGA 11730
## 3:    EWR  2649

К-сть рейсів для origin i dest перевізника АА:

ans <- flights[carrier == "AA", 
               .(count = .N), 
               by = .(origin, dest)]
head(ans)
##    origin dest count
## 1:    JFK  LAX  3387
## 2:    LGA  PBI   245
## 3:    EWR  LAX    62
## 4:    JFK  MIA  1876
## 5:    JFK  SEA   298
## 6:    EWR  MIA   848

Середня затримка прильоту і відльоту для кожної пари origin i dest в кожному місяці перевізника АА:

ans <- flights[carrier == "AA",
               .(m.arr = mean(arr_delay), m.dep = mean(dep_delay)),
               .(origin, dest, month)]
ans
##      origin dest month      m.arr      m.dep
##   1:    JFK  LAX     1   6.590361 14.2289157
##   2:    LGA  PBI     1  -7.758621  0.3103448
##   3:    EWR  LAX     1   1.366667  7.5000000
##   4:    JFK  MIA     1  15.720670 18.7430168
##   5:    JFK  SEA     1  14.357143 30.7500000
##  ---                                        
## 196:    LGA  MIA    10  -6.251799 -1.4208633
## 197:    JFK  MIA    10  -1.880184  6.6774194
## 198:    EWR  PHX    10  -3.032258 -4.2903226
## 199:    JFK  MCO    10 -10.048387 -1.6129032
## 200:    JFK  DCA    10  16.483871 15.5161290

Впорядкувати по змінним, які групуються - замість by вказати keyby:

ans <- flights[carrier == "AA",
               .(m.arr = mean(arr_delay), m.dep = mean(dep_delay)),
               keyby = .(origin, dest, month)]
ans
##      origin dest month      m.arr      m.dep
##   1:    EWR  DFW     1   6.427673 10.0125786
##   2:    EWR  DFW     2  10.536765 11.3455882
##   3:    EWR  DFW     3  12.865031  8.0797546
##   4:    EWR  DFW     4  17.792683 12.9207317
##   5:    EWR  DFW     5  18.487805 18.6829268
##  ---                                        
## 196:    LGA  PBI     1  -7.758621  0.3103448
## 197:    LGA  PBI     2  -7.865385  2.4038462
## 198:    LGA  PBI     3  -5.754098  3.0327869
## 199:    LGA  PBI     4 -13.966667 -4.7333333
## 200:    LGA  PBI     5 -10.357143 -6.8571429

4. Формування ланцюжка (chaining)

Визначити к-сть рейсів для кожної пари origin i dest перевізника АА і впорядкувати ans по origin в порядку зростання і dest в порядку зменшення:

ans <- flights[carrier == "AA", .N, by = .(origin, dest)]
ans <- ans[order(origin, -dest)]
head(ans)
##    origin dest    N
## 1:    EWR  PHX  121
## 2:    EWR  MIA  848
## 3:    EWR  LAX   62
## 4:    EWR  DFW 1618
## 5:    JFK  STT  229
## 6:    JFK  SJU  690

Але краще використати Ланцюжок (chaining) - можна не зберігати таблицю, а зразу сортувати:

ans <- flights[carrier == "AA", .N, by = .(origin, dest)][order(origin, -dest)]
head(ans)
##    origin dest    N
## 1:    EWR  PHX  121
## 2:    EWR  MIA  848
## 3:    EWR  LAX   62
## 4:    EWR  DFW 1618
## 5:    JFK  STT  229
## 6:    JFK  SJU  690

5. Множина стовбців j - .SD (Subset of Data).

DT <- data.table(ID = c("a", "a", "a", "b", "b", "c"), a = 1:6, b = 7:12, c = 13:18)
DT
##    ID a  b  c
## 1:  a 1  7 13
## 2:  a 2  8 14
## 3:  a 3  9 15
## 4:  b 4 10 16
## 5:  b 5 11 17
## 6:  c 6 12 18

.SD зберігає в собі всі колонки окрім колонки в by, створюються дані, які відповідають відповідно даним в групуючій колонці.

DT[, print(.SD), by = ID]
##    a b  c
## 1: 1 7 13
## 2: 2 8 14
## 3: 3 9 15
##    a  b  c
## 1: 4 10 16
## 2: 5 11 17
##    a  b  c
## 1: 6 12 18
## Empty data.table (0 rows) of 1 col: ID

Щоб розрахувати середнє для кожного значення із ID, для всіх колонок використаємо lapply(оскільки data.table - це список, елементи якого це колонки). Оскільки lapply повертає список, то не треба додатково заключати її в .():

ans <- DT[, lapply(.SD, mean), by = ID]
ans
##    ID   a    b    c
## 1:  a 2.0  8.0 14.0
## 2:  b 4.5 10.5 16.5
## 3:  c 6.0 12.0 18.0

Для того, щоб вибрати вибрати необхідні нам стовбці потрібно використовувати аргумент .SDcols - він приймає імена або номера стовбців. Обрахуємо середнє для arr_delay і dep_delay, згрупуємо по origin, dest і month:

ans <- flights[, 
               lapply(.SD, mean), 
               by = .(origin, dest, month),
               .SDcols = c("arr_delay", "dep_delay")]
ans
##       origin dest month  arr_delay dep_delay
##    1:    JFK  LAX     1  11.342956 20.521940
##    2:    LGA  PBI     1  23.000000 21.382143
##    3:    EWR  LAX     1   7.906494 18.745455
##    4:    JFK  MIA     1  20.454902 22.505882
##    5:    JFK  SEA     1  15.101449 27.253623
##   ---                                       
## 1913:    EWR  SAT    10 -15.500000  9.333333
## 1914:    EWR  BTV    10  -2.000000 -3.800000
## 1915:    EWR  ORF    10   6.833333 12.000000
## 1916:    LGA  DAL    10 -16.000000 -6.266667
## 1917:    LGA  CVG    10 -11.333333 -5.666667

Виберемо два перших рядка для кожного month

ans <- flights[, head(.SD, 2), by = month]
ans
##     month year day dep_time dep_delay arr_time arr_delay cancelled carrier
##  1:     1 2014   1      914        14     1238        13         0      AA
##  2:     1 2014   1     1157        -3     1523        13         0      AA
##  3:     2 2014   1      859        -1     1226         1         0      AA
##  4:     2 2014   1     1155        -5     1528         3         0      AA
##  5:     3 2014   1      849       -11     1306        36         0      AA
##  6:     3 2014   1     1157        -3     1529        14         0      AA
##  7:     4 2014   1     1812        -8     1927       -23         0      MQ
##  8:     4 2014   1     1812        -8     1949       -11         0      MQ
##  9:     5 2014   1     1743        43     1955         5         0      AA
## 10:     5 2014   1      759        -1     1057       -38         0      AA
## 11:     6 2014   1      851        -9     1205        -5         0      AA
## 12:     6 2014   1     1220       -10     1522       -13         0      AA
## 13:     7 2014   1      727        -3     1043        -2         0      VX
## 14:     7 2014   1      904        -6     1201       -14         0      VX
## 15:     8 2014   1      822        -3      925        -5         0      AA
## 16:     8 2014   1     1549        19     2000        55         0      AA
## 17:     9 2014   1      851        -9     1144       -26         0      AA
## 18:     9 2014   1     1223        -7     1509       -21         0      AA
## 19:    10 2014   1     1412        -5     1551       -15         0      EV
## 20:    10 2014   1     1243        -7     1351       -21         0      EV
##     tailnum flight origin dest air_time distance hour min
##  1:  N338AA      1    JFK  LAX      359     2475    9  14
##  2:  N335AA      3    JFK  LAX      363     2475   11  57
##  3:  N783AA      1    JFK  LAX      358     2475    8  59
##  4:  N784AA      3    JFK  LAX      358     2475   11  55
##  5:  N784AA      1    JFK  LAX      375     2475    8  49
##  6:  N787AA      3    JFK  LAX      368     2475   11  57
##  7:  N514MQ   3486    LGA  BNA      113      764   18  12
##  8:  N543MQ   3501    LGA  RDU       71      431   18  12
##  9:  N3ELAA     45    JFK  LAS      288     2248   17  43
## 10:  N789AA     59    JFK  SFO      330     2586    7  59
## 11:  N787AA      1    JFK  LAX      324     2475    8  51
## 12:  N795AA      3    JFK  LAX      329     2475   12  20
## 13:  N361VA     11    JFK  SFO      343     2586    7  27
## 14:  N836VA    161    EWR  LAX      320     2454    9   4
## 15:  N3GDAA     84    JFK  BOS       40      187    8  22
## 16:  N783AA     85    JFK  SFO      373     2586   15  49
## 17:  N794AA      1    JFK  LAX      325     2475    8  51
## 18:  N798AA      3    JFK  LAX      320     2475   12  23
## 19:  N12567   4100    EWR  MEM      140      946   14  12
## 20:  N13538   4104    EWR  BNA      109      748   12  43

.SD містить всі рядки для кожної групи month ми просто беремо перші два рядка із кожної групи head(.SD, 2) повертає data.table як list, тому не потірбно ставити .().

Можна об“єднати колонки:

DT[, .(val = c(a, b)), by = ID]
##     ID val
##  1:  a   1
##  2:  a   2
##  3:  a   3
##  4:  a   7
##  5:  a   8
##  6:  a   9
##  7:  b   4
##  8:  b   5
##  9:  b  10
## 10:  b  11
## 11:  c   6
## 12:  c  12
DT[, .(val = list(c(a, b))), by = ID]
##    ID         val
## 1:  a 1,2,3,7,8,9
## 2:  b  4, 5,10,11
## 3:  c        6,12

РЕЗЮМЕ

  1. Використання i:
  • 1.1. ми можемо відбирати рядки як в data.frame, за виключенням того, що не потірбно весь час використовувати символ $ - DT$a. Бо стовбці в data.table ведуть себе як змінні;
  • 1.2 для впорядкування data.table використовується більш швидка внутрішня реалізація фукнції order() (forder);
  1. Використання j:
  • 2.1 вибір стовбців в стилі data.table: DT[, .(colA, colB)];
  • 2.2 вибір стовбців в стилі data.frame: DT[, c("colA", "colB"), with = FALSE];
  • 2.3 обрахунки на основі стовбців: DT[, .(sum(colA), mean(colB))];
  • 2.4 комбінація із і: DT[colA > 0, sum(colB)];
  1. Використання by:
  • 3.1 групування по стовбцях, вказуючи список стовбців або символьний вектор із іменами стовбців;
  • 3.2 може опрацьовувати множинні стовбці;
  • 3.3 можна використовувати keyby для групуючих стовбців, для їх подальшого впорядкування по групах;
  • 3.4 можна використовувати .SD(всі стовбці) і .SDcols(вибрані стовбці) для виконання базових фукнцій над стовбцями:
    • 3.4.1 DT[, lapply(.SD, fun), by= ., .SDcols = .] - застосовує fun до всіх стовців, які вказані в .SDcols;
    • 3.4.2 DT[., head(.SD, 2), by = .] - виводить перші два рядки для кожної групи;
    • 3.4.3 DT[col > val, head(.SD, 1), by = .] - комбінація i, j, by.

ВАЖЛИВО!!! Оскільки j-вираз повертає список, кожен елемент списку стає стовбцем підсумкової таблиці data.table.

# dplyr
f.1 <- flights %>%
  mutate_each("factor", origin, tailnum) %>%
  group_by(origin, tailnum) %>%
  summarise(count = n(),
            m.dist = mean(distance, na.rm = T),
            m.dep.delay = mean(dep_delay, na.rm = T)) 
head(f.1)
##   origin tailnum count    m.dist m.dep.delay
## 1    JFK  N338AA    72 2375.7083    13.54167
## 2    JFK  N335AA    32 2502.7500    17.31250
## 3    JFK  N327AA     7 2277.0000    21.85714
## 4    LGA  N3EHAA    47  999.5957    37.29787
## 5    JFK  N319AA    90 2337.1000    15.04444
## 6    EWR  N3DEAA     6 1890.6667    -2.00000
# data.table
f.2 <- flights[, 
               .(count = .N,
                 m.dist = mean(distance, na.rm = T),
                 m.dep.delay = mean(dep_delay, na.rm = T)),
               .(origin, tailnum)]
head(f.2)
##    origin tailnum count    m.dist m.dep.delay
## 1:    JFK  N338AA    72 2375.7083    13.54167
## 2:    JFK  N335AA    32 2502.7500    17.31250
## 3:    JFK  N327AA     7 2277.0000    21.85714
## 4:    LGA  N3EHAA    47  999.5957    37.29787
## 5:    JFK  N319AA    90 2337.1000    15.04444
## 6:    EWR  N3DEAA     6 1890.6667    -2.00000
g <- f.1 %>%
  ggplot(aes(x = m.dist , y = m.dep.delay, colour = origin )) + 
  geom_point(alpha = 1/4) +
  geom_smooth(method = "loess", se = F, size = 1) +
  scale_y_continuous(limits = c(0, quantile(f.1$m.dep.delay, 0.99))) +
  scale_x_continuous(limits = c(0, quantile(f.1$m.dist, 0.99))) +
  scale_colour_manual(labels = c("EWR", "JFK", "LGA"), values = c("orange1", "royalblue2", "red4"))

g