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

install.packages("tidyverse")

И аналогичным образом включать в начале работы:1

library(tidyverse)
── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.4.2     ✔ purrr   1.0.1
✔ tibble  3.2.1     ✔ dplyr   1.1.2
✔ tidyr   1.3.0     ✔ stringr 1.5.0
✔ readr   2.1.2     ✔ forcats 0.5.1
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()

Работать с Tidyverse удобнее всего в редакторе скриптов, а не консоли. Поэтому далее предполагается, что вы используете RStudio.

Программа R за несколько десятилетий существования обросла огромным количеством сторонних пакетов, в том числе меняющих и базовые принципы работы с данными. При этом скриптовый код будет словно написан на другом языке — или, вернее, на другом диалекте “языка R”. Диалект Tidyverse выстроился на основе идей новозеландского статистика Хэдли Уикхема о принципах работы с данными и вокруг его сверхпопулярных пакетов dplyr, tidyr и ggplot2. Они повсеместно встречаются в сетевых руководствах по тем или иным операциям в R, а ggplot2 вообще является самой популярной графической системой. Общая идеология этих пакетов строится на принципах функционального программирования, и в двух словах её можно представить примерно так: код, написанный в Tidyverse, будет более громоздким, но зато легко читаемым и понятным. При этом используемые команды просты, единообразно устроены и прячут от пользователя большую часть мелкой работы.

Настоятельно рекомендуем в свободное время ознакомиться с ультимативным руководством от Уикхема по работе с данными в Tidyverse (и вообще в R).

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

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

select() выбор переменных (столбцов)
mutate() изменение или добавление переменных
filter() отобрать строки (наблюдения) по интересующим критериям
arrange() сортировка строк
group_by() разбивка наблюдений на группы для последующих операций
summarise() создание новых таблиц, обобщающих наблюдения

Если использовать их по отдельности, каждая функция первым параметром принимает объект, с которым будет работать: таблицу с данными типа data.frame или tibble. Однако гораздо удобнее объединять их в цепочку с помощью оператора конвейера %>%2. Тогда функции “передают” друг другу объект, с которым поработали, и указывать его в параметрах не нужно. Сравните два варианта кода, выполняющих одно и то же:

# Каждая команда сохраняет результат в новую переменную,
# которую использует следующая функция:

a <- filter(iris, Species == "versicolor")
b <- mutate(a, ratio = Sepal.Length / Sepal.Width)
c <- select(b, 1, 2, 6)
d <- arrange(c, ratio)
# То же самое, но без лишних переменных и понятнее:

d <- iris %>% 
  filter(Species == "versicolor") %>% 
  mutate(ratio = Sepal.Length / Petal.Length) %>% 
  select(1, 3, 6) %>% 
  arrange(ratio)

# Сначала выполняются все операции, затем результат сохраняется в d
d
  Sepal.Length Petal.Length    ratio
1          6.0          5.1 1.176471
2          5.4          4.5 1.200000
3          5.9          4.8 1.229167
4          5.6          4.5 1.244444
...

Рассмотрим этот пример подробнее. Первая команда filter() отбирает из таблицы iris лишь те строки, в которых переменная Species имеет значение “versicolor”. Следующая команда mutate()создаёт в таблице новую переменную ratio, значения которой у каждого ириса равны отношению длины “лепестков” к длине “чашелистиков” — т.е. отношению значений из соответствующих столбцов. Далее команда select() оставляет только первый, третий и шестой столбцы (Sepal.Length, Petal.Length и ratio), а arrange() сортирует строки полученной таблицы по столбцу ratio, начиная с наименьшего значения.

Далее эти и другие функции будут рассмотрены подробнее.

select() — выбор переменных (столбцов)

Эта функция позволяет выбирать нужные столбцы в таблице различными способами:

названия столбцов через запятую select(iris, Sepal.Length, Petal.Length)
номера столбцов через запятую select(iris, 1, 3)
: выбирает все столбцы между двумя указанными select(iris, 1:4) — выберет все 4 числовые переменные в iris; подходят номера или имена столбцов
! выбирает все столбцы, кроме указанных select(iris, !Species) — выберет те же 4 переменные; можно использовать и минус, как в базовом R

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

  • last_col() — последний столбец.

  • starts_with("текст"), ends_with("текст"), contains("текст") — выбирают столбцы, содержащие требуемый текст в начале, в конце или где угодно.

  • num_range("текст", n:m) — для выбора столбцов, имена которых содержат текст и последовательность чисел. Пример: num_range("day", 5:10) выберет столбцы day5, day6, day7, day8, day9, day10.

  • where(имя функции) — выбирает столбцы, которые задаваемая вами функция проверяет и даёт результат TRUE. select(iris, where(is.numeric)) выберет только числовые переменные (первые 4 столбца)3.

Операторы & и | позволяют объединять разные условия так, чтобы либо они выполнялись все (&), либо хотя бы одно (|).

Столбцы, которые не были выбраны, отбрасываются. Можно оставить их, добавив в конце параметр everything() — так можно, не меняя таблицу, переставлять столбцы местами.

Несколько примеров:

# Самый простой вариант:
select(mtcars, mpg, wt, disp)
                mpg    wt disp
Mazda RX4      21.0 2.620  160
Mazda RX4 Wag  21.0 2.875  160
Datsun 710     22.8 2.320  108
Hornet 4 Drive 21.4 3.215  258
...
# Просто вытащим эти столбцы вперёд, для удобства:
select(mtcars, mpg, wt, disp, everything())
                mpg    wt disp cyl  hp drat  qsec vs am gear carb
Mazda RX4      21.0 2.620  160   6 110 3.90 16.46  0  1    4    4
Mazda RX4 Wag  21.0 2.875  160   6 110 3.90 17.02  0  1    4    4
Datsun 710     22.8 2.320  108   4  93 3.85 18.61  1  1    4    1
Hornet 4 Drive 21.4 3.215  258   6 110 3.08 19.44  1  0    3    1
...
# Всё, кроме первого столбца:
select(mtcars, 2:last_col())
               cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4        6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag    6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710       4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive   6  258 110 3.08 3.215 19.44  1  0    3    1
...
# Операторы & и |
select(iris, starts_with("Sepal") & ends_with("Width"))
  Sepal.Width
1         3.5
2         3.0
3         3.2
4         3.1
...
select(iris, starts_with("Sepal") | ends_with("Width"))
  Sepal.Length Sepal.Width Petal.Width
1          5.1         3.5         0.2
2          4.9         3.0         0.2
3          4.7         3.2         0.2
4          4.6         3.1         0.2
...
# Сложное условие
select(iris, 2:last_col() & ! where(is.factor) & ! contains("Width"))
  Petal.Length
1          1.4
2          1.4
3          1.3
4          1.5
...

Переименование столбцов

Функция rename(новое_имя = старое_имя) позволяет переименовывать столбцы. Выбирать столбцы для переименования (“старое_имя”) нужно так же, как и в select().

<tidy-select>

Если в описании какой-нибудь функции вы встречаете в качестве значения одного из параметров “<tidy-select>”, значит вы можете выбрать один или несколько столбцов таблицы, с которой работает эта функция, точно таким же образом, как и в select(). Если нужно перечислить несколько критериев через запятую, то окружите их функцией c(). Типичные примеры функций, использующих <tidy-select>: pivot_longer(), across() или описанная выше rename().

mutate() — изменение или добавление столбцов

Добавление столбца в таблице похоже на создание нового объекта, только пишется внутри функции mutate() и использует оператор = вместо <-. При этом можно использовать другие столбцы (переменные) таблицы, совершая с ними произвольные операции. Можно создавать сразу несколько переменных.

# Посчитаем соотношение длины к ширине для "лепестков" и "чашелистиков" ирисов.
#
# Обратите внимание: так как это скрипт, то можно для удобства
# записывать длинные команды на нескольких строчках.
iris %>%
  mutate(s.ratio = Sepal.Length / Sepal.Width,
         p.ratio = Petal.Length / Petal.Width)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species  s.ratio p.ratio
1          5.1         3.5          1.4         0.2  setosa 1.457143     7.0
2          4.9         3.0          1.4         0.2  setosa 1.633333     7.0
3          4.7         3.2          1.3         0.2  setosa 1.468750     6.5
4          4.6         3.1          1.5         0.2  setosa 1.483871     7.5
...

