library(tidyverse)
library(stringr) # работа со строками
library(nycflights13) # учебный набор данных

# Раскомментируйте и запустите следующие команды, 
# если у вас не ставится tidyverse

#library(dplyr)
#library(tidyr)

Введение

Процесс анализа данных

Процесс анализа данных

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

В этом блокноте показаны наиболее полезные приемы трансформации данных в R. Мы будем использовать пакеты dplyr и tidyr.

Также будет рассмотрено создание “конвейеров” по обработке данных с помощью оператора %>%.

Более подробно познакомиться с трансформацией данных вы можете в следующих главах книги R for Data Science:

Также полезно знать, как работать с факторами, строками и датами.

В RStudio всегда под рукой “шпаргалки” по трансформации данных - см. Help>Cheatsheets.

Грамматика трансформации данных

Пакет dplyr реализует концепцию грамматики трансформации данных, согласно которой любое преобразование данных можно описать как последовательность шагов, обозначающих определенное действие с данными. В терминологии dplyr эти действия называются глаголами (verbs). Каждое действие реализовано как функция пакета, которая принимает на вход таблицу данных и возвращает преобразованную таблицу. Например, глагол select() позволяет отобрать из таблицы нужные столбцы:

select(my_table, important_column1, important_column2)

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

data(mpg) #загрузка набора данных о топливной эффективности автомобилей
head(mpg)
## # A tibble: 6 x 11
##   manufacturer model displ  year   cyl trans  drv     cty   hwy fl    class
##   <chr>        <chr> <dbl> <int> <int> <chr>  <chr> <int> <int> <chr> <chr>
## 1 audi         a4      1.8  1999     4 auto(~ f        18    29 p     comp~
## 2 audi         a4      1.8  1999     4 manua~ f        21    29 p     comp~
## 3 audi         a4      2    2008     4 manua~ f        20    31 p     comp~
## 4 audi         a4      2    2008     4 auto(~ f        21    30 p     comp~
## 5 audi         a4      2.8  1999     6 auto(~ f        16    26 p     comp~
## 6 audi         a4      2.8  1999     6 manua~ f        18    26 p     comp~
head(
  select(mpg, manufacturer, model, displ, trans, cty) #отбор 5 столбцов
)
## # A tibble: 6 x 5
##   manufacturer model displ trans        cty
##   <chr>        <chr> <dbl> <chr>      <int>
## 1 audi         a4      1.8 auto(l5)      18
## 2 audi         a4      1.8 manual(m5)    21
## 3 audi         a4      2   manual(m6)    20
## 4 audi         a4      2   auto(av)      21
## 5 audi         a4      2.8 auto(l5)      16
## 6 audi         a4      2.8 manual(m5)    18

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

Предложение, составленное из глаголов dplyr

Предложение, составленное из глаголов dplyr

Мы рассмотрим самые важные глаголы:

  • filter() - отобрать строки,
  • mutate() - вычислить новые столбцы,
  • select() - отобрать столбцы,
  • arrange() - упорядочить строки,
  • group_by() и summarize() - посчитать итоги по группам

В блокноте tidy_data.Rmd вы можете узнать о глаголах для переструктурирования таблиц:

  • gather() - “свернуть” столбцы в строки, перейти к “длинному” формату,
  • spread() - “развернуть” строки в столбцы, перейти к “широкому” формату.

Большинство функций реализованы в пакете dplyr. Функции для преобразования структуры данных gather() и spread() реализованы в пакете tidyr. Оба пакета автоматически подключаются при загрузке tidyverse.

Для слияния нескольких таблиц в dplyr существуют глаголы для соединения, объединения и дополнения столбцов: - inner_join() - соединить таблицы внутренним соединением - left_join() - соединить таблицы левым соединением и др.

С ними можно познакомиться в главе Relational Data книги R for Data Science и документации на dplyr.

Отбор строк с помощью filter()

Функция filter() позволяет отобрать нужные строки таблицы по условию.

Отбор строк с помощью filter()

Отбор строк с помощью filter()

Совет в RStudio открыть картинку в большем размере можно щелкнув по ней или по ссылке с зажатой клавишей Shift. Если выдается сообщение об ошибке (не найден файл) - установите рабочий каталог на папку, в которой находится этот блокнот (Session>Set Working Directory>To Source File Location).

