Введение в dplyr

Логотип библиотеки dplyr

dplyr – это наиболее популярный пакет R, посвящённый трансформации больших таблиц. Мы с особым трепетом относимся к dplyr, т.к. именно он позволяет бывшему экселисту быстро оценить преимущества R. dplyr’у мы посвящаем больше всего учебного времени, и на dplyr написаны основы наших скриптов.

Итак, dplyr – (“d” – это dataframe, “plyr” – это pliers – пассатижи) – это пакет для манипулирования табличными данными. Он предоставляет согласованный набор функций, необходимый для решения наиболее распространенных задач из манипулирования данными.

Синтаксис dplyr – это глагол, указывающий на тип трансформации таблицы, и существительные, указывающие на обрабатываемый датафрейм и его столбцы.

Команды dplyr звучат почти так же, как устная лаконичная команда сделать то-то и то-то с таблицей на английском языке.

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

Установка библиотеки:

install.packages("dplyr")

Подключение библиотеки к вашему проекту:

library(dplyr) 

Фильтрация строк при помощи filter()

Опертора filter привычен экселисту, так как отбор строк по неким условиям по столбцам называется фильтрацией.

filter(df, название_столбца == 1, …) – позволяет вам выбрать подмножество строк из таблицы данных с определённым условием.
Первый аргумент – название набора данных,
второй и последующие – условия фильтра относительно этого набора данных.

В качестве тестового набора данных сгенерируем такой датафрейм:

df <- data.frame(sku = c("Котлеты из индейки", 
                         "Утиная печень", 
                         "Сардельки свиные", 
                         "Паштет датский", 
                         "Пельмени вег.",
                         "Котлеты говяжьи"),
                 store =c("У дома", "У дома", "ТЦ в центре", "ТЦ в центре", "У дома", "У дома"),
                 category = c("Котлеты", "Птица", "Сардельки", "Паштеты", "Пельмени", "Котлеты"),
                 quantity = c(5, 10, 7, 16, 3, 7),
                 sales = c(724.15, 513.79, 68.31, 275.95, 660.58, 785.15))

df

>
                 sku       store  category quantity  sales
1 Котлеты из индейки      У дома   Котлеты        5 724.15
2      Утиная печень      У дома     Птица       10 513.79
3   Сардельки свиные ТЦ в центре Сардельки        7  68.31
4     Паштет датский ТЦ в центре   Паштеты       16 275.95
5      Пельмени вег.      У дома  Пельмени        3 660.58
6    Котлеты говяжьи      У дома   Котлеты        7 785.15

Не забыв подключить библиотеку, мы можем, например, выбрать все строки с числом продаж(quantity) больше 8 и при этом сумма продаж(sales) меньше 300, следующим образом:

filter(df, quantity > 8, sales < 300)

>
             sku       store category quantity  sales
1 Паштет датский ТЦ в центре  Паштеты       16 275.95

Что будет эквивалентно более многословному (и сложному на восприятие) варианту:

df[df$quantity > 8 & df$sales < 300, ]

Также можно объединять вместе несколько условий через логические знаки(|, &, !):

filter(df, quantity > 5 & sales < 700 & !store == "ТЦ в центре")

>
            sku  store category quantity  sales
1 Утиная печень У дома    Птица       10 513.79

Сортировка строк при помощи arrange()

arrange() – в свою очередь, работает аналогично filter() за исключением того, что вместо выбора строк она отсортирует их.
Функция получает на вход название таблицы данных и список имён колонок (или более сложное выражение) для сортировки по ним. Если будет указано больше одной колонки, то каждая следующая колонка будет сортироваться в пределах каждого отдельного набора значений предыдущих:

arrange(df, sales, category)

>
                 sku       store  category quantity  sales
1   Сардельки свиные ТЦ в центре Сардельки        7  68.31
2     Паштет датский ТЦ в центре   Паштеты       16 275.95
3      Утиная печень      У дома     Птица       10 513.79
4      Пельмени вег.      У дома  Пельмени        3 660.58
5 Котлеты из индейки      У дома   Котлеты        5 724.15
6    Котлеты говяжьи      У дома   Котлеты        7 785.15

Чтобы задать порядок по убыванию необходимо использовать (минус) перед названием столбца:

arrange(df, -sales, category)

>
                 sku       store  category quantity  sales
1    Котлеты говяжьи      У дома   Котлеты        7 785.15
2 Котлеты из индейки      У дома   Котлеты        5 724.15
3      Пельмени вег.      У дома  Пельмени        3 660.58
4      Утиная печень      У дома     Птица       10 513.79
5     Паштет датский ТЦ в центре   Паштеты       16 275.95
6   Сардельки свиные ТЦ в центре Сардельки        7  68.31

В общем виде arrange(датафрейм, столбец_1, столбец_2, …) – сортирует таблицу данных по указанным столбцам и по определенном направлении.
При наличии Na, ставит их в конец списка.

Выбор колонок при помощи select()

select() – позволяет вам быстро сфокусироваться на интересующей части колонок, используя при этом символические имена на манер номеров колонок:

select(df, sku, sales, category)

>
                 sku  sales  category
1 Котлеты из индейки 724.15   Котлеты
2      Утиная печень 513.79     Птица
3   Сардельки свиные  68.31 Сардельки
4     Паштет датский 275.95   Паштеты
5      Пельмени вег. 660.58  Пельмени
6    Котлеты говяжьи 785.15   Котлеты

Выделить колонки в диапазоне:

select(df, sku:category)

>
                 sku       store  category
1 Котлеты из индейки      У дома   Котлеты
2      Утиная печень      У дома     Птица
3   Сардельки свиные ТЦ в центре Сардельки
4     Паштет датский ТЦ в центре   Паштеты
5      Пельмени вег.      У дома  Пельмени
6    Котлеты говяжьи      У дома   Котлеты

Удалить конкретную колонку:

select(df, -category)

>
                 sku       store quantity  sales
1 Котлеты из индейки      У дома        5 724.15
2      Утиная печень      У дома       10 513.79
3   Сардельки свиные ТЦ в центре        7  68.31
4     Паштет датский ТЦ в центре       16 275.95
5      Пельмени вег.      У дома        3 660.58
6    Котлеты говяжьи      У дома        7 785.15

Также при извлечении столбцов сохраняется их порядок заданный в select, это можно использовать, в том числе для простого перестроения порядка:

select(df, sales, quantity, sku)

>
   sales quantity                sku
1 724.15        5 Котлеты из индейки
2 513.79       10      Утиная печень
3  68.31        7   Сардельки свиные
4 275.95       16     Паштет датский
5 660.58        3      Пельмени вег.
6 785.15        7    Котлеты говяжьи

Переименовать название колонки во время извлечения:

select(df, товар = sku)

>
                товар
1 Котлеты из индейки
2      Утиная печень
3   Сардельки свиные
4     Паштет датский
5      Пельмени вег.
6    Котлеты говяжьи

Добавление и изменение колонок при помощи mutate()

mutate() – вычисляет на основании заданных колонок, значения для новых или существующих колонок:

mutate() – добавляет новую или изменяет существующую колонку.
mutate(df, price = sales / quantity)

>
                 sku       store  category quantity  sales      price
1 Котлеты из индейки      У дома   Котлеты        5 724.15 144.830000
2      Утиная печень      У дома     Птица       10 513.79  51.379000
3   Сардельки свиные ТЦ в центре Сардельки        7  68.31   9.758571
4     Паштет датский ТЦ в центре   Паштеты       16 275.95  17.246875
5      Пельмени вег.      У дома  Пельмени        3 660.58 220.193333
6    Котлеты говяжьи      У дома   Котлеты        7 785.15 112.164286

Главная особенность mutate() в том что можно ссылаться в вычислениях на колонку которая создаётся в рамках того же вызова функции:

mutate(df, 
       price = sales / quantity,
       percent_price = price * 100 / sum(price))

>
                 sku       store  category quantity  sales      price percent_price
1 Котлеты из индейки      У дома   Котлеты        5 724.15 144.830000     26.068625
2      Утиная печень      У дома     Птица       10 513.79  51.379000      9.247945
3   Сардельки свиные ТЦ в центре Сардельки        7  68.31   9.758571      1.756491
4     Паштет датский ТЦ в центре   Паштеты       16 275.95  17.246875      3.104345
5      Пельмени вег.      У дома  Пельмени        3 660.58 220.193333     39.633622
6    Котлеты говяжьи      У дома   Котлеты        7 785.15 112.164286     20.188971

