Mar 27: ggplot2 pt.1

Запись занятий


Все записи организованы в плейлист


ggplot2 package

Компоненты графика

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

  • данные
  • пространство координат
  • визуальные параметры (aesthetics)
  • геометрическая форма представления данных (geom)
  • статистические вычисления и трансформации (stats)
  • параметры смещения графиков на координатной сетке (position adjustment)
  • параметры осей (scales)
  • фасеты (группировка, facets)

Ниже представлен типовой код создания графика с помощью пакета ggplot2. На графике отражены все перечисленные структурные элементы: данные (объект mpg), координатная сетка (coord_cartesian()), параметры визуализаций (функция aes() и её аргументы). Основная особенность синтаксиса ggplot2 — его аддитивность, когда объект графика создается путем постепенного добавления к базовому объекту новых элементов и/или параметров визуализаций. Базовый объект создается с помощью функции ggplot(), а различные слои, геометрические объекты или текстовые метки, параметры визуализаций и т.д. - другими функциями, через внутренний оператор +:

ggplot() +
  geom_smooth(method = 'lm') +
  geom_point(data = mpg, mapping = aes(x = hwy, y = cty, color = cyl)) +
  coord_cartesian() +
  scale_color_gradient() +
  theme_classic()


Базовые геомы

geom_point()

Функция geom_point() позволяет конструировать точечные диаграммы (графики рассеяния). Стандартные аргументы функций: data (таблица, данные из которой надо отрисовать) и mapping (с помощью этого аргумента указываются, какие колонки таблица как именно будут использованы при отрисовке — какие будут задавать оси, какие - цвета или форму).

# подключаем библиотеки
library(ggplot2)
library(data.table)

# импорт данных
imdb_link <- 'https://gitlab.com/upravitelev/mar201s/raw/master/data/IMDb movies.csv'
tg_cols <- c("director", "title", "original_title", "year", 
             "genre", "duration", "country", "avg_vote")

imdb <- fread(imdb_link, select = tg_cols)
imdb[, year := as.numeric(year)]

imdb_lynch <- imdb[director == 'Woody Allen']
imdb_martin <- imdb[director == 'Martin Scorsese']
imdb_lynch <- imdb[director == 'David Lynch']

imdb_genres <- imdb[genre %in% c('Horror', 'Comedy', 'Drama')]
imdb_genres_scores <- imdb_genres[, 
                                  list(n_titles = .N, votes = mean(avg_vote)), 
                                  by = list(year, genre)] 

theme_set(theme_classic())

Рисуем точками фильмы, которые выпустил Дэвид Линч. Задаём базовый объект с помощью ggplot() и на него наслаиваем геом geom_point(). Получаем стандартный график, где черным цветом отрисованы точки, а названия осей взяты из названий колонок.

ggplot() +
  geom_point(data = imdb_lynch,
             mapping = aes(x = year, y = avg_vote))


0.0.0.2.1 geom_point() aesthetics

У geom_point() есть множество опций, которые позволяют задать визуальные параметры (aesthetics) точки графика. Самые часто используемые:

  • colour: цвет края точки (названием или rgb-кодом)
  • fill: цвет заливки точки (названием или rgb-кодом)
  • shape: форма точки, можно задать номером или по названию.
  • size: размер точки
  • alpha: прозрачность точки
Типы точек по номерам:
Типы точек по названиям:


0.0.0.2.2 Настройка визуальных параметров

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

Повторим предыдущий график (фильмы Линча), и в mapping укажем, что цвет и форма точки задаются по значениям из колонки country, а размер точки берется из значений в колонке duration.

ggplot() +
  geom_point(data = imdb_lynch,
             mapping = aes(x = year, y = avg_vote,
                           color = country, shape = country, size = duration))  

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

На графике ниже все точки, маркирующие фильмы Дэвида Линча, покрашены в красный цвет и сделаны ромбами, независимо от страны или еще каких-то других особенностей фильма. А вот размер точки все так же зависит от значений в колонке duration:

ggplot() +
  geom_point(data = imdb_lynch,
             mapping = aes(x = year, y = avg_vote),
             color = 'red', shape = 'diamond', size = 4)


geom_line()

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

Покажем динамику оценок фильмов жанра Horror:

ggplot() +
  geom_line(data = imdb_genres_scores[genre == 'Horror'], 
            mapping = aes(x = year, y = votes))

Воспользуемся механизмом добавления слоев и добавим на график ещё и точки:

ggplot() +
  geom_line(
    data = imdb_genres_scores[genre == 'Horror'], 
    mapping = aes(x = year, y = votes)) + 
  geom_point(
    data = imdb_genres_scores[genre == 'Horror'],
    mapping = aes(x = year, y = votes))