Поддерживаются следующие условия:

  • сравнения: < <= > >=
  • равенство: ==, неравенство: !=
  • сравнение для действительных чисел: near(pi, 3.14, tol = 0.01) (с указанием точности)
  • пропущенные значения: is.na(), !is.na()
  • вхождение в список: %in%
  • отбор по части строки - см. книгу R4DS

Несколько условий можно комбинировать с помощью логических союзов:

Логические союзы

Логические союзы

Примеры filter()

Пример 1. Отбор по сочетанию условий

Отобрать все машины с задним приводом и 6-цилиндровым двигателем.

filter(mpg, drv == 'r' & cyl == 6)
## # A tibble: 4 x 11
##   manufacturer model  displ  year   cyl trans drv     cty   hwy fl    class
##   <chr>        <chr>  <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
## 1 ford         musta~   3.8  1999     6 manu~ r        18    26 r     subc~
## 2 ford         musta~   3.8  1999     6 auto~ r        18    25 r     subc~
## 3 ford         musta~   4    2008     6 manu~ r        17    26 r     subc~
## 4 ford         musta~   4    2008     6 auto~ r        16    24 r     subc~
# или так:
filter(mpg, drv == 'r', cyl == 6)
## # A tibble: 4 x 11
##   manufacturer model  displ  year   cyl trans drv     cty   hwy fl    class
##   <chr>        <chr>  <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
## 1 ford         musta~   3.8  1999     6 manu~ r        18    26 r     subc~
## 2 ford         musta~   3.8  1999     6 auto~ r        18    25 r     subc~
## 3 ford         musta~   4    2008     6 manu~ r        17    26 r     subc~
## 4 ford         musta~   4    2008     6 auto~ r        16    24 r     subc~

Пример 2. Отбор по вхождению в множество и сравнение действительных чисел

Действительные числа хранятся с ограниченной точностью. Это может вызывать проблемы при сравнении таких чисел на строгое равенство:

# Нельзя использовать для действительных чисел:
sqrt(2)^2 == 2
## [1] FALSE
sprintf("%.30f", sqrt(2)^2)
## [1] "2.000000000000000444089209850063"

Безопаснее пользоваться функцией near():

# Правильно сравнивать действительные числа так:
near(sqrt(2)^2, 2)
## [1] TRUE

Отобрать автомобили марок Honda и Toyota с автоматической коробкой передач и объемом двигателя 1.8 литра.

filter(mpg, 
       manufacturer %in% c('honda', 'toyota'),
       str_detect(trans, 'auto'),
       near(displ, 1.8))
## # A tibble: 5 x 11
##   manufacturer model  displ  year   cyl trans drv     cty   hwy fl    class
##   <chr>        <chr>  <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
## 1 honda        civic    1.8  2008     4 auto~ f        25    36 r     subc~
## 2 honda        civic    1.8  2008     4 auto~ f        24    36 c     subc~
## 3 toyota       corol~   1.8  1999     4 auto~ f        24    30 r     comp~
## 4 toyota       corol~   1.8  1999     4 auto~ f        24    33 r     comp~
## 5 toyota       corol~   1.8  2008     4 auto~ f        26    35 r     comp~

Пример 3. Отбор по вычисляемому условию

Отобрать автомобили, в которых средневзвешенный пробег на 1 галлоне топлива (город - 50%, шоссе - 50%) свыше 30 миль.

filter(mpg,
       0.5 * cty + 0.5 * hwy > 30)
## # A tibble: 8 x 11
##   manufacturer model  displ  year   cyl trans drv     cty   hwy fl    class
##   <chr>        <chr>  <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
## 1 honda        civic    1.6  1999     4 manu~ f        28    33 r     subc~
## 2 honda        civic    1.8  2008     4 auto~ f        25    36 r     subc~
## 3 toyota       corol~   1.8  1999     4 manu~ f        26    35 r     comp~
## 4 toyota       corol~   1.8  2008     4 manu~ f        28    37 r     comp~
## 5 toyota       corol~   1.8  2008     4 auto~ f        26    35 r     comp~
## 6 volkswagen   jetta    1.9  1999     4 manu~ f        33    44 d     comp~
## 7 volkswagen   new b~   1.9  1999     4 manu~ f        35    44 d     subc~
## 8 volkswagen   new b~   1.9  1999     4 auto~ f        29    41 d     subc~