Группировка и агрегирование таблиц

group_by(датафрейм, столбец_1, столбец_2, …) ‒ разбивает набор данных по какому-либо набору колонок. Используется перед summarize().

summarize(датафрейм, столбец_1 = функция(столбец)) ‒ для каждой из полученных групп выполняет какую либо операцию.
Выполняет чаще всего суммирование, подсчёт, усреднение.

После выполнения summarize() мы получаем совершенно новые данные, на основе исходных данных.

Например для нашей таблицы данных мы можем сгруппировать для каждой категории(category) новый столбец с количеством товаров по этой категории и построить новую таблицу с полученным результатом:

categories <- group_by(df, category)
categories <- summarise(categories, 
                        skus = length(sku))

categories

>
  category   skus
  <fct>     <int>
1 Котлеты       2
2 Паштеты       1
3 Пельмени      1
4 Птица         1
5 Сардельки     1

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

  • min() ‒ поиск минимального(наименьшего) значения;
  • max() ‒ поиск максимального(наибольшего) значения;
  • mean() ‒ среднее арифметическое значение;
  • sum() ‒ сумма всех элементов;
  • sd() ‒ стандартное отклонение;
  • median() ‒ медиана (средний элемент) среди указанных значений;
  • length() ‒ количество элементов;
  • first(x)last(x) и nth(x, n) – работают подобно x[1]x[length(x)], и x[n], но дают больше контроля над результатом если значение не может быть получено.

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

stores <- group_by(df, store)
stores <- summarise(stores, 
                    sum_sales = sum(sales),
                    sum_quantity = sum(quantity),
                    min_quantity = min(quantity),
                    kus = length(sku))

stores

>
  store       sum_sales sum_quantity min_quantity  skus
  <fct>           <dbl>        <dbl>        <dbl> <int>
1 ТЦ в центре      344.           23            7     2
2 У дома          2684.           25            3     4

Дополнительные возможности dplyr и упрощение записи кода

Пример выше для group_by() и summarise() разбит на две строки для промежуточного сохранения данных в таблицу category, данный вариант можно записать так:

summarise(group_by(df, category), 
          skus = length(sku))

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

Чтобы обойти эту проблему dplyr использует оператор %>% (пайп), который можно просто напечатать или использовать Ctrl + Shift + M , для быстрой вставки. 

Главным принципом работы данного оператора является формирование последовательности действий(цепочки) относительно данных заданных в самом начале данной цепи.
данные %>% действие_1 %>% действие_2 %>% …
Также можно сохранять получившуюся конструкцию(цепочку) в отдельные таблицы.

В случае с нашим примером(во всех случаях получается один и тот же результат):

group_by(df, category) %>% 
summarise(skus = length(sku))

# или
df %>% 
group_by(category) %>% 
summarise(skus = length(sku))

# также можно сохранить в отдельную таблицу
categories <- df %>% 
              group_by(category) %>% 
              summarise(skus = length(sku))

categories

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

Схематичный пример работы группировки и расчета нового столбика:

В этом примере мы разбили сначала исходную таблицу на три подтаблицы по значениям колонки “а”, а потом для каждой применили вычисление среднего значения из колонки “b”

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

В данном примере создадим цепь операций над нашим исходным датафреймом, где в начале возьмем только магазины(store) с названием “У дома”, после чего сгруппируем данные по категориям(category), вычислим новые столбцы с количеством уникальных товаров в данной категории(и соответственно только в магазине “У дома” с учетом нашего фильтра) и суммарной выручке для каждой категории с полным округлением до целого, после чего отсортируем по убыванию суммарной прибыли:

df %>%
filter(store == "У дома") %>%
group_by(category) %>%
ummarise(skus = length(unique(sku)),
         sales_rub = round(sum(sales), 0)) %>%
arrange(-sales_rub)

>
  category  skus sales_rub
  <fct>    <int>     <dbl>
1 Котлеты      2      1509
2 Пельмени     1       661
3 Птица        1       514

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>