Материалы для самостоятельного изучения

Графика с {ggplot2} - продолжение

Автор

Анна Валяева

Дата публикации

4 октября 2024 г.

Группированные датафреймы

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

starwars %>%
  filter(homeworld %in% c("Naboo", "Tatooine")) %>%
  group_by(eye_color, species) %>%
  summarise(
    n = n(),
    max_height = max(height, na.rm = TRUE)) %>%
  mutate(max_height_of_all = max(max_height, na.rm = TRUE))
1
Отбираем из датафрейма starwars только персонажей с планет Набу и Татуин
2
Группируем датафрейм по комбинациям значений из столбцов eye_color и species
3
Для каждой получившейся группы строк рассчитываем их число (записываем в новый столбец n) и максимальное значение по столбцу height (записываем в столбец max_height). Все остальные столбцы, не спользуемые при группировке, исчезают.
4
Добавлям столбец max_height_of_all, в который записываем максимальное значение из столбца max_height. Поскольку, к этому моменту осталась группировка датафрейма по столбцу eye_color, в новом столбце окажутся несколько значений - для каждого цвета глаз.
# A tibble: 8 × 5
# Groups:   eye_color [5]
  eye_color species     n max_height max_height_of_all
  <chr>     <chr>   <int>      <int>             <int>
1 blue      Human       5        188               188
2 blue      <NA>        1        183               188
3 brown     Human       6        185               185
4 brown     <NA>        1        183               185
5 orange    Gungan      3        224               224
6 red       Droid       2         97                97
7 yellow    Droid       1        167               202
8 yellow    Human       2        202               202

Применив функцию group_by(), вы получаете группированный датафрейм. Обратите внимание, что функция summarise() по умолчанию снимает один уровень группировки (по одному столбцу), а mutate() не влияет на группировку совсем. Если вы группировали датафрейм, например, по двум столбцам, то после одного использования summarise() датафрейм все еще останется граппированным по первому из перечисленных в group_by() столбцов. Чтобы вручную избавиться от граппировки датафрейма, необходимо применить функцию ungroup().

starwars %>%
  filter(homeworld %in% c("Naboo", "Tatooine")) %>%
  group_by(eye_color, species) %>%
  summarise(
    n = n(),
    max_height = max(height, na.rm = TRUE)) %>%
  ungroup() %>%
  mutate(max_height_of_all = max(max_height, na.rm = TRUE))
1
Отбираем из датафрейма starwars только персонажей с планет Набу и Татуин
2
Группируем датафрейм по комбинациям значений из столбцов eye_color и species
3
Для каждой получившейся группы строк рассчитываем их число (записываем в новый столбец n) и максимальное значение по столбцу height (записываем в столбец max_height). Все остальные столбцы, не спользуемые при группировке, исчезают.
4
Снимаем все группировки с датафрейма.
5
Добавлям столбец max_height_of_all, в который записываем максимальное значение из столбца max_height. В этот раз группировок не осталось, поэтому будет посчитано одно значение по всему столбцу max_height.
# A tibble: 8 × 5
  eye_color species     n max_height max_height_of_all
  <chr>     <chr>   <int>      <int>             <int>
1 blue      Human       5        188               224
2 blue      <NA>        1        183               224
3 brown     Human       6        185               224
4 brown     <NA>        1        183               224
5 orange    Gungan      3        224               224
6 red       Droid       2         97               224
7 yellow    Droid       1        167               224
8 yellow    Human       2        202               224

Графика

Facets

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

sw_eye_colors <- starwars %>% 
  drop_na(eye_color) %>% 
  mutate(fct_eye_color = fct_lump_n(eye_color, 5))

sw_eye_colors_pl <- sw_eye_colors %>% 
  ggplot(aes(x = height, fill = fct_eye_color)) +
  geom_density(alpha = 0.5) +
  scale_fill_manual(values = c(levels(sw_eye_colors$fct_eye_color)[1:5], "grey"))

sw_eye_colors_pl

В такой ситуации разумно использовать фасеты (facets) - разбить график на несколько и собрать их в панель. Это можно сделать с помощью функции facet_wrap() из пакета {ggplot2}. Если вы хотите разбить график по двум или более категориальным переменным, то вам пригодится функция facet_grid(). У этих функций немного необычный синтаксис: столбец с категориальной переменной, по которой вы хотите разбить график на несколько, необходимо указывать через тильду ~ или задавать при помощи функции vars(). Также заголовки графиков в панели настраиваются необычным образом - с помощью объектов типа labeller. Кроме того, вам пригодится параметр scales, который определяет, будет ли для всех графиков использоваться единый масштаб или нет.