Замечание: в качестве условия отбора можно использовать любое выражение, которое возвращает TRUE или FALSE для каждой строки обрабатываемой таблицы.

Упражнения filter()

Для выполнения упражнений понадобится набор данных flights, который устанавливается вместе с пакетом nycflights13.

data(flights)
head(flights)
## # A tibble: 6 x 19
##    year month   day dep_time sched_dep_time dep_delay arr_time
##   <int> <int> <int>    <int>          <int>     <dbl>    <int>
## 1  2013     1     1      517            515         2      830
## 2  2013     1     1      533            529         4      850
## 3  2013     1     1      542            540         2      923
## 4  2013     1     1      544            545        -1     1004
## 5  2013     1     1      554            600        -6      812
## 6  2013     1     1      554            558        -4      740
## # ... with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
## #   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
## #   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
## #   time_hour <dttm>

Посмотреть описание столбцов можно командой: ?flights.

Упражнение 1. Отбор по сочетанию условий

Найдите все рейсы компаний ‘Delta Airlines’, ‘American Airlines’ и ‘United Airlines’, которые вылетели в Хьюстон (IAH или HOU) летом 2013 года. При этом рейс должен был вылететь без опоздания, но совершить посадку с задержкой более 1 часа.

Коды авиакомпаний можно посмотреть в таблице: nycflights13::airlines.

#filter(flights,
#       ...
#       )

Должно получиться 7 строк.

Подсказки:

  • Вместо объединения нескольких условий союзом & их можно перечислить через запятую в списке аргументов filter().
  • В пакете dplyr имеется функция between(), которая может использоваться для отбора по условию вхождения в интервал значений. Обратите внимание, что границы интервала входят в него.

Упражнение 2. Отбор по вхождению в интервал значений

Найдите рейсы, вылетевшие в ночное время (с 00:00 до 5:59) с задержкой не менее 1 часа и наверставшие в пути более 50 минут.

# Напишите свой код здесь

Должно получиться 7 строк.

Упражнение 3. Поиск пропущенных значений

Сколько рейсов в Питтсбург отменили в сентябре 2013 года?

# Напишите свой код здесь

Должно получиться 5 строк.

Отбор столбцов с помощью select()

Глагол select() позволяет отобрать нужные столбцы таблицы.

Отбор столбцов с помощью select()

Отбор столбцов с помощью select()

Указать, какие столбцы нужны, можно следующими способами:

  • перечислить их в списке аргументов select()

  • указать последовательность для включения начало:конец или исключения: -(начало:конец)

  • использовать вспомогательные функции для выбора столбцов:

    • starts_with('abc') - имя начинается с abc
    • ends_with('xyz') - имя заканчивается на abc
    • contains('ijk') - имя содержит ijk
    • matches('\d') - имя соответствует регулярному выражению (см. книгу R4DS), в даном случае - содержит любую цифру
    • num_range('x', 1:3) - имя входит в последовательность: x1, x2, x3
    • one_of(c('a', 'b', 'c')) - имя входит в список: a, b, c
    • everything() - все остальные столбцы.

Примеры select()

names(mpg)
##  [1] "manufacturer" "model"        "displ"        "year"        
##  [5] "cyl"          "trans"        "drv"          "cty"         
##  [9] "hwy"          "fl"           "class"
head(mpg)
## # A tibble: 6 x 11
##   manufacturer model displ  year   cyl trans  drv     cty   hwy fl    class
##   <chr>        <chr> <dbl> <int> <int> <chr>  <chr> <int> <int> <chr> <chr>
## 1 audi         a4      1.8  1999     4 auto(~ f        18    29 p     comp~
## 2 audi         a4      1.8  1999     4 manua~ f        21    29 p     comp~
## 3 audi         a4      2    2008     4 manua~ f        20    31 p     comp~
## 4 audi         a4      2    2008     4 auto(~ f        21    30 p     comp~
## 5 audi         a4      2.8  1999     6 auto(~ f        16    26 p     comp~
## 6 audi         a4      2.8  1999     6 manua~ f        18    26 p     comp~

