Графическая библиотека ggplot2 и ее возможности

Библиотека 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)

Построим первый график. До этого занятия мы не обсуждали линейные графики (line plots), но все с ними так или иначе сталкивались, когда следили за динамикой каких-то количественных показателей. Попробуем визуализировать динамику температуры тела бобров в течении времени (в качестве показателя времени будем использовать id замера температуры, так как все замеры производились последовательно, с интервалом в минут).

# в 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 не факторная (как должна быть), а количественная (бинарная). Чтобы получить правильную легенду, скорректируем тип переменной:

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 наблюдений, а это очень мало.

Ящики с усами (box plots) и скрипичные диаграммы (violin plots)

Про ящики с усами и скрипичные диаграммы мы уже говорили, поэтому давайте просто их построим:

# ящик с усами
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()

Диаграммы рассеяния (scatter plots) и пузырьковые диаграммы (bubble plots)

Обычные диаграммы рассеяния мы строили и не раз. Построим теперь диаграмму рассеяния с помощью 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 позволяет делать график как будто объемным – добавлять дополнительные измерения. На диаграмму рассеяния выше мы можем добавить значение еще одной переменной, не превращая при этом график в какую-то трехмерную конструкцию. Каким образом? Сделав размер точек на диаграмме рассеяния зависмым от значений третьей переменной! Более того, можно добавить и четвертое измерение – закрашивать точки на графике разным цветом в зависимости от значений еще одного показателя.

Давайте сейчас сделаем следующее: построим диаграмму рассеяния для индексов 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), как в домашнем задании, и сделаем нашу пузырьковую диаграмму осмысленно разноцветной.

Добавим переменную:

library(dplyr)
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")