sw_eye_colors_pl +
  facet_wrap(vars(fct_eye_color)) +
  theme(legend.position = "none")

Объединение графиков в панели

Каждый график, нарисованный с помощью {ggplot2} или совместимых пакетов, представляет собой ggplot-объект. Несколько графиков можно собрать в панель, разместив графики на одной странице, согласно определенной схеме (layout). Существует несколько пакетов, которые позволяют создавать такие панели из графиков в R: {patchwork}, {cowplot} и др. Эти пакеты не входят к коллекцию tidyverse - их нужно устанавливать дополнительно.

library(patchwork)

densitypl <- starwars %>% 
  ggplot(aes(x = height)) +
  geom_density(fill = "yellowgreen")

dotpl <- starwars %>%
  filter(species %in% c("Droid", "Gungan")) %>%
  ggplot(aes(x = species, y = height, color = mass)) +
  geom_point(size = 5, alpha = 0.8) +
  scale_color_gradient(
    low = "#455e89", high = "#b7094c",
    breaks = seq(50, 150, 50),
    limits = c(30, 150))

scatterpl <- starwars %>% 
  ggplot(aes(x = height, y = mass)) +
  geom_point(size = 3, alpha = 0.8, color = "coral") +
  scale_y_log10() +
  coord_cartesian(xlim = c(0, 300))


(dotpl + scatterpl) / densitypl +
  plot_annotation(tag_levels = "A")

Задания

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

Задание 1

Задание 1.1

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

lab_people <- tibble(
  day = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"),
  n_people = c(3, 1, 11, 2, 5, 1, 0))

Задание 1.2

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

lab_people2 <- tibble(
  lab = c("Great R Visualizations Lab", "The Neighbour's Lab"),
  Monday = c(3, 5),
  Tuesday = c(1, 9),
  Wednesday = c(11, 7),
  Thursday = c(2, 4),
  Friday = c(5, 5),
  Saturday = c(1, 4),
  Sunday = c(0, 1))

Задание 2

Прочитайте датасет про исчезнувшие виды растений по ссылке https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-08-18/plants.csv.

Изучите датасет!

Что в нем есть:

  • binomial_name - название вида растения,
  • country - страна произрастания,
  • continent - континент произрастания,
  • group - таксономическая группа (Цветковое растение - Flowering plant, и т.д.),
  • year_last_seen - период, когда было замечено в природе последний раз,
  • threat_ и action_ - виды угроз и действий по сохранению вида,
  • red_list_category - категория в Красной книге: Extinct или Extinct in the Wild.

Больше информации о датасете по ссылке: https://github.com/rfordatascience/tidytuesday/blob/master/data/2020/2020-08-18/readme.md.

Постройте столбчатую диаграмму числа исчезнувших видов цветковых растений и не-цветковых растений на каждом континенте.

Пояснения:

  • В колонке group оставьте группу Flowering plants (цветковые растения), остальные группы растений объедините в группу Non-flowering plants (не-цветковые растения). Используйте возможности пакета forcats.
  • Посчитайте число исчезнувших видов (Extinct) по каждому континенту, в каждой таксономической группе (Flowering plants и Non-flowering plants).
  • Нарисуйте группированную столбчатую диаграмму (2 отдельных столбца, для Flowering plants и Non-flowering plants, расположенные рядом). По горизонтали расположите континенты.
  • Пусть континенты будут расположены в порядке убывания количества стран на них.
  • Осмысленно подпишите оси.
  • Перенесите легенду наверх, уберите название легенды.
  • Обозначьте Flowering plants желтым и Non-flowering plants зеленым.

Задание 3

Загрузите датасет, содержащий динамику использования сои в пищевой и непищевой промышленности за несколько последних десятков лет. Ссылка для скачивания: https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-04-06/soybean_use.csv.

Изучите датасет! Про него можно прочитать дополнительно, например, здесь: https://github.com/rfordatascience/tidytuesday/blob/master/data/2021/2021-04-06/readme.md.