Пример 1. Перечисление столбцов и смежные столбцы

head(
  select(mpg, manufacturer:displ, trans, cty)
)
## # A tibble: 6 x 5
##   manufacturer model displ trans        cty
##   <chr>        <chr> <dbl> <chr>      <int>
## 1 audi         a4      1.8 auto(l5)      18
## 2 audi         a4      1.8 manual(m5)    21
## 3 audi         a4      2   manual(m6)    20
## 4 audi         a4      2   auto(av)      21
## 5 audi         a4      2.8 auto(l5)      16
## 6 audi         a4      2.8 manual(m5)    18

Пример 2. Исключение столбцов

head(
  select(mpg, -(displ:cty), -fl, -class)
)
## # A tibble: 6 x 3
##   manufacturer model   hwy
##   <chr>        <chr> <int>
## 1 audi         a4       29
## 2 audi         a4       29
## 3 audi         a4       31
## 4 audi         a4       30
## 5 audi         a4       26
## 6 audi         a4       26

Пример 3. Отбор столбцов по условию

Отобрать столбцы, которые начинаются на ‘m’ или содержат ‘y’.

head(
  select(mpg, starts_with('m'), contains('y'))
)
## # A tibble: 6 x 6
##   manufacturer model  year   cyl   cty   hwy
##   <chr>        <chr> <int> <int> <int> <int>
## 1 audi         a4     1999     4    18    29
## 2 audi         a4     1999     4    21    29
## 3 audi         a4     2008     4    20    31
## 4 audi         a4     2008     4    21    30
## 5 audi         a4     1999     6    16    26
## 6 audi         a4     1999     6    18    26

Пример 4. Перемещение столбцов в начало таблицы

head(
  select(mpg, cty, hwy, everything())
)
## # A tibble: 6 x 11
##     cty   hwy manufacturer model displ  year   cyl trans  drv   fl    class
##   <int> <int> <chr>        <chr> <dbl> <int> <int> <chr>  <chr> <chr> <chr>
## 1    18    29 audi         a4      1.8  1999     4 auto(~ f     p     comp~
## 2    21    29 audi         a4      1.8  1999     4 manua~ f     p     comp~
## 3    20    31 audi         a4      2    2008     4 manua~ f     p     comp~
## 4    21    30 audi         a4      2    2008     4 auto(~ f     p     comp~
## 5    16    26 audi         a4      2.8  1999     6 auto(~ f     p     comp~
## 6    18    26 audi         a4      2.8  1999     6 manua~ f     p     comp~

Упражнение select()

Для выполнения задания вам потребуется набор данных flights.

head(flights)
## # A tibble: 6 x 19
##    year month   day dep_time sched_dep_time dep_delay arr_time
##   <int> <int> <int>    <int>          <int>     <dbl>    <int>
## 1  2013     1     1      517            515         2      830
## 2  2013     1     1      533            529         4      850
## 3  2013     1     1      542            540         2      923
## 4  2013     1     1      544            545        -1     1004
## 5  2013     1     1      554            600        -6      812
## 6  2013     1     1      554            558        -4      740
## # ... with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
## #   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
## #   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
## #   time_hour <dttm>

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

# Напишите свой код здесь

Сортировка таблицы с помощью arrange()

Глагол arrange() позволяет упорядочить таблицу по одному или нескольким столбцам. По умолчанию сортировка производится по возрастанию. Используйте функцию desc() для сортировки по убыванию.

Примеры arrange()

Пример 1. Сортировка по одному столбцу

Выведите рейтинг из 10 наиболее экономичных автомобилей (при поездках по городу)?

head(
  arrange(mpg, desc(cty)),
  n = 10)
## # A tibble: 10 x 11
##    manufacturer model displ  year   cyl trans drv     cty   hwy fl    class
##    <chr>        <chr> <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
##  1 volkswagen   new ~   1.9  1999     4 manu~ f        35    44 d     subc~
##  2 volkswagen   jetta   1.9  1999     4 manu~ f        33    44 d     comp~
##  3 volkswagen   new ~   1.9  1999     4 auto~ f        29    41 d     subc~
##  4 honda        civic   1.6  1999     4 manu~ f        28    33 r     subc~
##  5 toyota       coro~   1.8  2008     4 manu~ f        28    37 r     comp~
##  6 honda        civic   1.8  2008     4 manu~ f        26    34 r     subc~
##  7 toyota       coro~   1.8  1999     4 manu~ f        26    35 r     comp~
##  8 toyota       coro~   1.8  2008     4 auto~ f        26    35 r     comp~
##  9 honda        civic   1.6  1999     4 manu~ f        25    32 r     subc~
## 10 honda        civic   1.8  2008     4 auto~ f        25    36 r     subc~