Чтобы изменить существующий столбец, надо “создать” новый столбец с тем же именем:

# Для анализа таблицы mtcars может понадобиться преобразовать
# несколько столбцов в факторы, в т.ч. упорядоченные:
mtcars %>%
  mutate(cyl = ordered(cyl),
         gear = ordered(gear),
         am = factor(am, labels=c("automatic", "manual")))
# Преобразуем единицы измерения переменных mtcars в привычные СИ:
mtcars %>% mutate(mpg = mpg * 1.60934 / 3.78541, # км / л
                  disp = disp * 0.0163871,       # литры
                  wt = wt * 0.453592)            # тонны
                    mpg cyl     disp  hp drat       wt  qsec vs am gear carb
Mazda RX4      8.928000   6 2.621936 110 3.90 1.188411 16.46  0  1    4    4
Mazda RX4 Wag  8.928000   6 2.621936 110 3.90 1.304077 17.02  0  1    4    4
Datsun 710     9.693257   4 1.769807  93 3.85 1.052333 18.61  1  1    4    1
Hornet 4 Drive 9.098057   6 4.227872 110 3.08 1.458298 19.44  1  0    3    1
...

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

  • rank(имя_столбца) — ранжирует значения переменной, т.е. присваивает им порядковые номера, начиная с самого маленького. Можно наоборот, начать с самого большого значения, с помощью dec(), как в примере ниже. Судьба одинаковых значений решается параметром ties.method.
# Top-5 самых экономичных автомобилей из mtcars
mtcars %>%
  mutate(top_econom = rank( desc(mpg) )) %>% 
  arrange(top_econom) %>% 
  head(5)
                mpg cyl disp  hp drat    wt  qsec vs am gear carb top_econom
Toyota Corolla 33.9   4 71.1  65 4.22 1.835 19.90  1  1    4    1        1.0
Fiat 128       32.4   4 78.7  66 4.08 2.200 19.47  1  1    4    1        2.0
Honda Civic    30.4   4 75.7  52 4.93 1.615 18.52  1  1    4    2        3.5
Lotus Europa   30.4   4 95.1 113 3.77 1.513 16.90  1  1    5    2        3.5
Fiat X1-9      27.3   4 79.0  66 4.08 1.935 18.90  1  1    4    1        5.0
  • if_else(условие, если да, если нет) — работает аналогично функции ЕСЛИ / IF из Excel. Принцип её работы проще всего понять на конкретном примере:
# Допустим, мы считаем, что экономные машины имеют значение mpg больше 20.
# Отметим все машины в mtcars как экономные или растратные:
mtcars %>% 
  mutate(efficiency = if_else(mpg > 20,     # проверяем условие
                              "экономная",  # новое значение, если условие выполняется
                              "растратная")) %>%     # если не выполняется 
  select(mpg, efficiency)
                   mpg efficiency
Mazda RX4         21.0  экономная
Mazda RX4 Wag     21.0  экономная
Datsun 710        22.8  экономная
Hornet 4 Drive    21.4  экономная
Hornet Sportabout 18.7 растратная
Valiant           18.1 растратная
Duster 360        14.3 растратная
...
  • case_when() — усложненная версия if_else(), в которой можно выполнить сразу несколько проверок, и для каждой задать свой результат. Формат проверки: запрос ~ результат. Проверки будут проводиться строго по очереди:
# Разделим ирисы по длине лепестков:
iris %>% 
  mutate(size = case_when(
    Petal.Length > 6 ~ "большой",
    Petal.Length > 4 ~ "средний",
    Petal.Length > 2 ~ "маленький",
    Petal.Length > 1 ~ "малюсенький",
    TRUE ~ "ошибка"   # выполняется всегда, если не выполнились предыдущие
  ))
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species        size
1          5.1         3.5          1.4         0.2  setosa малюсенький
2          4.9         3.0          1.4         0.2  setosa малюсенький
3          4.7         3.2          1.3         0.2  setosa малюсенький
4          4.6         3.1          1.5         0.2  setosa малюсенький
...
# Если мы хотим, чтобы на диаграммах разные виды ирисов всегда
# помечались конкретными цветами, можно сделать для этого отдельный
# столбец с названиями цветов
iris %>% 
  mutate(colors = case_when(
    Species == "setosa" ~ "aquamarine2",
    Species == "virginica" ~ "darkviolet",
    Species == "versicolor" ~ "yellowgreen"
  ))
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species      colors
1          5.1         3.5          1.4         0.2  setosa aquamarine2
2          4.9         3.0          1.4         0.2  setosa aquamarine2
3          4.7         3.2          1.3         0.2  setosa aquamarine2
4          4.6         3.1          1.5         0.2  setosa aquamarine2
...

