Как вы уже, наверное, убедились, базовый R умеет очень много, в том числе и для работы с данными. Однако какие-то операции все равно выполнить довольно непросто.
Возьмем, например, задачу агрегации: вам нужно посчитать средний рост супергероев отдельно для мужчин и для женщин (а еще и для NA, за компанию). Три группы еще ничего, а если бы их было 10, 50 или 200? В базовом R для этого есть специальная функция aggregate(), но она довольно неудобная.
Поэтому стали появляться пакеты, которые пытаются сделать агрегацию и другие непростые операции максимально безболезненными способами. Основных таких пакетов два: {data.table} и {tidyverse}. Это огромные пакеты, которые очень сильно изменяют работу в R, в том числе в плане стиля и используемой парадигмы. Тем не менее, в основе своей стоит все то, что мы прошли раньше.
9.1 Подход {data.table}
{data.table} – это распространенный пакет, который позволяет анализировать датафреймы максимально быстро и с помощью очень лаконичного кода.
install.packages("data.table")
Давайте импортируем наш набор данных про супергероев. Для этого воспользуемся функцией fread() из пакета {data.table}. Эта функция нам уже знакома как функция для импорта больших наборов данных Глава 6.7.
“f” в fread() означает “fast and friendly”: эта функция очень быстрая и довольно хорошо угадывает формат текстовой таблицы.
Warning in
fread("https://raw.githubusercontent.com/Pozdniakov/tidy_stats/master/data/heroes_information.csv",
: na.strings[4]==" " consists only of whitespace, ignoring. Since
strip.white=TRUE (default), use na.strings="" to specify that any number of
spaces in a string column should be read as <NA>.
Функция fread() создает не просто датафрейм, а дататейбл (datatable):
heroes_dt
V1 name Gender Eye color Race Hair color
<int> <char> <char> <char> <char> <char>
1: 0 A-Bomb Male yellow Human No Hair
2: 1 Abe Sapien Male blue Icthyo Sapien No Hair
3: 2 Abin Sur Male blue Ungaran No Hair
4: 3 Abomination Male green Human / Radiation No Hair
5: 4 Abraxas Male blue Cosmic Entity Black
---
730: 729 Yellowjacket II Female blue Human Strawberry Blond
731: 730 Ymir Male white Frost Giant No Hair
732: 731 Yoda Male brown Yoda's species White
733: 732 Zatanna Female blue Human Black
734: 733 Zoom Male red <NA> Brown
Height Publisher Skin color Alignment Weight
<num> <char> <char> <char> <int>
1: 203.0 Marvel Comics <NA> good 441
2: 191.0 Dark Horse Comics blue good 65
3: 185.0 DC Comics red good 90
4: 203.0 Marvel Comics <NA> bad 441
5: NA Marvel Comics <NA> bad NA
---
730: 165.0 Marvel Comics <NA> good 52
731: 304.8 Marvel Comics white good NA
732: 66.0 George Lucas green good 17
733: 170.0 DC Comics <NA> good 57
734: 185.0 DC Comics <NA> bad 81
class(heroes_dt)
[1] "data.table" "data.frame"
Дататейбл – это “улучшенный” датафрейм: с ним работают все те функции, которые мы применяли для датафрейма, специальные функции для дататейбла, а что-то работает немного по-другому по сравнению с датафреймом. Например, оператор [, т.е. квадратные скобки.
Давайте посмотрим по-внимательнее как это происходит на примере расчета среднего роста супергероев, группируя по полу:
heroes_dt[, mean(Height, na.rm =TRUE), by = Gender]
Уух! Выглядит монструозно, да? Зато как мы все сделали используя минимальное количество знаков. Заметьте, что здесь необычного для нас:
Не нужно прописывать heroes_dt$Alignment, поиск переменной будет начинаться с колонок дататейбла.
Там, где мы раньше выбирали колонки, мы еще и расчеты можем вести.
Внутри квадратных скобок появилась вторая запятая, т.е. третье поле, в котором мы прописали группировку.
Несколько операций прописываются путем соединения квадратных скобочек, код превращается в эдакий паровозик1.
И это не все отличия!
На сайте пакета {data.table} особенно уделяется вниманию скорости {data.table}, приводя в качестве доказательства бэнчмарк, где сравниваются по скорости различные инструменты для работы с данными. {data.table} почти на порядок обгоняет как {dplyr}, так и питоновский pandas – самый используемый пакет для анализа данных в Python.
Разработчики {data.table} делают особый акцент на “консервативности” пакета: у него нет никаких зависимостей (в этом плане пакет {data.table} обгоняет большинство российских экспатов в Тбилиси), ему достаточно очень старой версии R, функционирование пакета не будет ломаться из-за выкинутых устаревших функций. В общем, {data.table} очень суров и уважаем программистами. Он и не особо пытается понравиться рядовым пользователям. Зато освоив его, вы сможете творить магию: то, что с помощью базового R, tidyverse или Python будет выполняться очень долго (если выполнится вообще), {data.table} сможет сделать гораздо быстрее, иногда в десятки и сотни раз!
Очень сильно, не правда ли? Чем же может ответить tidyverse?
9.2 Подход tidyverse
Давайте посмотрим, как будет выглядеть решение тех же задач (отбор строк по условию, агрегация и сортировка) в tidyverse.
Не пугайтесь сообщений, все в порядке. Во-первых, пакет {tidyverse} – это не просто пакет, а “пакет с пакетами” (да-да, как у вас дома), который подключает сразу несколько других пакетов, которые составляют ядро tidyverse. Список и версии этих пакетов {tidyverse} выводит при подключении. Разные пакеты tidyverse мы очень детально разберем позже (Глава 10 ), а сейчас просто посмотрите, как это все выглядит.
Warning: One or more parsing issues, call `problems()` on your data frame for details,
e.g.:
dat <- vroom(...)
problems(dat)
Rows: 734 Columns: 11
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (8): name, Gender, Eye color, Race, Hair color, Publisher, Skin color, A...
dbl (3): ...1, Height, Weight
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Функция read_csv() (не путать с функцией из базового R – read.csv()!) возвращает тиббл – “улучшенный” датафрейм, примерно как это было с дататейблом.
heroes_tbl
# A tibble: 734 × 11
...1 name Gender `Eye color` Race `Hair color` Height Publisher
<dbl> <chr> <chr> <chr> <chr> <chr> <dbl> <chr>
1 0 A-Bomb Male yellow Human No Hair 203 Marvel C…
2 1 Abe Sapien Male blue Icthyo … No Hair 191 Dark Hor…
3 2 Abin Sur Male blue Ungaran No Hair 185 DC Comics
4 3 Abomination Male green Human /… No Hair 203 Marvel C…
5 4 Abraxas Male blue Cosmic … Black NA Marvel C…
6 5 Absorbing Man Male blue Human No Hair 193 Marvel C…
7 6 Adam Monroe Male blue <NA> Blond NA NBC - He…
8 7 Adam Strange Male blue Human Blond 185 DC Comics
9 8 Agent 13 Female blue <NA> Blond 173 Marvel C…
10 9 Agent Bob Male brown Human Brown 178 Marvel C…
# ℹ 724 more rows
# ℹ 3 more variables: `Skin color` <chr>, Alignment <chr>, Weight <dbl>
class(heroes_tbl)
[1] "spec_tbl_df" "tbl_df" "tbl" "data.frame"
Теперь же сделаем то же самое с нашими данными, что мы делали с помощью {data.table}:
# A tibble: 3 × 2
Gender mean_height
<chr> <dbl>
1 Male 189.
2 <NA> 180.
3 Female 175.
Очень сильно отличается от того, как мы работали раньше! Хотя в основе лежит все тот же R. Код, написанный в tidyverse, нарочито многословен (особенно по сравнению с {data.table}), каждая отдельная операция имеет свою функцию. Писать нужно больше, зато это гораздо легче: меньше нужно думать, какими хитрыми трюками сделать преобразование данных. Нужно просто разделить весь процесс преобразования данных на отдельные операции и последовательно прописать их. Код получается аккуратный и очень читаемый, даже для человека, который не знает tidyverse или даже R в целом. Даже этот новый оператор %>% выглядит довольно понятно: его можно прочитать как “затем”.
Заметьте, что tidyverse выводит очень подробные сообщения, которые даже выглядят очень красиво: со всякими иконками, красивым форматированием. Разработчики tidyverse работают над тем, чтобы делать свой интерфейс максимально понятным для пользователя: говорящие сами за себя названия функций, куча удобных фишек на все случаи жизни.
tidyverse постоянно обновляется, регулярно появляются новые функции, а старые функции заменяются на более удобные новые. И это не всегда плюс: обновив пакеты, установленные год назад, вы можете обнаружить, что старый код перестал работать! Мол, мы тут придумали, как сделать лучше, переписывайте код заново (или используйте старые версии пакетов).
Разработчики tidyverse, в целом, не стремится за высокой скоростью. Часто можно заметить, что новые функции работают довольно медленно. Но если у вас строчек меньше миллиона, то разницу в скорости с {data.table} вы едва ли заметите.
Команда разработчиков tidyverse работает на компанию Posit (бывшая RStudio). Поэтому в RStudio вы найдете несколько “шпаргалок” для tidyverse, но не для {data.table}. Они также активно активно работают над популяризацией tidyverse, стараясь сделать вход в него максимально комфортным, особенно для людей без опыта программирования. tidyverse команда открыто заявляет о своей политике diversity, некоторые члены этой команды – открытые представители гендерных и сексуальных меньшинств.
9.3 {data.table} vs tidyverse
Так что же лучше: {data.table} или tidyverse? Это один из самых частых споров в R-комьюнити. У обоих подходов есть плюсы, которые можно обсуждать вечно. Сегодня tidyverse выигрывает в популярности, особенно за пределами русскоязычного пространства.
В последнее время {data.table} и tidyverse все меньше противостоят друг другу и все больше взаимодополняют. Например, некоторые используют в качестве основного инструмента tidyverse, но при работе с данными побольше переключаются на {data.table}2. Кроме того, сами разработчики tidyverse пытаются приладить суперскоростной {data.table} в tidyverse: пакет {dtplyr} позволяет “переводить” код, написанный в tidyverse в код на {data.table}.
Таким образом, выбирая из tidyverse и {data.table}, начинать лучше с более удобного и популярного tidyverse, чем и займемся далее.
Это работает и в базовом R, но именно в {data.table} это очень частая конструкция.↩︎