Пример 2. Сортировка по нескольким столбцам

Выведите рейтинг автомобилей по экономичности в городе для каждого производителя.

head(
  arrange(mpg, manufacturer, desc(cty)),
  n = 30)
## # A tibble: 30 x 11
##    manufacturer model displ  year   cyl trans drv     cty   hwy fl    class
##    <chr>        <chr> <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
##  1 audi         a4      1.8  1999     4 manu~ f        21    29 p     comp~
##  2 audi         a4      2    2008     4 auto~ f        21    30 p     comp~
##  3 audi         a4      2    2008     4 manu~ f        20    31 p     comp~
##  4 audi         a4 q~   2    2008     4 manu~ 4        20    28 p     comp~
##  5 audi         a4 q~   2    2008     4 auto~ 4        19    27 p     comp~
##  6 audi         a4      1.8  1999     4 auto~ f        18    29 p     comp~
##  7 audi         a4      2.8  1999     6 manu~ f        18    26 p     comp~
##  8 audi         a4      3.1  2008     6 auto~ f        18    27 p     comp~
##  9 audi         a4 q~   1.8  1999     4 manu~ 4        18    26 p     comp~
## 10 audi         a4 q~   2.8  1999     6 manu~ 4        17    25 p     comp~
## # ... with 20 more rows

Упражнения arrange()

Для выполнения задания вам потребуется набор данных flights.

head(flights)
## # A tibble: 6 x 19
##    year month   day dep_time sched_dep_time dep_delay arr_time
##   <int> <int> <int>    <int>          <int>     <dbl>    <int>
## 1  2013     1     1      517            515         2      830
## 2  2013     1     1      533            529         4      850
## 3  2013     1     1      542            540         2      923
## 4  2013     1     1      544            545        -1     1004
## 5  2013     1     1      554            600        -6      812
## 6  2013     1     1      554            558        -4      740
## # ... with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
## #   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
## #   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
## #   time_hour <dttm>

Упражнение 1. Сортировка по одному столбцу

Вылет каких рейсов был больше всего задержан?

Упражнение 2. Сортировка по вычисляемому условию

Какие рейсы самые быстрые (по скорости движения в воздухе)?

Вычисление новых столбцов с помощью mutate().

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

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

Примеры mutate()

Пример 1. Вычислить несколько новых столбцов

Пусть требуется рассчитать средневзвешенную топливную эффективность для условий, когда доля поездок по городу составляет 50% и 80%.

head(
  mutate(mpg,
         mpg_city50 = 0.5 * cty + 0.5 * hwy,
         mpg_city80 = 0.8 * cty + 0.2 * hwy)
)
## # A tibble: 6 x 13
##   manufacturer model displ  year   cyl trans drv     cty   hwy fl    class
##   <chr>        <chr> <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
## 1 audi         a4      1.8  1999     4 auto~ f        18    29 p     comp~
## 2 audi         a4      1.8  1999     4 manu~ f        21    29 p     comp~
## 3 audi         a4      2    2008     4 manu~ f        20    31 p     comp~
## 4 audi         a4      2    2008     4 auto~ f        21    30 p     comp~
## 5 audi         a4      2.8  1999     6 auto~ f        16    26 p     comp~
## 6 audi         a4      2.8  1999     6 manu~ f        18    26 p     comp~
## # ... with 2 more variables: mpg_city50 <dbl>, mpg_city80 <dbl>

Пример 2. Использование ifelse()

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

Предположим, что доли поездок по городу для разных автомобилей в таблице отличаются. Для наглядности, будем считать, что автомобили классов minivan и 2seater используются только в городе, а остальные - по 50% времени в городе и для поездок по автомагистралям. Требуется рассчитать средневзвешенную топливную эффективность.

