Feb 6: data.table pt.1

Запись занятия

Запись занятия 6 февраля:

Все записи организованы в плейлист


Таблицы

Таблица — это набор наблюдений по строкам и пространство признаков этих наблюдений в виде набора колонок. Базовый типа для таблиц в R — data.frame. На уровне структуры data.frame это все те же списки, в которых могут храниться разные по типу объекты, однако с требованием равенства длины объектов. Важно: все значения одной колонки могут быть только одного типа (потому что это по сути векторы), а не как в Excel, OpenOffice или каком другом табличном процессоре.

data.frame vs data.table vs dplyr

Несмотря на то, что базовый тип таблиц это data.frame, в настоящее время используются варианты надстроек над этим типом: формат data.table или формат tibble из пактов data.table и dplyr соответственно. Различить их можно по элементам синтаксиса. В частности, data.frame почти всегда использует оператор $ (my_table$my_var), в data.table активно используется оператор :=, а в tibble - оператор %>%.

Пример создания таблицы и выбора строки по условию в data.frame:

# создаём таблицу
set.seed(1234)
df <- data.frame(
  var1 = sample(letters, 5),
  var2 = sample(1:5, 5)
)
# смотрим результат
print(df)
##   var1 var2
## 1    p    1
## 2    v    5
## 3    e    2
## 4    l    3
## 5    o    4
# обращаемся к значениям первой колонки
df$var1
## [1] "p" "v" "e" "l" "o"
# выводим все строки, где в колонке var2 значения меньше или равны 3
df[df$var2 <= 3, ]
##   var1 var2
## 1    p    1
## 3    e    2
## 4    l    3

Аналогичные операции в tibble-формате:

## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
dp <- tibble(
  var1 = sample(letters, 5),
  var2 = sample(1:5, 5)
)
dp %>% 
  filter(var2 <= 3) 
## # A tibble: 3 × 2
##   var1   var2
##   <chr> <int>
## 1 p         1
## 2 e         2
## 3 l         3

data.table

Преимущества data.table

высокая скорость IO / манипуляций (бенчмарки)

параллелизация вычислений по умолчанию

опирается только на base R

лаконичность выражений

бережные апдейты (поддерживается R 3.1)

забота об обратной совместимости

Установка и подключение пакета

# устанавливаем пакет (однократно)
install.packages('data.table')

# подключаем пакет (при каждой сессии)
library(data.table)

Создание data.table-таблиц

Создать data.table можно следующим образом (синтаксис немного напоминает создание именованного списка, как и для всех форматов таблиц):

# подключаем пакет, если не был подключен ранее
library(data.table)
## 
## Attaching package: 'data.table'
## The following objects are masked from 'package:dplyr':
## 
##     between, first, last
# создаем датасет
dt1 <- data.table(
  month_names = month.name,
  month_abb = month.abb,
  month_ord = seq_len(length(month.abb)),
  is_winter = grepl('Jan|Dec|Feb', month.abb)
)
print(dt1)
##     month_names month_abb month_ord is_winter
##  1:     January       Jan         1      TRUE
##  2:    February       Feb         2      TRUE
##  3:       March       Mar         3     FALSE
##  4:       April       Apr         4     FALSE
##  5:         May       May         5     FALSE
##  6:        June       Jun         6     FALSE
##  7:        July       Jul         7     FALSE
##  8:      August       Aug         8     FALSE
##  9:   September       Sep         9     FALSE
## 10:     October       Oct        10     FALSE
## 11:    November       Nov        11     FALSE
## 12:    December       Dec        12      TRUE

Основная формула dt-синтаксиса

Общая формула data.table выглядит как dataset[выбор строк, операции над колонками, группировка]. То есть, указание, какие строки необходимо выделить, осуществляется в первой части (до первой запятой в синтаксисе data.table). Если нет необходимости выделять какие-то строки, перед первой запятой ничего не ставится. Параметр группировки (как и прочие параметры, кроме i и j опциональны).

Также можно провести параллели с синтаксисом SQL-запроса. В терминах SQL data.table-выражения выглядят как таблица[where, select, group by].

Выбор строки

Выбор строк в data.table осуществляется аналогично выбору элементов в векторе — по номеру строки или по какому-то условию. При выборе по номеру строки также можно указать вектор номеров строк, которые необходимо вернуть. При выборке строки по условию проверяется, удовлетворяет ли условию каждый элемент строки в определённой колонке, и если удовлетворяет, выделяется вся строка.

#  выбор по одному номеру строки
dt1[1, ]
##    month_names month_abb month_ord is_winter
## 1:     January       Jan         1      TRUE
# выбор по нескольким номерам строк
# сначала создаём вектор номеров строк
my_rows <- c(2, 5, 8)

# выводим строки, которые мы указали в векторе
dt1[my_rows]
##    month_names month_abb month_ord is_winter
## 1:    February       Feb         2      TRUE
## 2:         May       May         5     FALSE
## 3:      August       Aug         8     FALSE
# или, аналогично, сразу указываем, какие строки хотим выделить
dt1[c(2, 5, 8)]
##    month_names month_abb month_ord is_winter
## 1:    February       Feb         2      TRUE
## 2:         May       May         5     FALSE
## 3:      August       Aug         8     FALSE