0.0.0.2.3 geom_line() aesthetics

Основные визуальные параметры схожи с параметрами точек, тип линии задаётся параметром linetype:

  • colour: цвет линии (названием или rgb-кодом)
  • linetype: тип линии
  • size: толщина линии
  • alpha: прозрачность линии
Типы линий можно задать как номером, так и названием:

Пунктирные линии также можно задать кодом, код должен состоять из 2, 4, 6 или 8 символов шестнадцатеричной системы счисления, где цифры обозначают количество точек. Например, код 3519 означает ‘три отрисуй, 5 пропусти, 1 отрисуй, 9 пропусти, повторяй в цикле’. В результате получается вот такой пунктир:

ggplot() +
  geom_line(data = imdb_genres_scores, 
            mapping = aes(x = year, y = votes,
                          group = genre, color = genre, linetype = genre))


0.0.0.2.4 Настройка визуальных параметров

Аналогично geom_point() можно указать конкретные значения, так же можно указать, что значения должны браться из определенной колонки датасета. Возьмём датасет по жанрам, чтобы можно было в зависимости от жанра указать тип и цвет линии:

ggplot() +
  geom_line(data = imdb_genres_scores, 
            mapping = aes(x = year, y = votes,
                          group = genre, color = genre, linetype = genre))

Если указать эти параметры не в mapping, а задать конкретные значения, то изменены будут параметры всех линий, независимо от жанра:

ggplot() +
  geom_line(data = imdb_genres_scores, 
            mapping = aes(x = year, y = votes,
                          group = genre, color = genre, linetype = genre), 
            size = 1.5)


геомы визуальных акцентов

geom_vline()

Простейший геом, который позволяет с помощью вертикальной линии акцентировать внимание пользователя на какой-то части кода (vline - vertical line). Точка на оси OX, из которой выводится линия, задается с помощью аргумента xintercept. Этот геом не требует обязательного указания датасета и осей, так как не особо зависит от них - главное, чтобы ось OX была такого же типа, как задается xintercept (число, строка, дата).

Укажем на графике границу 2000 года, чтобы понять, какие фильмы Линч снял после 2010 года.

ggplot() +
  geom_point(
    data = imdb_lynch,
    mapping = aes(x = year, y = avg_vote, 
                  color = country, shape = country, size = duration)) + 
  geom_vline(xintercept = 2010)


geom_hline()

Аналогично geom_vline() позволяет нанести на график горизонтальную линию. Для этого надо указать yintercept - значение на оси OY, из которого будет выводиться линия, параллельная OX. Графически отметим фильмы, которые имеют avg_vote больше 0. У Дэвида Линча был один фильм (согласно датасету), у которого оценка была заметно больше 8, и тот был задолго до 2000 года:

ggplot() +
  geom_point(
    data = imdb_lynch,
    mapping = aes(x = year, y = avg_vote, 
                  color = country, shape = country, size = duration)) + 
  geom_vline(xintercept = 2010) + 
  geom_hline(yintercept = 8)


geom_text()

Еще один инструмент расстановки акцентов на график — нанесение текстовых меток значений. В самом простом виде geom_text() аналогичен геомам точек и линий, с единственным отличием: метки задаются с помощью аргумента label в mapping. В качестве источника значений для меток указывают колонку датасета, в нашем случае это original_title:

ggplot() +
  geom_text(
    data = imdb_lynch,
    mapping = aes(x = year, 
                  y = avg_vote, 
                  label = original_title)
  )


0.0.0.2.5 geom_text() aesthetics

Для текстовых меток на графике есть ряд дополнительных параметров, помимо размера или цвета. Это семейство шрифта (sans, serif, mono) и тип выделения (обычный, жирный, курсив). Так как текстовые метки обычно приводятся для каких-то других элементов графиков (точек или линий), еще есть аргументы hjust и vjust, для сдвига по вертикали или по горизонтали относительно целевой координаты.

  • alpha: прозрачность
  • angle: угол (если надо разместить метку под углом к точке)
  • colour: цвет
  • hjust: сдвиг по вертикали
  • vjust: сдвиг по погризонтали
  • lineheight: межстрочный интервал
  • size: размер
  • family: семейство шрифта (с засечками, без засечек)
  • fontface: тип выделения
  • check_overlap: если TRUE, то текстовые метки размещаются без того, чтобы перекрывать друг друга

Значения для family и fontface:

df_fontface <- data.frame(x = 1:4, fontface = c("plain", "bold", "italic", "bold.italic"))
df_family <- data.frame(x = 1:3, y = 3:1, family = c("sans", "serif", "mono"))