head(
  filter(

        mutate(mpg, 
           weighted_mpg = 
             ifelse(class %in% c('pickup', '2seater'),
                    cty, 0.5 * cty + 0.5 * hwy)),
        class %in% c('2seater', 'suv')))
## # A tibble: 6 x 12
##   manufacturer model displ  year   cyl trans drv     cty   hwy fl    class
##   <chr>        <chr> <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
## 1 chevrolet    c150~   5.3  2008     8 auto~ r        14    20 r     suv  
## 2 chevrolet    c150~   5.3  2008     8 auto~ r        11    15 e     suv  
## 3 chevrolet    c150~   5.3  2008     8 auto~ r        14    20 r     suv  
## 4 chevrolet    c150~   5.7  1999     8 auto~ r        13    17 r     suv  
## 5 chevrolet    c150~   6    2008     8 auto~ r        12    17 r     suv  
## 6 chevrolet    corv~   5.7  1999     8 manu~ r        16    26 p     2sea~
## # ... with 1 more variable: weighted_mpg <dbl>

Упражнения mutate()

Для выполнения задания вам потребуется набор данных flights.

head(flights)
## # A tibble: 6 x 19
##    year month   day dep_time sched_dep_time dep_delay arr_time
##   <int> <int> <int>    <int>          <int>     <dbl>    <int>
## 1  2013     1     1      517            515         2      830
## 2  2013     1     1      533            529         4      850
## 3  2013     1     1      542            540         2      923
## 4  2013     1     1      544            545        -1     1004
## 5  2013     1     1      554            600        -6      812
## 6  2013     1     1      554            558        -4      740
## # ... with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
## #   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
## #   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
## #   time_hour <dttm>

Упражнение 1. Вычисляемые столбцы

В наборе данных время вылета находится в неудобном формате: часы и минуты собраны в одно целое число. Поэтому, например, после значения 1359 будет идти значение 1400. Ваша задача - вычислить три новых поля: dep_hour, dep_minute и dep_minutes_since_midnight. Поля должны содержать, соответственно, час вылета, минуту вылета и количество минут с полуночи до вылета.

# Напишите свой код здесь

Подсказки:

  • Воспользуйтесь операциями целочисленного деления %/% и взятия остатка от деления %%. Например, 1359 %/% 100 = 13, а 1359 %% 100 = 59.

  • Добавив формулу для расчета столбца, вы можете использовать имя этого столбца в этом же вызове функции mutate() для расчета новых столбцов. Т.е. можно сделать так: mutate(mpg, cty2 = cty, cty3 = cty2).

Упражнение 2. Условные выражения

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

# Напишите свой код здесь

Конвейеры %>%

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

  1. Вычислить новый столбец weighted_mpg с помощью mutate()
  2. Отфильтровать данные только по двум классам автомобилей с помощью filter()
  3. Ограничить количество выводимых строк таблицы с помощью head()

Однако записывать эти действия приходится в обратном порядке, вкладывая функцию предыдущего шага внутрь функции, использующейся на следующем шаге преобразования:

head(
  filter(
    mutate(...)
  )
)

Такой код сложно понять.

Мы могли бы улучшить наглядность кода, используя дополнительные переменные для сохранения промежуточных результатов:

step1 <- mutate(mpg, ...)
step2 <- filter(step1, ...)
head(step2)

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

Чтобы обеспечить “последовательную” логику выполнения шагов преобразования данных, для R был создан пакет magrittr, который позволяет использовать новый оператор - %>% (pipe) для записи цепочек преобразования данных.

Оператор подставляет любой объект в своей левой части в качестве первого аргумента функции, указанной в правой части.

Как работает конвейер %>%

Как работает конвейер %>%

Предыдущая запись эквивалентна: function(object, arg2, ...)

Поскольку функции в R возвращают новые объекты, цепочку операций можно продолжать сколько угодно. Результат предыдущей операции используется в качестве первого аргумента второй операции.

Конвейер из двух операций

Конвейер из двух операций

Эквивалентная запись: function2(function1(object))

Пример использования %>%

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

Было:

head(
  filter(

        mutate(mpg, 
           weighted_mpg = 
             ifelse(class %in% c('pickup', '2seater'),
                    cty, 0.5 * cty + 0.5 * hwy)),
        class %in% c('2seater', 'suv')))

