Menu

ggplot2 в R: введение в грамматику графики

Как работает ggplot2 — мысленная модель «данные + aes() + geom», отображение против фиксированной настройки, labs(), facet_wrap(), темы и сохранение через ggsave().

ggplot2 строит графики из слоёв

ggplot2 — самый широко используемый пакет графики в R, и причина, по которой на него молятся, — идея дизайна: грамматика графики. Базовый R даёт по функции на диаграмму — plot(), hist(), barplot(), — каждая со своими аргументами. ggplot2 вместо этого даёт небольшой набор составных частей — набор данных, отображение столбцов на визуальные свойства и слои рисования, — которые складываются через + в любой график. Выучите грамматику один раз, и каждый новый тип диаграммы окажется тем же предложением с одним изменённым словом. Бонусом идут умолчания (серая панель, разумные оси, автоматические легенды), выглядящие презентабельно без всяких усилий по оформлению.

Это дополнительный пакет, поэтому ему нужна разовая установка и загрузка в каждой сессии:

install.packages("ggplot2")   # once, on your machine
library(ggplot2)              # at the top of every script that uses it

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

Мысленная модель: данные, отображение, геометрия

Каждый ggplot отвечает на три вопроса, по одному на часть:

  1. Данные — какой датафрейм? ggplot(mtcars, ...)
  2. Отображение — какие столбцы управляют какими визуальными свойствами? aes(x = wt, y = hp)
  3. Геометрия — как рисовать наблюдения? + geom_point()

Вот анатомия, элемент за элементом:

ggplot(mtcars, aes(x = wt, y = hp)) +
    geom_point()
  • ggplot(mtcars, ...) начинает график, чьими данными по умолчанию является mtcars. Сам по себе он рисует пустую серую панель — слоёв пока нет.
  • aes(x = wt, y = hp) — это отображение эстетик: горизонтальная позиция берётся из столбца wt, вертикальная из hp. Обратите внимание, что вы пишете голые имена столбцов, а не mtcars$wt: отображение вычисляется внутри переданных вами данных.
  • + geom_point() добавляет слой, рисующий каждую строку точкой. + действительно является сложением слоёв: график — это объект, который вы наращиваете, и добавлять можно и дальше — ещё геом, подписи, тему, — каждый со своим +.

Запустите — и получите ту же диаграмму рассеяния «вес против мощности», что и версия на базовом R в руководстве по диаграммам рассеяния, но на серой панели с белой сеткой. Замените geom_point() на geom_line() — и то же отображение станет линейным графиком: вот грамматика в действии.

Геомы, которыми вы будете пользоваться

Дюжина геомов покрывает почти всю повседневную работу:

geom_point()       # scatter plot: two numeric variables
geom_line()        # line chart: trends over an ordered variable
geom_col()         # bar chart from pre-computed heights (needs y)
geom_bar()         # bar chart that counts raw rows for you (no y)
geom_histogram()   # distribution of one numeric variable (set binwidth)
geom_boxplot()     # distributions compared across groups
geom_smooth()      # fitted trend line; method = "lm" for straight

Слои складываются, и вот здесь ggplot2 начинает обыгрывать базовый R по выразительности: geom_point() + geom_smooth(method = "lm") — это диаграмма рассеяния с линией регрессии и доверительной полосой в одной читаемой строке. Поскольку графики являются объектами, можно также сохранить основу и ветвиться от неё:

p <- ggplot(mtcars, aes(x = wt, y = hp)) + geom_point()

p + geom_smooth(method = "lm")   # one variant
p + geom_smooth()                # another (loess curve)

Отображение против настройки: классическая путаница с aes()

Ошибка, которую делает каждый новичок в ggplot2, обычно на первой неделе, — помещение буквального цвета внутрь aes(). Правило коротко — внутри aes() означает «меняется вместе с данными»; снаружи означает «фиксированная настройка», — и эти два размещения делают совершенно разные вещи:

# MAPPING: color varies by a column - each cylinder count gets its own
# color, and a legend appears automatically
ggplot(mtcars, aes(x = wt, y = hp, color = factor(cyl))) +
    geom_point(size = 2)

# SETTING: every point is literally steelblue - no legend, no mapping
ggplot(mtcars, aes(x = wt, y = hp)) +
    geom_point(color = "steelblue", size = 2)

А вот и сама ловушка:

# WRONG: "blue" inside aes() is treated as data, not as a color
ggplot(mtcars, aes(x = wt, y = hp, color = "blue")) +
    geom_point()

