Библиотека ggplot2
позволяет строить красивые графики. Установим библитеку.
install.packages("ggplot2")
Обратимся к библиотеке ggplot2
(и заодно к dplyr
, она нам тоже понадобится):
library(ggplot2)
library(dplyr)
Чтобы разобраться с логикой построения графиков с помощью ggplot2
, пока будем работать с простыми данными – данными по температуре тела бобров beaver1
(к политологии вернемся на следующем занятии). Подготовим базу:
beav <- beaver1 # загрузим базу - она встроена в R
beav$id <- 1:dim(beaver1)[1] # добавим id
Теперь перейдем к ggplot2
. Можно считать, что у библиотеки ggplot2
есть своя философия, поняв которую, строить графики гораздо легче.
Во-первых, графики ggplot
многослойные, то есть строятся они поэтапно, по слоям. Сначала указывается база данных, с которой мы работаем, и интересующие нас показатели (первый слой), затем указывается тип графика (второй слой), затем настройки для подписей, легенды и прочее (остальные слои). Все слои добавляются через +
.
Во-вторых, для любого графика указывается параметр aes
, сокращенно от aesthetics, задающий оформление графика, которое непосредственно связано с переменными в базе данных. О чем речь? Проще понять на примерах.
Пример 1. Строим диаграмму рассеяния для роста и веса человека, хотим, чтобы все точки на диаграмме рассеяния были зелеными.
Пример 2. Строим диаграмму рассеяния для роста и веса человека, хотим, чтобы точки на диаграмме рассеяния, соответствующие женщинам, были красными, а мужчинам – синими.
В первом примере оформление графика никак не связано со значениями переменных в базе данных, все точки закрашиваем одним цветом. Во втором примере цвет точек зависит от значения переменной пол, то есть оформление графика связано с переменными в базе данных. Как увидим позже, в случаях, аналогичным первому, цвет точек будет определяться за пределами aes()
, второму – внутри aes()
.
Построим первый график. До этого занятия мы не обсуждали линейные графики (line plots), но все с ними так или иначе сталкивались, когда следили за динамикой каких-то количественных показателей. Попробуем визуализировать динамику температуры тела бобров в течении времени (в качестве показателя времени будем использовать id замера температуры, так как все замеры производились последовательно, с интервалом в 10 минут).
# в aes - показатели по оси x и y
# через + указан тип графика
ggplot(data = beav, aes(x = id, y = temp)) + geom_line()
Типы графиков можно сочетать. Добавим точки (чтобы получились точки, соединенные линиями):
ggplot(data = beav, aes(x = id, y = temp)) + geom_line() + geom_point()
Цвета и типы точек и линий можно изменять. Сделаем это!
# синие линии
# точки поменьше
ggplot(data = beav, aes(x = id, y = temp)) + geom_line(color = "blue") +
geom_point(size = 0.5)
Разных опций, конечно, много. Чтобы узнать о всех возможностях, можно запросить help отдельно для оформления точек или линий:
?geom_point
?geom_line
Посмотрим теперь, в каких случаях параметры оформления графика имеет смысл указывать внутри aes()
. В “бобриной” базе данных у нас есть переменная activ – активность бобров (0 - не активен, 1 – активен). Представим, что мы хотим построить два линейных графика в одной плоскости: один для неактивных бобров, другой – для активных.
# group - группировка по переменной, чтобы получилось 2 отдельных графика
# color - чтобы разные группы точек были разного цвета (в зависимости от значений activ)
ggplot(data = beav, aes(x = id, y = temp, group = activ, color = activ)) +
geom_line() + geom_point()
Внимание, вопрос: почему график правильный, а легенда у него такая странная? Переменная activ принимает всего два значения, 0 и 1, а тут целая шкала от 0 до 1 образовалась… Эта проблема возникла потому, что у нас в базе данных переменная activ не факторная (тип factor
), а числовая (тип numeric
). Чтобы получить правильную легенду, скорректируем тип переменной:
beav <- beav %>% mutate(activ = factor(activ))
Посмотрим теперь:
ggplot(data = beav, aes(x = id, y = temp, group = activ, color = activ)) +
geom_line() + geom_point()
Теперь все верно. Но цвета поменялись. По умолчанию в R разбивка на две группы – разбивка по признаку “пол”, поэтому цвета получились такими. Конечно, их можно поменять:
# scale_color_manual - задаем вектор значений цветов
ggplot(data = beav, aes(x = id, y = temp, group = activ, color = activ)) +
geom_line() + geom_point() +
scale_color_manual(values = c("red", "blue"))
А теперь с помощью этого же слоя scale_color_manual
поменяем названия групп, указанных в легенде графика:
# то же + вектор labels
ggplot(data = beav, aes(x = id, y = temp, group = activ, color = activ)) +
geom_line() + geom_point() +
scale_color_manual(values = c("red", "blue"),
labels = c("Not active", "Active"))
Теперь осталось узнать, как подписывать оси на графике и добавлять заголовок. Для всего этого есть один слой labs
:
# title - заголовок
# x - подпись оси x
# y - подпись оси y
ggplot(data = beav, aes(x = id, y = temp, group = activ, color = activ)) +
geom_line() + geom_point() +
scale_color_manual(values = c("red", "blue"),
labels = c("Not active", "Active")) +
labs(title = "Beavers: body temperature",
x = "Observations",
y = "Temperature, C")
В завершение нашего первого знакомства с ggplot2
поменяем тему графика (theme). По умолчанию график строится на сером фоне, но фон можно сделать, например, белым:
# добавляем еще слой с theme
ggplot(data = beav, aes(x = id, y = temp, group = activ, color = activ)) +
geom_line() + geom_point() +
scale_color_manual(values = c("red", "blue"),
labels = c("Not active", "Active")) +
labs(title = "Beavers: body temperature",
x = "Observations",
y = "Temperature, C") +
theme_bw() # black white
Или, наоборот, темным:
ggplot(data = beav, aes(x = id, y = temp, group = activ, color = activ)) +
geom_line() + geom_point() +
scale_color_manual(values = c("red", "blue"),
labels = c("Not active", "Active")) +
labs(title = "Beavers: body temperature",
x = "Observations",
y = "Temperature, C") +
theme_dark()
А теперь перейдем к другим графикам.
Для чего нужны гистограммы, мы уже обсуждали. Гистограммы строятся для визуализации формы распределения количественного показателя. Построим гистограмму для температуры тела бобров:
ggplot(data = beav, aes(x = temp)) +
geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Пока выглядит несильно симпатично. Начинаем исправлять. Для начала поменяем цвет. При изменении цвета “заполненных” (состоящих не из отдельных линий и точек) графиков нужно помнить, что есть два параметра: color
и fill
. Параметр color
отвечает за цвет границ графика, а за не цвет их заливки. А уже fill
– как раз за заливку.
# желто-зеленая гистограмма
# столбцы которой очерчены черной линией
ggplot(data = beav, aes(x = temp)) +
geom_histogram(fill = "yellowgreen", color = "black")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
На гистограммы можно добавлять вспомогательные вертикальные или горизонтальные линии. Например, можно отчертить значение частоты, равной 10:
# yintercept - значение, где прямая пересекает ось y
ggplot(data = beav, aes(x = temp)) +
geom_histogram(fill = "yellowgreen", color = "black") +
geom_hline(yintercept = 10, color = "red")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Гистограммы тоже можно строить отдельно для разных групп наблюдений, оставаясь при этом в пределах одного графика. В нашем случае это не очень наглядно (в группе активных бобров всего 6 наблюдений), но для примера можно построить гистограммы по группам:
ggplot(data = beav, aes(x = temp, group = activ, fill = activ)) +
geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Если требуется построить графики распределения, которые накладываются друг на друга, для большей наглядности вместо гистограмм иногда используют сглаженные графики плотности распределения (kernel density plots).
# alpha = 0.5 - для 50% прозрачности
ggplot(data = beav, aes(x = temp, group = activ, fill = activ)) +
geom_density(alpha = 0.5)
Такие графики выглядят симпатично, однако могут дезинформировать. Из-за того, что такие графики плотности получаются путем сглаживания гистограммы, они могут получаться неточными. Например, не будут отражены некоторые перепады в частотах, сгладятся “пики” распределения. Даже в нашем случае заметны неточности: по гистограмме видно, что в распределении температуры тела активных бобров есть “дырки” (некоторых столбцов нет), а на сглаженном графике плотности мы этого не увидим.
Раз уж зашла речь о дезинформации, обсудим еще один важный момент. Очень удобно (и честно!), когда на графике по возможности отражено число наблюдений, по которому он строился. Если в случае с точечными графиками это видно и так (много точек или совсем мало), то в случае гистограмм, графиков плотности, ящиков с усами и прочих, число наблюдений определить по графику сложновато. В ggplot
для отметки наблюдений есть специальный параметр rugs
(устоявшегося русскоязычного термина нет). Выглядит это следующим образом:
ggplot(data = beav, aes(x = temp, group = activ, fill = activ)) +
geom_density(alpha = 0.5) + geom_rug()
Под графиком добавляются “палочки” – обозначения наблюдений. И хотя эти засечки (rugs
) не показывают явно общее числе наблюдений (вряд ли кто-то захочет их считать), по ним можно представлять, сколько наблюдений сконцентрировано в той или иной части графика. Зачем это нужно? Представим, что мы ничего не знаем о базе данных по бобрам и видим графики плотностей распределения по группам. Нам может показаться, что, если мы исключим несколько значений температуры тела активных бобров в окрестности 37.5 градусов, распределение температуры тела этих бобров будет похоже на нормальное. Однако, когда мы посмотрим на график с rugs, про нормальность мы думать не будем – увидим, что в группе всего 6 наблюдений, а это очень мало.
Про ящики с усами и скрипичные диаграммы мы уже говорили, поэтому давайте просто их построим:
# ящик с усами
ggplot(data = beav, aes(x = "", y = temp)) +
geom_boxplot()
# ящики с усами по группам
ggplot(data = beav, aes(x = "", y = temp, group = activ, fill = activ)) +
geom_boxplot()
# скрипичные диаграммы по группам
ggplot(data = beav, aes(x = "", y = temp, group = activ, fill = activ)) +
geom_violin()
Обычные диаграммы рассеяния мы строили и не раз. Построим теперь диаграмму рассеяния с помощью ggplot2
. Для начала возьмем нашу любимую базу по показателям WGI и Freedom House.
dat <- read.csv("wgi_fh_new.csv", dec = ",")
dat <- na.omit(dat)
Построим диаграмму рассеяния для индексов Voice & Accountability (va) и Rule of Law (rl).
# диаграмма рассеяния
ggplot(data = dat, aes(x = va, y = rl)) +
geom_point() +
labs(title = "WGI indicators", x = "Voice and Accountability", y = "Rule of Law")
О том, что маркеры для точек можно менять, мы уже знаем (параметр shape
в geom_point
). Попрактикуемся на семинаре. А пока познакомимся с пузырьковой диаграммой (bubble plot).
Bubble plot позволяет делать график как будто “объемным” – добавлять дополнительные измерения. На диаграмму рассеяния выше мы можем добавить значение еще одной переменной, не превращая при этом график в какую-то трехмерную конструкцию. Каким образом? Сделав размер точек на диаграмме рассеяния зависимым от значений третьей переменной! Более того, можно добавить и четвертое измерение – закрасить точки на графике разным цветом в зависимости от значений еще одного показателя.
Давайте сейчас сделаем следующее: построим диаграмму рассеяния для индексов Voice & Accountability и Rule of Law, учитывая при этом значение индекса Freedon House в интересующих нас государствах.
# тот же aes, но теперь еще и в geom_point
# величина точки зависит от fh, и оно задано внутри aesthetics
# цвет пока у всех точек один, поэтому он задан вне aesthetics
ggplot(data = dat, aes(x = va, y = rl)) +
geom_point(aes(size = fh), color = "darkred") +
labs(title = "WGI indicators", x = "Voice and Accountability",
y = "Rule of Law")
А теперь финальный аккорд. Давайте добавим в базу данных факторную переменную для значений Freedom House (Free, Partly Free, Not Free), как в домашнем задании, и сделаем нашу пузырьковую диаграмму осмысленно разноцветной.
Добавим переменную:
# из ДЗ
dat <- dat %>% mutate(not_free = as.integer(fh >= 5.5),
partly_free = as.integer(fh >= 3 & fh <= 5),
free = as.integer(fh <= 2.5))
colnames(dat)[11] <- "fh_score"
dat$fh_type <- names(dat[12:14])[max.col(dat[12:14])]
dat$fh_type <- factor(dat$fh_type)
Построим диаграмму:
ggplot(data = dat, aes(x = va, y = rl)) +
geom_point(aes(size = fh_score, color = fh_type)) +
labs(title = "WGI indicators", x = "Voice and Accountability",
y = "Rule of Law")
Графики можно строить по группам так, чтобы графики для каждой группы были в отдельной ячейке (“фасетке”):
# слой facet_grid
# в нем указан показатель (признак), по которому делим на группы
ggplot(data = dat, aes(x = va, y = rl)) +
geom_point() +
labs(title = "WGI indicators", x = "Voice and Accountability",
y = "Rule of Law") + facet_grid(~fh_type)
Бонус: небольшой бонус – библиотека ggflags
, с помощью которой вместо точек на график можно наносить флаги государств. См. описание здесь.
Установим ее. Эта библиотека интересна тем, что она устанавливается не из официального “хранилища” (CRAN), а с Github. Установим сначала библиотеку для разработчиков devtools
, а затем с ее помощью поставим ggflags
.
install.packages("devtools")
library(devtools)
install_github("baptiste/ggflags") # имя пользователя и библиотека
Выберем несколько строк из нашей базы данных (для небольшого числа точек, конечно, диаграмма рассеяния как-то не очень интересна, но для примера):
set.seed(111)
cnt_sample <- dat[sample(nrow(dat), 5), ]
View(cnt_sample)
Нам нужны флаги следующих стран: Malta, Poland, Hong Kong, Libya, Honduras.
library(ggflags)
# коды стран для флагов
cnt_sample$codes = c("mt", "pl", "hk", "ly", "hn")
# график
# заодно добавили названия стран:
# label в aes
# слой geom_text(с выравниваем подписей по горизонтали и вертикали)
ggplot(data = cnt_sample, aes(x = va, y = rl, country = codes, label = country)) + geom_flag() + geom_text(hjust=0.5, vjust=-0.5)
Disclaimer: я не знаю, насколько часто обновляется эта библиотека и насколько актуальны флаги государств, используемые в ней.