Информация про датасет:

  • entity - континент, часть континента или страна,
  • code - код страны,
  • year - год,
  • human_food - сколько сои было использованно в пищевой промышленности (в тоннах),
  • animal_feed - сколько сои было использовано в производстве кормов для животных (в тоннах),
  • processed - сколько сои было использовано в непищевой промышленности (производство биотоплива и т.д.) (в тоннах).

Отберите для анализа данные только по 6 континентам: c("Africa", "Europe", "Asia", "Northern America", "South America", "Australia & New Zealand"), за 33 года - 1981-2013. Нарисуйте столбчатую диаграмму, которая бы изображала среднее количество сои по трем типам промышленности, использованной за 33 года на всех 6 обитаемых континентах. Изобразите разброс 3/4 SD (чтобы линии разброса выровнялись со столбцами, возможно, вам понадобится параметр position = position_dodge(width = 0.9)). Отсортируйте континенты по среднему количеству сои, использованной во всех областях промышленности за исследуемый период. Логарифмируйте ось Y с помощью подходящей функции scale_y_*. Измените формат числовых подписей по оси Y.

Добавьте осознанные названия осей. Поверните названия континентов, чтобы они отображались целиком, на 45 градусов. Уберите название легенды и поменяйте цвета (например, на c("#ffb4a2", "#e5989b", "#b5838d")) и названия категорий промышленности в легенде (чтобы было более приятно читать).

Задание 4

Задание 4.1

Напишите функцию, которая бы рассчитывала ядерно-цитоплазматическое соотношение (“N:C” или “karyoplasmic” ratio) по формуле \(\frac{NucleusVol}{CellVol}\), где NucleusVol - объем ядра (мкм3), CellVol - объем клетки (мкм3).

На небольшом примере продемонстрируйте работу этой функции.

Задание 4.2

Прочитайте данные по размерам ядер и клеток разных организмов из разнообразных таксономических групп, они находятся по ссылке https://raw.githubusercontent.com/kirushka/datasets/main/NC_ratio.csv. Эти данные взяты из статьи Malerba et al. 2021.

В прочитанном датафрейме 4 столбца:

  • SpeciesGroup - название таксономической группы,
  • Species - видовое название организма,
  • NucleusVol - объем ядра (мкм3),
  • CellVol - объем клетки (мкм3).

Используя функцию, которую вы создали в предыдущем задании, посчитайте ядерно-цитоплазматическое соотношение (“N:C” или “karyoplasmic” ratio) для каждого организма - запишите результат в новый столбец.

Также преобразуйте строковый столбец SpeciesGroup в столбец с факторами. Отсортируйте уровни фактора по убыванию медианного значения NC ratio.

Задание 4.3

По модифицированному датафрейму постройте точковую диаграмму, на которой по оси X отложен объем клеток, по оси Y - NC ratio, цветом показана таксономическая группа.

Добавьте на график линии тренда, построенные с помощью линейной регрессии, для каждой таксономической группы с помощью geom_smooth(method = "lm").

Чтобы использовать на графиках “математические” обозначения, греческие буквы и т.п., например, в подписях осей, можно использовать функцию expression, которой подать на вход все выражение без кавычек. Например, так: ggplot(...) + labs(x = expression(Cell~volume~(mu*m^3))).

Для настройки “tick labels”, т.е. подписей интервалов на осях, можно использовать пакет scales. Например, scale_x_log10(labels = scales::label_math(format = log10)) преобразит подписи по оси X в формат вида \(10^{log10(x)}\). Поэкспериментируйте и с другими вариантами отображения этих подписей.

Поменяйте цветовую палитру графика.

Задание 4.4

Разделите график из задания 4.3 на “панельки” с помощью facet_wrap, так чтобы каждая таксономическая группа оказалась на своем графике.

Задание 5

Напишите несколько функций для моделирования подбрасывания кубика.

Задание 5.1

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

Задание 5.2

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

Задание 5.3

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

Задание 5.4

Напишите функцию, которая возвращает результат подбрасывания “честного” кубиков с заданным числом граней. Число граней задается параметром sides. Продемонстрируйте работу функции.

Задание 5.5

Напишите функцию, которая возвращает результат подбрасывания нескольких “честного” кубиков с заданным числом граней. Число граней задается параметром sides (пусть по умолчанию оно будет равно 6). Число кубиков задается параметром n. Продемонстрируйте работу функции.