Стало:

mpg %>% 
  mutate(weighted_mpg = 
           ifelse(class %in% c('pickup', '2seater'),
                  cty, 0.5 * cty + 0.5 * hwy)) %>%
  filter(class %in% c('2seater', 'suv')) %>%
  select(model, cty, hwy, class, weighted_mpg) %>%
  head()
## # A tibble: 6 x 5
##   model                cty   hwy class   weighted_mpg
##   <chr>              <int> <int> <chr>          <dbl>
## 1 c1500 suburban 2wd    14    20 suv             17  
## 2 c1500 suburban 2wd    11    15 suv             13  
## 3 c1500 suburban 2wd    14    20 suv             17  
## 4 c1500 suburban 2wd    13    17 suv             15  
## 5 c1500 suburban 2wd    12    17 suv             14.5
## 6 corvette              16    26 2seater         16

Для удобства, мы добавили дополнительный шаг, оставив в таблице только нужные столбцы.

Замечание: обратите внимание, что при включении функции в конвейер аргументы в списке надо указывать начиная со второго. Первый аргумент автоматически заполняется оператором %>%.

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

final_result <- initial_data %>% step1() %>% step2() %>% last_step()

В dplyr И многих других функциях R принято, что первый аргумент функции - это обрабатываемые ей данные, поэтому %>% удобно использовать не только для преобразования табличных данных, но и в других ситуациях, требующих нескольких последовательных шагов.

Подстановка в произвольный аргумент

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

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

В качестве примера, построим модель зависимости топливной эффективности только для автомобилей класса compact, отобрав их с помощью фильтрации.

mpg %>%
  filter(class == 'compact') %>%
  lm(cty ~ hwy, data = .) %>%
  summary()
## 
## Call:
## lm(formula = cty ~ hwy, data = .)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -2.7171 -0.3593  0.2463  0.4802  2.6407 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -3.62688    1.32391   -2.74  0.00879 ** 
## hwy          0.83945    0.04638   18.10  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1.19 on 45 degrees of freedom
## Multiple R-squared:  0.8792, Adjusted R-squared:  0.8765 
## F-statistic: 327.6 on 1 and 45 DF,  p-value: < 2.2e-16

Группировка и подсчет итогов

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

Некоторые востребованные функции для подсчета итогов приведены ниже:

  • mean() - среднее
  • sum() - сумма
  • n() - количество записей
  • sd() - стандартное отклонение
  • first()/last() - первый/последний по порядку элемент
  • min()/max() - наименьший и наибольший элемент
  • quantile(x, p) - квантиль уровня p

Пример подсчета итогов для всей таблицы - summarise()

mpg %>% summarise(avg_cty = mean(cty), 
                  median_cty = median(cty),
                  iqr_cty = IQR(cty),
                  n_obs = n(),
                  percent_mising = 100 * mean(is.na(cty)))
## # A tibble: 1 x 5
##   avg_cty median_cty iqr_cty n_obs percent_mising
##     <dbl>      <dbl>   <dbl> <int>          <dbl>
## 1    16.9         17       5   234              0

Подсчет итогов по группам - group_by()

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

В большинстве же случаев подсчет итогов проводится по группам. Для этого необходимо задать условие группировки набора данных с помощью функции group_by().

Сама по себе функция group_by() никак не изменяет данные, однако она добавляет информацию о группировке в свойства таблицы данных.

mpg %>% group_by(manufacturer) %>% head()
## # A tibble: 6 x 11
## # Groups:   manufacturer [1]
##   manufacturer model displ  year   cyl trans  drv     cty   hwy fl    class
##   <chr>        <chr> <dbl> <int> <int> <chr>  <chr> <int> <int> <chr> <chr>
## 1 audi         a4      1.8  1999     4 auto(~ f        18    29 p     comp~
## 2 audi         a4      1.8  1999     4 manua~ f        21    29 p     comp~
## 3 audi         a4      2    2008     4 manua~ f        20    31 p     comp~
## 4 audi         a4      2    2008     4 auto(~ f        21    30 p     comp~
## 5 audi         a4      2.8  1999     6 auto(~ f        16    26 p     comp~
## 6 audi         a4      2.8  1999     6 manua~ f        18    26 p     comp~