Выбор по условию: мы сразу указываем название колонки, к значениям которых будем применять условие-фильтр. Писать в стиле dt1[dt1$month_ord <= 3] избыточно, data.table понимает просто название колонки.

# выводим все строки, в которых в колонке month_ord значения меньше или равны 3
dt1[month_ord <= 3]
##    month_names month_abb month_ord is_winter
## 1:     January       Jan         1      TRUE
## 2:    February       Feb         2      TRUE
## 3:       March       Mar         3     FALSE

Работа с колонками

Обращение к колонке

В синтаксисе data.table все операции над колонками производятся после первой запятой. Выделение колонок также относится к операциям над колонками. Для выделения одной или нескольких колонок необходимо просто указать лист (список) с названиями колонки или колонок.

Если указать название колонки, то будут возвращены значения из этой колонки. Если название обернуть в list(), то будет возвращена таблица, с которой будет одна эта колонка:

dt1[, month_names]
##  [1] "January"   "February"  "March"     "April"     "May"       "June"     
##  [7] "July"      "August"    "September" "October"   "November"  "December"
dt1[, list(month_names)]
##     month_names
##  1:     January
##  2:    February
##  3:       March
##  4:       April
##  5:         May
##  6:        June
##  7:        July
##  8:      August
##  9:   September
## 10:     October
## 11:    November
## 12:    December

Если посмотреть структуру, то оба объекта будут различаться — вектор строковых значений и таблица соответственно:

str(dt1[, month_names])
##  chr [1:12] "January" "February" "March" "April" "May" "June" "July" ...
str(dt1[, list(month_names)])
## Classes 'data.table' and 'data.frame':   12 obs. of  1 variable:
##  $ month_names: chr  "January" "February" "March" "April" ...
##  - attr(*, ".internal.selfref")=<externalptr>

Выбор нескольких колонок

Если мы хотим на основе большой таблицы создать новую таблицу, с какими-то определенными колонками, мы их можем также перечислить в list():

dt1[, list(month_names, month_abb)]
##     month_names month_abb
##  1:     January       Jan
##  2:    February       Feb
##  3:       March       Mar
##  4:       April       Apr
##  5:         May       May
##  6:        June       Jun
##  7:        July       Jul
##  8:      August       Aug
##  9:   September       Sep
## 10:     October       Oct
## 11:    November       Nov
## 12:    December       Dec

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

# выделяем в отдельную таблицу колонку month_names, month_abb
# колонку month_names переименовываем в new_m_names
dt3 <- dt1[, list(new_m_names = month_names, month_abb)]
print(dt3)
##     new_m_names month_abb
##  1:     January       Jan
##  2:    February       Feb
##  3:       March       Mar
##  4:       April       Apr
##  5:         May       May
##  6:        June       Jun
##  7:        July       Jul
##  8:      August       Aug
##  9:   September       Sep
## 10:     October       Oct
## 11:    November       Nov
## 12:    December       Dec

Cоздание колонок

Создать новую колонку в синтаксисе data.table можно с помощью оператора :=. Это точно такая же операция над колонками, как и все прочие, просто происходит создание новой колонки:

dt1[, new_col := 12:1]
dt1
##     month_names month_abb month_ord is_winter new_col
##  1:     January       Jan         1      TRUE      12
##  2:    February       Feb         2      TRUE      11
##  3:       March       Mar         3     FALSE      10
##  4:       April       Apr         4     FALSE       9
##  5:         May       May         5     FALSE       8
##  6:        June       Jun         6     FALSE       7
##  7:        July       Jul         7     FALSE       6
##  8:      August       Aug         8     FALSE       5
##  9:   September       Sep         9     FALSE       4
## 10:     October       Oct        10     FALSE       3
## 11:    November       Nov        11     FALSE       2
## 12:    December       Dec        12      TRUE       1

Модификация колонок

Оператор := позволяет изменять объект на месте, поэтому мы можем просто колонке присвоить новое значение. Фактически мы на основе старой колонки создаем вектор новых значений и записываем его в в колонку с тем же названием.

dt1[, new_col := new_col + 5]
dt1
##     month_names month_abb month_ord is_winter new_col
##  1:     January       Jan         1      TRUE      17
##  2:    February       Feb         2      TRUE      16
##  3:       March       Mar         3     FALSE      15
##  4:       April       Apr         4     FALSE      14
##  5:         May       May         5     FALSE      13
##  6:        June       Jun         6     FALSE      12
##  7:        July       Jul         7     FALSE      11
##  8:      August       Aug         8     FALSE      10
##  9:   September       Sep         9     FALSE       9
## 10:     October       Oct        10     FALSE       8
## 11:    November       Nov        11     FALSE       7
## 12:    December       Dec        12      TRUE       6