Впрочем, можно лаконичнее выполнить ту же процедуру с помощью следующей функции:

  • recode() — упрощенная версия case_when(), когда нужно только поменять конкретные значения в столбце:
iris %>% 
  mutate(colors = recode(Species,
                         "setosa" = "aquamarine2",
                         "virginica" = "darkviolet",
                         "versicolor" = "yellowgreen" ))
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species      colors
1          5.1         3.5          1.4         0.2  setosa aquamarine2
2          4.9         3.0          1.4         0.2  setosa aquamarine2
3          4.7         3.2          1.3         0.2  setosa aquamarine2
4          4.6         3.1          1.5         0.2  setosa aquamarine2
...
mtcars %>%
  mutate(am = recode(am,
                    "0" = "automatic",
                    "1" = "manual")) %>%
  select(am)
                         am
Mazda RX4            manual
Mazda RX4 Wag        manual
Datsun 710           manual
Hornet 4 Drive    automatic
Hornet Sportabout automatic
...

Однотипные преобразования многих переменных

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

mutate( across(столбцы, функция) )

Столбцы выбираются так же, как в select(). Функция для преобразования указывается либо названием (без кавычек, скобок и параметров), либо в виде анонимной лямбда-функции — так же, как в map().

Несколько простых примеров использования across() внутри mutate():

# Преобразуем три категориальных переменных mtcars в факторы:
mtcars %>% 
  mutate( across( c(am, cyl, gear), factor ) )

# Округлим числа во всех столбцах iris, не являющихся фактором:
iris %>% 
  mutate( across( !where(is.factor), round ) )

# Анонимная функция
# Преобразуем в iris все наблюдения в миллиметры
# Для этого умножим содержимое первых четырёх столбцов на 10:
iris %>% 
  mutate( across( 1:4, ~ (.x)*10 ) )

filter() — отбор нужных строк по критерию

Для выбора строк используйте логическое выражение: запрос, который даст ответ “да” или “нет”. Функция применит этот запрос ко всем строкам таблицы и оставит только те, где получит ответ “да”.

Чаще всего сравнивают значения переменных с конкретными числами, строками текста или значениями других переменных с помощью операторов сравнения: <, >, <=, >=, != (не равно), == (равно). Обратите внимание на последний оператор! Одна из наиболее частых, вездесущих и раздражающих ошибок в R — случайное использование = вместо == при сравнении.

Операторы & и | позволяют объединять разные запросы так, чтобы либо они все должны дать ответ “да” (&), либо хотя бы один (|). В сложных случаях можно использовать скобки. Простые запросы можно перечислить через запятую — это аналогично объединению через &.

# Пример сложного условия
mtcars %>% 
  filter( (mpg > 20 & am == '0') | (am == '1' & cyl == '8'))
                mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Hornet 4 Drive 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
Merc 240D      24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
Merc 230       22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
Toyota Corona  21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
Ford Pantera L 15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
Maserati Bora  15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
# Более типичный пример
iris %>% 
  filter(Species == 'virginica',
         Petal.Length > 6.5)
  Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
1          7.6         3.0          6.6         2.1 virginica
2          7.7         3.8          6.7         2.2 virginica
3          7.7         2.6          6.9         2.3 virginica
4          7.7         2.8          6.7         2.0 virginica

Помимо сравнений, значения переменных можно проверять с помощью функций — главное, чтобы они давали ответ “да”/“нет” (TRUE/FALSE). В частности, могут пригодиться is.na() (позволяет добавить строки с пропущенными значениями — NA), is.numeric(), is.factor(), и т.д.

slice() выбирает строки по номеру (положению в таблице)

Это работает аналогично номерам в квадратных скобках, но удобнее применять внутри конвейера.

arrange() — сортировка строк таблицы

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