Это нарисует лососево-розовые точки с легендой, единственная запись которой подписана «blue». Почему: aes() отображает столбцы на свойства, поэтому color = "blue" создаёт фальшивый столбец из одной категории, значение которого везде равно слову «blue», и ggplot назначает этой категории свой первый цвет по умолчанию, который оказывается красноватым лососевым. Если вы имели в виду «сделай точки синими», цвет уходит за пределы отображения: geom_point(color = "blue"). То же правило управляет size, shape, fill и alpha. (Заодно обратите внимание на factor(cyl) в примере с отображением: cyl числовой, и обёртка в factor() просит различные цвета по группам вместо непрерывного цветового градиента.)

Подписи, фасеты и темы

labs() именует всё в одном месте — а график не готов, пока у него нет настоящих подписей:

ggplot(mtcars, aes(x = wt, y = hp, color = factor(cyl))) +
    geom_point(size = 2) +
    labs(title = "Horsepower vs. weight",
         x     = "Weight (1000 lbs)",
         y     = "Horsepower",
         color = "Cylinders")

Фасеты — главная особенность ggplot2: разбить один график на серию малых кратных, по панели на группу, с общими осями, чтобы панели сравнивались честно:

ggplot(mtcars, aes(x = wt, y = hp)) +
    geom_point() +
    facet_wrap(~ cyl)

Появляются три панели — по одной для 4-, 6- и 8-цилиндровых машин, — разложенные сеткой, с подписью значения группы над каждой панелью. В базовом R это жонглирование par(mfrow) с ручной синхронизацией пределов; здесь это одна строка. Фасетирование вознаграждает аккуратные данные, поэтому ggplot2 так естественно сочетается с конвейером dplyr: отфильтровали, свели — и подали результат прямо в ggplot().

Темы переоформляют всё «неданные» чернила одним вызовом: + theme_minimal() (чистый белый фон), + theme_bw(), + theme_classic() (только оси, без сетки) или серое умолчание. Выберите одну на проект и придерживайтесь её — отчёт, где все рисунки в одной теме, читается как один документ, а не как альбом вырезок.

Сохранение через ggsave() — и когда базовый R по-прежнему годится

ggsave() записывает последний график на диск, выводя формат из расширения файла:

ggsave("hp-vs-weight.png", width = 8, height = 5)          # last plot shown
ggsave("hp-vs-weight.pdf", plot = p, width = 8, height = 5) # a saved object

width и height по умолчанию задаются в дюймах; указывайте их явно, потому что размеры шрифтов масштабируются вместе с размерами вывода, а умолчания редко подходят к тому месту, куда направляется рисунок.

Что до того, стоит ли вообще возиться: для пятисекундного взгляда на одну переменную по ходу анализа plot() базового R и родственники требуют меньше набора и нулевых зависимостей — приза за использование ggplot2 везде не дают. ggplot2 окупается в момент, когда в рисунке появляются группы, легенды, фасеты или аудитория: грамматика масштабируется на ту сложность, с которой базовая графика справляется лишь с усилием.

Что вы уносите с собой

  • ggplot2 собирает каждую диаграмму из данных + отображения aes() + слоёв geom, соединённых через +; выучите грамматику один раз и переиспользуйте её для любого типа диаграммы.
  • aes() отображает столбцы на визуальные свойства — голые имена столбцов, вычисляемые в вашем датафрейме.
  • Внутри aes() = меняется вместе с данными (и получает легенду); снаружи = фиксированная настройка. aes(color = "blue") — классический баг.
  • labs() для заголовков, facet_wrap(~ group) для малых кратных, theme_minimal() и родственники для оформления, ggsave() с явными размерами для экспорта.
  • Быстрый одиночный взгляд — базовый R. Группы, фасеты или аудитория — ggplot2.

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

Часто задаваемые вопросы

Что такое ggplot2 в R?

ggplot2 — самый популярный пакет построения графиков в R. Вместо одной функции на тип диаграммы вы собираете каждый график из трёх одних и тех же частей: датафрейма, отображения aes() столбцов на визуальные свойства и одного или нескольких слоёв geom_*(), соединённых через +. Он входит в tidyverse.

Как установить ggplot2?

Выполните install.packages("ggplot2") один раз, а затем library(ggplot2) в начале каждого скрипта, который его использует. Установка помещает пакет на вашу машину; library() загружает его в текущую сессию.

Что делает aes() в ggplot2?

aes() отображает столбцы ваших данных на визуальные свойства: aes(x = wt, y = hp, color = factor(cyl)) говорит, что горизонтальная позиция берётся из wt, вертикальная из hp, а цвет точки из числа цилиндров. Всё внутри aes() меняется вместе с данными; всё за его пределами является фиксированной настройкой.

Почему мои точки в ggplot не того цвета, который я задал?

Вы поместили цвет внутрь aes(): aes(color = "blue") не задаёт цвет, а отображает выдуманную категорию с названием «blue», поэтому ggplot выбирает собственный цвет по умолчанию и добавляет легенду. Чтобы задать буквальный цвет, выносите его за пределы отображения: geom_point(color = "blue").

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