ggplot() + 
  geom_text(data = df_fontface, 
            mapping = aes(x = x, y = 0.1, label = fontface, fontface = fontface), 
            size = 10) + 
  geom_text(data = df_family, 
            mapping = aes(x = x, y = 0.2, label = family, family = family), 
            size = 10) + 
  lims(x = c(0.5, 4.5), y = c(0, 0.3)) + 
  theme_void()


Настройка визуальных параметров

Аналогично прочим геомам можно задать параметры как константу, можно использовать значения определенных колонок для управления цветом, размером и т. д. У geom_text() так же есть ряд параметров, который не может быть связан со значениями колонок — это запрет на пересечение текстовых меток (check_overlap) и смещение по вертикали/горизонтали (hjust, vjust). Сами по себе текстовые метки не очень интересны, поэтому также накладываем слой точек:

ggplot() +
  geom_point(
    data = imdb_lynch,
    mapping = aes(x = year, y = avg_vote, size = duration),
    color = 'steelblue') + 
  geom_text(
    data = imdb_lynch, 
    mapping = aes(x = year, y = avg_vote, label = original_title),
    check_overlap = TRUE,
    hjust = -0.1
  )


Композиция слоев

Если внимательно посмотреть на график, на котором были бы точками отмечены фильмы режиссера и текстовые метки названий, то видно много повторяющихся элементов — аргументы data и частично mapping в каждом геоме.

ggplot() +
  geom_point(
    data = imdb_lynch,
    mapping = aes(x = year, y = avg_vote, size = duration),
    color = 'steelblue') + 
  geom_text(
    data = imdb_lynch, 
    mapping = aes(x = year, y = avg_vote, label = original_title),
    check_overlap = TRUE,
    hjust = -0.1
  )

Можно сократить количество кода, воспользовавшись логикой наследования параметров геомов от основного ggplot(). Таким образом датасет и параметры осей можно указать в ggplot() и они будут применяться ко всем наслаиваемым геомам. А в геомах можно оставить только параметры этого конкретного геома.

ggplot(data = imdb_lynch, mapping = aes(x = year, y = avg_vote)) +
  geom_point(mapping = aes(size = duration), color = 'steelblue') + 
  geom_text(mapping = aes(label = original_title), check_overlap = TRUE, hjust = -0.1)

Если сокращать ещё больше, то можно опустить указание аргументов и воспользоваться тем, что в ggplot() первый аргумент задает датасет, второй — метод использования колонок (mapping). В геомах наоборот, сначала указывается mapping:

ggplot(imdb_martin, aes(x = year, y = avg_vote)) +
  geom_point(aes(size = duration), color = 'steelblue') + 
  geom_text(aes(label = original_title), check_overlap = TRUE, hjust = -0.1)

Наследование параметров не отменяет того, что на график можно наложить данные другого датасета (если параметры осей совпадают), для этого надо так же, как и ранее, указать датасет в геоме. Наложим на точечный график фильмов Дэвида Линча фильмы Мартина Скорсезе и покрасим их красным:

ggplot(imdb_lynch, aes(x = year, y = avg_vote)) +
  geom_point(aes(size = duration), color = 'steelblue') + 
  geom_text(aes(label = original_title), check_overlap = TRUE, hjust = -0.1) + 
  geom_point(
    data = imdb_martin, 
    color = 'red'
  )

Дополнительные материалы

Документация по пакету, есть примеры.

Шпаргалки - короткие и наглядные справочные материалы по основам синтаксиса и базовым геомам

R cookbook - сборник примеров и кейсов, как решать наиболее часто встречающиеся задачи при работе с ggplot. Сгруппировано по разделам.

Список названий цветов

Elegant Graphics for Data Analysis (Use R!) - книга автора ggplot2, с очень внятным описанием базовых идей. При желании, можно найти в сети.

Домашнее задание

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

Задание 1

С помощью пакета ggplot2 отрисуйте график рассеяния, отражающий связь таких параметров, как carat и price. Используйте уже доступный после установки ggplot2 датасет diamonds, сделайте выборку на 10000 строк (используйте set.seed(1234) для генерации зерна генератора случайных цифр). Удалите строки, в которых carat > 3. Сабсет назовите diamonds_sample. Для конвертации diamonds в data.table вам поможет as.data.table()

Задание 2

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

Задание 3

Добавьте на график из задания 2 вертикальную линию (OX = 2) и горизонтальную линию (OY = 15000).

Задание 4

Выделите цветами из образованных вертикальной и горизонтальной линиями секторов первый и третий секторы (счет против часовой). Используйте зеленый и красный цвета соответственно, с параметром прозрачности opacity = 0.1. Сами линии можно не рисовать. Вам потребуется выбрать необходимый геом для решения этой задачи.

Задание 5

Модифицируйте предыдущий график: сделайте точки во втором и четвертом секторах черными.