Чтобы сортировать по убыванию, “окружите” название столбца функцией desc().

# Упорядочим машины по числу цилиндров, затем по типу коробки передач
# В каждой подгруппе отсортируем по убыванию mpg
mtcars %>% 
  arrange(cyl, am, desc(mpg)) %>% 
  select(cyl, am, mpg) # чтобы было удобнее смотреть
                    cyl am  mpg
Merc 240D             4  0 24.4
Merc 230              4  0 22.8
Toyota Corona         4  0 21.5
Toyota Corolla        4  1 33.9
Fiat 128              4  1 32.4
Honda Civic           4  1 30.4
Lotus Europa          4  1 30.4
Fiat X1-9             4  1 27.3
Porsche 914-2         4  1 26.0
Datsun 710            4  1 22.8
Volvo 142E            4  1 21.4
Hornet 4 Drive        6  0 21.4
Merc 280              6  0 19.2
Valiant               6  0 18.1
Merc 280C             6  0 17.8
Mazda RX4             6  1 21.0
Mazda RX4 Wag         6  1 21.0
Ferrari Dino          6  1 19.7
Pontiac Firebird      8  0 19.2
Hornet Sportabout     8  0 18.7
Merc 450SL            8  0 17.3
Merc 450SE            8  0 16.4
Dodge Challenger      8  0 15.5
Merc 450SLC           8  0 15.2
AMC Javelin           8  0 15.2
Chrysler Imperial     8  0 14.7
Duster 360            8  0 14.3
Camaro Z28            8  0 13.3
Cadillac Fleetwood    8  0 10.4
Lincoln Continental   8  0 10.4
Ford Pantera L        8  1 15.8
Maserati Bora         8  1 15.0

group_by() — разделение данных на группы

Многие операции требуется проводить не на всех данных, а на подгруппах. Например, посчитать среднюю длину “лепестков” ирисов из таблицы iris для каждого вида отдельно. Или найти максимальный показатель экономичности (mpg) для автомобилей из mtcars в зависимости от числа цилиндров. Можно отбирать нужные строки командой filter() и проводить операции на каждой полученной группе по-отдельности — но это значительно увеличивает число команд и раздувает код.

Команда group_by() позволяет удобно разбивать данные на группы по значениям категориальных (текстовых, факторных) переменных. При этом структура и отображение таблицы не меняются: функция просто добавляет пометки, к какой группе принадлежит каждая строка. Последующие операции с “группированной” таблицей будут проводиться отдельно по каждой группе. В основном это функции mutate(), filter() и summarise().

Для примера сначала рассчитаем долю длины “лепестков” ирисов относительно их средней длины:

iris %>% 
  mutate(relative.size = Petal.Length / mean(Petal.Length)) %>% 
  select(Petal.Length, relative.size)
  Petal.Length relative.size
1          1.4     0.3725386
2          1.4     0.3725386
3          1.3     0.3459287
4          1.5     0.3991485
...

А теперь относительно средней длины по каждому виду отдельно:

iris %>% 
  group_by(Species) %>% 
  mutate(relative.size = Petal.Length / mean(Petal.Length)) %>%
  select(Petal.Length, relative.size)
# A tibble: 4 × 3
# Groups:   Species [1]
  Species Petal.Length relative.size
  <fct>          <dbl>         <dbl>
1 setosa           1.4         0.958
2 setosa           1.4         0.958
3 setosa           1.3         0.889
4 setosa           1.5         1.03 
...

Команда ungroup() отменяет группирование.

Группировка по нескольким переменным приведёт к созданию групп по всем сочетаниям значений группирующих переменных — см. примеры в следующем разделе.

summarise() — создание сводных таблиц

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

Самый простой (и заведомо бесполезный) пример:

# Среднее значение 4-х переменных iris и общее число наблюдений:
iris %>% 
  summarise(S.L.mean = mean(Sepal.Length),
            S.W.mean = mean(Sepal.Width),
            P.L.mean = mean(Petal.Length),
            P.W.mean = mean(Petal.Width),
            count = n()
            )
  S.L.mean S.W.mean P.L.mean P.W.mean count
1 5.843333 3.057333    3.758 1.199333   150