Можно совмещать фильтрацию по строкам и модификацию колонок. Например, для всех строк, где в колонке month_ord значения меньше или равны 5, в колонке new_col проставляем NA:

dt1[month_ord <= 5, new_col := NA]
dt1
##     month_names month_abb month_ord is_winter new_col
##  1:     January       Jan         1      TRUE      NA
##  2:    February       Feb         2      TRUE      NA
##  3:       March       Mar         3     FALSE      NA
##  4:       April       Apr         4     FALSE      NA
##  5:         May       May         5     FALSE      NA
##  6:        June       Jun         6     FALSE      12
##  7:        July       Jul         7     FALSE      11
##  8:      August       Aug         8     FALSE      10
##  9:   September       Sep         9     FALSE       9
## 10:     October       Oct        10     FALSE       8
## 11:    November       Nov        11     FALSE       7
## 12:    December       Dec        12      TRUE       6

Удаление колонок

Удаление колонок осуществляется схожим образом, просто колонке присваивается значение NULL

## удаление колонок
dt1[, new_col := NULL]
dt1
##     month_names month_abb month_ord is_winter
##  1:     January       Jan         1      TRUE
##  2:    February       Feb         2      TRUE
##  3:       March       Mar         3     FALSE
##  4:       April       Apr         4     FALSE
##  5:         May       May         5     FALSE
##  6:        June       Jun         6     FALSE
##  7:        July       Jul         7     FALSE
##  8:      August       Aug         8     FALSE
##  9:   September       Sep         9     FALSE
## 10:     October       Oct        10     FALSE
## 11:    November       Nov        11     FALSE
## 12:    December       Dec        12      TRUE

Манипуляции с таблицами

rbind()

Функция rbind()(от row bind) используется для объединение двух или более таблиц по строкам. То есть, в результате получается таблица с таким же количеством колонок, но с увеличенным числом строк - по количеству строк в объединяемых таблицах.

Нередко в объединяемых таблицах отсутствует какая-нибудь колонка или колонки перепутаны. В таких случаях необходимо использовать аргументы use.names = TRUE (проверка названий колонок при объединение) и fill = TRUE (создание колонки с NA-значениями). Обратите внимание, это работает только с data.table-объектами.

# создаем первую таблицу
df1 <- data.table(tb = 'table_1',
                  col1 = sample(9, 3),
                  col3 = 'only in table1',
                  col2 = sample(letters, 3))

# создаем вторую таблицу
df2 <- data.table(tb = 'table_2',
                  col4 = 'only in table2',
                  col1 = sample(9, 3),
                  col2 = sample(letters, 3))

# объединяем по строкам
rbind(df1, df2, fill = TRUE)
##         tb col1           col3 col2           col4
## 1: table_1    2 only in table1    z           <NA>
## 2: table_1    7 only in table1    f           <NA>
## 3: table_1    6 only in table1    o           <NA>
## 4: table_2    4           <NA>    x only in table2
## 5: table_2    6           <NA>    d only in table2
## 6: table_2    8           <NA>    y only in table2

Полезные ссылки

Моя серия вебинаров по data.table. Есть как запись, так и конспект. На занятиях мы будем рассматривать лишь половину или треть материала вебинаров.

Базовые операции одновременно в data.table и dplyr-синтаксисе. Много полезных приемов, и, в целом, наглядно. Смотрите блоки по data.table, dplyr синтаксис можно игнорировать или смотреть для общего развития (это весьма часто используемый синтаксис в академии).

Перевод документации data.table от Андрея Огурцова. Полезно для понимания разных нюансов работы data.table

Продвинутый data.table для желающих, много неочевидных нюансов и трюков.

Экзотические возможности и ключевые слова, для совсем экстремалов. Заметка важна в первую очередь внутренними ссылками на разные релевантные и поясняющие ресурсы.


Домашнее задание

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

  • подключите библиотеку data.table (установите, если не была установлена)
  • скачайте файл titanic3.csv
  • с помощью команды titanic <- fread('titanic3.csv') импортируйте файл в рабочее окружение. Прочитайте справку по функции fread и попробуйте импортировать данные без сохранения на диск.
  • посмотрите с помощью команды class() объекта. если он отличается от data.table - сконвертируйте в data.table

работа со строками

  • выберите случайным образом 5 строк из таблицы titanic
  • выберите те строки, где возраст пассажира меньше 1 (младенцы)
  • выберите те строки, где возраст пассажира в диапазоне 20 до 50 лет
  • выберите строки по выжившим младенцам (survived)
  • выберите пассажирок, которые имеют титул lady (используйте grep()/grepl(), прочитайте справку по этим функциям и примеры использования)

работа с колонками

  • посчитайте средний возраст пассажиров
  • аналогично, посчитайте summary() по возрасту женщин
  • выделите в отдельный датасет всех погибших пассажиров, оставьте для них только значения пола, возраста и класса билета (pclass), переменную pclass переименуйте в class
  • в полученном датасете посчитайте количество пассажиров, их средний и медианный возраст, разброс по возрасту (sd())