Информация о группировке используется в дальнейшем при выполнении функции summarise(). Если задана группировка, то итоги считаются по группам.

Составим рейтинг автопроизводителей по средней топливной эффективности их автомобилей.

mpg %>% 
  group_by(manufacturer) %>%
  summarise(average_cty = mean(cty)) %>%
  arrange(desc(average_cty)) %>%
  top_n(5, wt = average_cty)
## # A tibble: 5 x 2
##   manufacturer average_cty
##   <chr>              <dbl>
## 1 honda               24.4
## 2 volkswagen          20.9
## 3 subaru              19.3
## 4 hyundai             18.6
## 5 toyota              18.5

Замечание: обратите внимание, что после выполнения агрегирующей функции статус группировки снимается.

Группировка по нескольким полям

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

В качестве примера, определим лучшего производителя в каждом классе автомобилей на основе средней топливной эффективности.

# Средний расход по типу машины и производителю
mpg_class_manuf <- mpg %>% 
  group_by(class, manufacturer) %>%
  summarise(average_cty = mean(cty)) %>%
  arrange(desc(average_cty))

head(mpg_class_manuf)
## # A tibble: 6 x 3
## # Groups:   class [3]
##   class      manufacturer average_cty
##   <chr>      <chr>              <dbl>
## 1 subcompact honda               24.4
## 2 subcompact volkswagen          24  
## 3 compact    toyota              22.2
## 4 compact    volkswagen          20.8
## 5 compact    nissan              20  
## 6 midsize    nissan              20

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

Теперь мы можем “снять” еще один уровень группировки - по классу автомобиля - чтобы выбрать лучшего производителя в каждом классе.

mpg_class_manuf %>% 
  top_n(1, wt = average_cty)
## # A tibble: 7 x 3
## # Groups:   class [7]
##   class      manufacturer average_cty
##   <chr>      <chr>              <dbl>
## 1 subcompact honda               24.4
## 2 compact    toyota              22.2
## 3 midsize    nissan              20  
## 4 suv        subaru              18.8
## 5 minivan    dodge               15.8
## 6 pickup     toyota              15.6
## 7 2seater    chevrolet           15.4

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

Вспомогательные агрегирующие функции

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

# Подсчет частот по группам
mpg %>% count(manufacturer)
## # A tibble: 15 x 2
##    manufacturer     n
##    <chr>        <int>
##  1 audi            18
##  2 chevrolet       19
##  3 dodge           37
##  4 ford            25
##  5 honda            9
##  6 hyundai         14
##  7 jeep             8
##  8 land rover       4
##  9 lincoln          3
## 10 mercury          4
## 11 nissan          13
## 12 pontiac          5
## 13 subaru          14
## 14 toyota          34
## 15 volkswagen      27
# Top-n анализ - самые объемные двигатели
mpg %>% top_n(n = 3, wt = displ)
## # A tibble: 4 x 11
##   manufacturer model  displ  year   cyl trans drv     cty   hwy fl    class
##   <chr>        <chr>  <dbl> <int> <int> <chr> <chr> <int> <int> <chr> <chr>
## 1 chevrolet    corve~   6.2  2008     8 manu~ r        16    26 p     2sea~
## 2 chevrolet    corve~   6.2  2008     8 auto~ r        15    25 p     2sea~
## 3 chevrolet    corve~   7    2008     8 manu~ r        15    24 p     2sea~
## 4 chevrolet    k1500~   6.5  1999     8 auto~ 4        14    17 d     suv

Упражнения - группировка и подсчет итогов

Упражнение 1. Доля задержанных рейсов

На основе набора данных flights рассчитайте долю задержанных рейсов по всей таблице.

Подсказка: R рассматривает логические значения TRUE и FALSE как 1 и 0. Поэтому выполнив команду: mean(x > 0) мы можем найти долю положительных значений в векторе x.

# Напишите свой код здесь

Упражнение 2. Надежность авиакомпаний

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

# Напишите свой код здесь

Упражнение 3. Тяжелые месяцы

Определите, какой месяц оказался самым тяжелым для каждой авиакомпании (по доле рейсов, задержанных более чем на 15 минут).

# Напишите свой код здесь