Сам по себе этот пример неудачен, т.к. того же результата можно гораздо проще добиться с помощью других команд: lapply(), map(), summary(). Однако добавление функции group_by открывает мощные функциональные возможности:

# Среднее значение 4-х переменных iris и общее число наблюдений,
# рассчитанные отдельно по каждому виду:
iris %>%
  group_by(Species) %>%                   # единственная добавленная строка
  summarise(S.L.mean = mean(Sepal.Length),
            S.W.mean = mean(Sepal.Width),
            P.L.mean = mean(Petal.Length),
            P.W.mean = mean(Petal.Width),
            count = n()
            )
# A tibble: 3 × 6
  Species    S.L.mean S.W.mean P.L.mean P.W.mean count
  <fct>         <dbl>    <dbl>    <dbl>    <dbl> <int>
1 setosa         5.01     3.43     1.46    0.246    50
2 versicolor     5.94     2.77     4.26    1.33     50
3 virginica      6.59     2.97     5.55    2.03     50
# Несколько статистик для всех сочетаний числа цилиндров
# и типа коробки передач автомобилей:
mtcars %>% 
  group_by(am, cyl) %>% 
  summarise(number.of.cars = n(),      # сколько машин в группе
            V.shaped = sum(vs == '0'), # сколько машин имеет V-образный двигатель
            most.econom = max(mpg),    # максимальный mpg
            mean.weight = mean(wt) )   # средний вес
# A tibble: 6 × 6
# Groups:   am [2]
     am   cyl number.of.cars V.shaped most.econom mean.weight
  <dbl> <dbl>          <int>    <int>       <dbl>       <dbl>
1     0     4              3        0        24.4        2.94
2     0     6              4        0        21.4        3.39
3     0     8             12       12        19.2        4.10
4     1     4              8        1        33.9        2.04
5     1     6              3        3        21          2.76
6     1     8              2        2        15.8        3.37

Внутри summarise() можно использовать стандартные функции R, такие как mean(), sd(), max() и другие, дающие одно значение. В частности, обратите внимание на следующие функции:

n() число строк в каждой группе
sum(!is.na(x)) число непустых значений переменной х в каждой группе
n_distinct(x) число уникальных значений переменной х в каждой группе
sum(условие) число значений переменной х, удовлетворяющих условию
first(x) первое значение переменной х в каждой группе
nth(x, n) n-ное значение
last(x) последнее значение

Применение одной функции ко множеству столбцов

По аналогии с mutate(), можно использовать функцию across(), чтобы упростить код. Так, выше был рассмотрен расчет среднего значения четырёх переменных iris в зависимости от видовой принадлежности. Сократим код ещё сильнее:

iris %>% 
  group_by(Species) %>% 
  summarise( across( 1:4, mean ) )
# A tibble: 3 × 5
  Species    Sepal.Length Sepal.Width Petal.Length Petal.Width
  <fct>             <dbl>       <dbl>        <dbl>       <dbl>
1 setosa             5.01        3.43         1.46       0.246
2 versicolor         5.94        2.77         4.26       1.33 
3 virginica          6.59        2.97         5.55       2.03 

Можно применить к каждому столбцу сразу несколько функций:

iris %>% 
  group_by(Species) %>% 
  summarise( across( 1:4, list(mean=mean, sd=sd) ) )
# A tibble: 3 × 9
  Species    Sepal.Length_mean Sepal.Length_sd Sepal.Width_mean Sepal.Width_sd
  <fct>                  <dbl>           <dbl>            <dbl>          <dbl>
1 setosa                  5.01           0.352             3.43          0.379
2 versicolor              5.94           0.516             2.77          0.314
3 virginica               6.59           0.636             2.97          0.322
# ℹ 4 more variables: Petal.Length_mean <dbl>, Petal.Length_sd <dbl>,
#   Petal.Width_mean <dbl>, Petal.Width_sd <dbl>

  1. Некоторые устанавливаемые пакеты из Tidyverse нужно запускать “вручную” отдельными командами library() — в дальнейшем это всегда будет явно указано.↩︎

  2. В RStudio оператор конвейера %>% можно вставить сочетанием клавиш Ctrl+Shift+M.↩︎

  3. Возможен даже совсем простой вариант select(iris, is.numeric). Команда поругается, но всё сделает правильно.↩︎