forked from Pozdniakov/tidy_stats
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path230-ggplot2.qmd
461 lines (334 loc) · 37 KB
/
230-ggplot2.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# Грамматика графики `ggplot2` {#sec-gg_ggplot2}
## История грамматики графики {#sec-grammar_of_graphics}
Встроенные возможности для визуализации в R довольно обширны, но дополнительные пакеты значительно ее расширяют. Среди этих пакетов, есть один, который занимает совершенно особенное место -- `ggplot2`.
`ggplot2` --- это не просто пакет, который рисует красивые графики. Красивые графики можно рисовать и в базовом R. Чтобы понять, почему пакет `ggplot2` занимает особенное место среди пакетов для визуализации (и не только среди пакетов для R, а вообще!), нужно расшифровать `gg` в его названии. `gg` означает грамматику графики (Grammar of Graphics), язык для описания графиков, изложенный в одноименной книге Леланда Уилкинсона [@gg].
Грамматика графики позволяет описывать графики не в терминах типологии (вот есть пайчарт, есть барплот, есть гистограмма, а есть ящик с усами), а с помощью специального разработанного языка. Этот язык позволяет с помощью грамматики и небольшого количества "слов" языка описывать и создавать практически любые графики и даже придумывать новые! Это дает огромную свободу в создании именно той визуализации, что необходима для текущей задачи.
Хэдли Уикхэм (да, снова он) немного дополнил идею грамматики графики в статье "A Layered grammar of graphics" [@layered-grammar], которую сопроводил пакетом `ggplot2` с реализацией идей Уилкинсона и своих.
## Основы грамматики графики {#sec-gg_base}
Каждый график состоит из одного или нескольких **слоев (layers)**. Если слоев несколько, то они располагаются один над другим, при этом верхние слои "перекрывают" нижние, примерно как это происходит в программах вроде Adobe Photoshop. У каждого слоя есть три обязательных элемента: **данные (data)**, **геом (geom)**, **эстетики (aestetics)**; и два вспомогательных: **статистические трансформации (stat).** и **регулировка положения (position adjustment).**
![](images/ggplot2_scheme.png)
- **Данные (data).** Собственно, сами данные в виде датафрейма, используемые в данном слое.
- **Геом (geom).** Геом --- это сокращение от "геометрический объект". Собственно, в какой геометрический объект мы собираемся превращать данные. Например, в точки, прямоугольники или линии.
- **Отображение (mapping).** Эстетические отображения или просто **эстетики (aestetics)** --- это набор правил, как различные переменные превращаются в визуальные особенности геометрии. Без эстетик остается непонятно, какие именно колонки в используемом датафрейме превращаются в различные особенности геомов: позицию, размер, цвет и т.д. У каждой геометрии свой набор эстетик, но многие из них совпадают у разных геомов, например, *x*, *y*, *colour*, *fill*, *size*. Без некоторых эстетик геом не будет работать. Например, геометрия в виде точек не будет работать без двух координат этих точек (*x* и *y*). Другие эстетики необязательны и имеют значения по умолчанию. Например, по умолчанию точки будут черными, но можно сделать их цвет зависимым от выбранной колонки в датафрейме с помощью эстетики *colour*.
- **Статистические трансформации (stat).** Название используемой статистической трансформации (или просто --- статистики). Да, статистические трансформации можно делать прямо внутри `ggplot2`! Это дает дополнительную свободу в выборе инструментов, потому что обычно те же статистические трансформации можно сделать вне `ggplot2` в процессе препроцессинга. Формально, статистические трансформации --- это обязательный элемент геома, но если вы не хотите преобразовывать данные, то можете выбрать "identity" преобразование, которое оставляет все как есть. В `ggplot2` у каждого геома есть статистика по умолчанию, а у каждой статистики - свой геом по умолчанию. И не всегда статистика по умолчанию --- это "identity" статистика. Например, для барплота (`geom_barplot()`) используется статистика "count" [^230-ggplot2-1], которая считает частоты, ведь именно частоты затем трансформируются в высоту барплотов.
[^230-ggplot2-1]: идентична по своему смыслу функции `dplyr::count`, которая считает частоты по выбранной колонке тиббла \@ref(tidy_count)
<!-- -->
- **Регулировка положения (position adjustment).** Регуляровка положения --- это небольшое улучшение позиции геометрий для части элементов. Например, можно добавить немного случайного шума ("jitter") в позицию точек, чтобы они не перекрывали друг друга. Или "раздвинуть" ("dodge") два барплота, чтобы один не загораживал другой. Как и в случае со статистическими трансформациями, в большинстве случаев значение по умолчанию --- "identity".
Кроме слоев, у графика есть:
- **Координатная система (coord).** Если мы задали координаты, то нам нужно задать и координатную плоскость, верно? Конечно, в большинстве случаев используется *декартова система координат (Cartesian coordinate system)* [^230-ggplot2-2], т.е. стандартная прямоугольная система координат, но можно использовать и другие, например, полярную систему координат или картографическую проекцию.
[^230-ggplot2-2]: Декартова система координат названа в честь великого математика и философа Рене Декарта, на латинском --- *Renatus Cartesius*, отсюда и название *cartesian coordinate system*.
<!-- -->
- **Шкалы (scales).** Шкалы задают то, *как именно* значения превращаются в эстетики. Например, если мы задали, что разные значения в колонке будут влиять на цвет точки, то какая именно палитра будет использоваться? В какие конкретно цвета будут превращаться числовые, логические или строковые значения в колонке? В `ggplot2` есть правила по умолчанию для всех эстестик, и они отличные, но самостоятельная настройка шкал может значительно улучшить график.
- **Фасетки (facets).** Фасетки --- это одно из нововведений Уикхэма в грамматику графики. Фасетки повзоляют разбить график на множество похожих, задав переменную, по которой график будет разделен. Это очень напоминает использование группировки с помощью `group_by()`.
- **Тема (theme).** Тема --- это зрительное оформление "подложки" графика, не относящийся к содержанию графика: размер шрифта, цвет фона, размер и цвет линий на фоне и т.д. и т.п. В `ggplot2` есть несколько встроенных тем, а также есть множество пакетов, которые добавляют дополнительные темы. Кроме того, их можно настраивать самостоятельно!
- **Значения по умолчанию (defaults).** Если в графике используется несколько слоев, то часто все они используют одни и те же данные и эстетики. Можно задать данные и эстетики по умолчанию для всего графика, чтобы не повторять код.
## Пример №0: пайчарт с распределение по полу {#sec-gg_0}
Сейчас мы попробуем сделать простой пример в `ggplot2`, похожий на пример, который использует в своей книге Леланд Уилкинсон, чтобы показать мощь грамматики графики [@gg]. Приготовьтесь, этот пример перевернет ваши представления о графиках!
Но сначала взглянем на структуру кода в `ggplot()`.
![](images/ggplot_code.png){width="400"}
Как видно, код чем-то напоминает стандартный код tidyverse, но с `+` вместо пайпов. Когда был написан `ggplot2`, Хэдли Уикхэм еще не знал про `%>%` из `magrittr`, хотя по смыслу `+` означает примерно то же самое.
```{r}
library(tidyverse)
heroes <- read_csv("https://raw.githubusercontent.com/Pozdniakov/tidy_stats/master/data/heroes_information.csv",
na = c("-", "-99"))
```
```{r, echo = FALSE, message = FALSE}
library(showtext)
showtext_auto()
```
Итак, запустим функцию `ggplot()`, задав наш тиббл `heroes` в качестве данных.
```{r}
ggplot(data = heroes)
```
Мы ничего не получили! Это естественно, ведь мы задали только данные по умолчанию, но не задали геометрию и эстетики.
Функция `ggplot()` не просто отрисовывает график, эта функция создает объект класса `ggplot`, который можно сохранить и модифицировать в дальнейшем:
```{r}
almost_empty_ggplot <- ggplot(data = heroes)
almost_empty_ggplot
```
Возьмем `geom_bar()` для отрисовки барплота. В качестве эстетик поставим `x = Gender` и `fill = Gender`. Поскольку это эстетики, они обозначаются внутри функции параметра `mapping = aes()` или просто внутри функции `aes()`. По умолчанию, `geom_bar()` имеет статистику "count", что нас полностью устраивает: `geom_bar()` сам посчитает табличу частот и использует значения `Gender` для обозначения позиций и заливки, а посчитанные частоты будет использовать для задания высоты столбцов.
```{r}
ggplot(data = heroes) +
geom_bar(aes(x = Gender, fill = Gender))
```
Сейчас мы сделаем один хитрый трюк: поставим значение эстетики `x = ""`, чтобы собрать все столбики в один.
```{r}
ggplot(data = heroes) +
geom_bar(aes(x = "", fill = Gender))
```
Получилось что-то не очень симпатичное, но вполне осмысленное: доли столбца обозначают относительную частоту.
Можно настроить общие параметры геома, не зависящие от данных. Это нужно делать *вне* функции `aes()`, но внутри функции для геома.
```{r}
ggplot(data = heroes) +
geom_bar(aes(x = "", fill = Gender), width = .2)
```
> Казалось бы, причем здесь Minecraft?
А теперь внимание! Подумайте, какого действия нам не хватает, чтобы из имеющегося графика получить пайчарт?
```{r}
ggplot(data = heroes) +
geom_bar(aes(x = "", fill = Gender)) +
coord_polar(theta = "y")
```
Нам нужно было всего-лишь поменять систему координат с декартовой на полярную (круговую)! Иначе говоря, пайчарт - это барплот в полярной системе координат.
Именно в этом основная сила грамматики графики и ее реализации в `ggplot2` --- вместо того, чтобы описывать и рисовать огромное количество типов графиков, можно описать практически любой график через небольшой количество элементарных элементов и правила их соединения.
Получившийся пайчарт осталось подретушировать, убрав все лишние элементы подложки с помощью самой минималистичной темы `theme_void()` и добавив название графика:
```{r}
ggplot(data = heroes) +
geom_bar(aes(x = "", fill = Gender)) +
coord_polar(theta = "y") +
theme_void() +
labs(title = "Gender distributions for superheroes")
```
Это был интересный, но немного шуточный пример. Все-таки пайчарт --- это довольно спорный способ визуализировать данные, вызывающий много вполне справедливой критики. Поэтому сейчас мы перейдем к гораздо более реалистичному примеру.
## Пример №1: Education and IQ meta-analysis {#sec-gg_1}
Для этого примера мы возьмем мета-анализ связи количества лет обучения и интеллекта: *"How Much Does Education Improve Intelligence? A Meta-Analysis"* [@eduiq]. Мета-анализ --- это группа статистических методов, которые позволяют объединить результаты нескольких исследований с похожим планом исследованием и тематикой, чтобы посчитать средний эффект между несколькими статьями сразу.
Данные и скрипт для анализа данных в этой статье находятся в открытом доступе: https://osf.io/r8a24/
Полный текст статьи доступен по [ссылке](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6088505/).
Существует положительная корреляция между количеством лет, который человек потратил на обучение, и интеллектом. Это может объясняться по-разному: как то, что обучение повышает интеллект, и как то, что люди с высоким интеллекте стремятся получать больше образования. Напрямую в эксперименте это проверить нельзя, поэтому есть несколько квази-экспериментальных планов, которые косвенно указывают на верность той или иной гипотезу. Например, если в стране изменилось количество лет обязательного школьного образования, то повлияло ли это на интеллект целого поколения? ~~Или все-таки дело в Моргенштерне~~
![](images/meta_2.png)
Данная картинка показывает, насколько размер эффекта (выраженный в баллах IQ) зависит от того, какой средний возраст участвоваших в исследовании испытуемых.
Каждая точка на этом графике --- это отдельное исследование, положение по оси *x* --- средний возраст респондентов, а положение по оси *y* - средний прирост интеллекта согласно исследованию. Размер точки отражает "точность" исследования (грубо говоря, чем больше выборка, тем больше точка). Два графика обозначают два квазиэкспериментальных плана.
Мы сфокусируемся на нижней картинке с "Policy change" --- это как раз исследования, в которых изучается изменения интеллекта в возрастных группах после изменения количества лет обучения в школе.
Мы полностью воспроизведем код построчно, посмотрим, как эта картинка создавалась шаг за шагом.
Заметьте, данный датасет использует немного непривычный для нас формат хранения данных. Попытайтесь самостоятельно прочитать его.
```{r}
library(tidyverse)
df <- read_tsv("https://raw.githubusercontent.com/Pozdniakov/tidy_stats/master/data/meta_dataset.txt")
```
Давайте посмотрим, как устроен датафрейм `df`:
```{r}
df
```
Каждая строчка --- это результат отдельного исследования, при этом одна статья может включать несколько исследований,
В дальнейшем мы будем использовать код авторов статьи и смотреть, строчка за строчкой, как он будет работать.
```{r}
cpiq <- subset(df, subset=(Design=="Control Prior IQ"))
poli <- subset(df, subset=(Design=="Policy Change"))
```
Авторы исследования используют `subset()`, это функция базового R, принцип которой очень похож на `filter()` [^230-ggplot2-3].
[^230-ggplot2-3]: Кстати, именно функция `subset()` вдохновила Уикхема на создание `filter()`.
Итак, начнем рисовать сам график. Сначала иницируем объект `ggplot` с данными `poli` по умолчанию.
```{r}
ggplot(data=poli)
```
Теперь добавим в качестве эстетик по умолчанию координаты: `aes(x=Outcome_age, y=Effect_size)`.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size), data=poli)
```
Что изменилось? Появилась координатная ось и шкалы. Заметьте, масштаб неслучаен: он строится на основе разброса значений в выбранных колонках. Однако этого недостаточно для отрисовки графика, нехватает геометрии: нужно задать, в какую географическую сущность отобразятся данные.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size), data=poli) +
geom_point()
```
Готово! Это и есть основа картинки. Добавляем размер:
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point()
```
Перед нами возникла проблема оверплоттинга: некоторые точки перекрывают друг друга, поскольку имеют очень близкие координат. Авторы графика решают эту проблему очевидным способом: добавляют прозрачности точкам. Заметьте, прозрачность задается для всех точек одним значением, поэтому параметр `alpha` задается вне функции `aes()`.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55)
```
Совершенно так же задается и цвет. Он задается одинаковым для всех точек с помощью [HEX-кода](https://ru.wikipedia.org/wiki/Цвета_HTML).
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825")
```
Теперь добавим регрессионную прямую с доверительными интервалами на график. Это специальный геом `geom_smooth()` со специальной статистикой, который займет второй слой данного графика.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_smooth()
```
По умолчанию `geom_smooth()` строит кривую линию. Поставим `method = "lm"` для прямой.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_smooth(method="lm")
```
Теперь нужно поменять цвет: ярко синий цвет, используемый по умолчанию здесь попросту мешает восприятию графика.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_smooth(method="lm", colour="#BA1825")
```
Авторы графика перекрашивают серую полупрозначную область тоже. В этом случае используется параметр `fill =`, а не `colour =`, но цвет используется тот же.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825")
```
Регрессионную линию авторы немного утоньшают с помощью параметра `size =`.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5)
```
Чтобы сместить фокус в сторону точек, авторы добавляют прозрачности для всего `geom_smooth()`.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25)
```
На шкале присутствует 0, и по умолчанию он никак не обозначен. Это легко исправить с помощью вспомогательного геома `geom_hline()`.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_hline(yintercept=0) +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25)
```
Оттенить эту линию можно, сделав ее пунктирной.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_hline(yintercept=0, linetype="dotted") +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25)
```
Авторы графика вручную задают деления шкалы по оси *x*.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_hline(yintercept=0, linetype="dotted") +
scale_x_continuous(breaks=c(20,30,40,50,60,70,80)) +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25)
```
С помощью функции `guides()` убирают легенду с картинки.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_hline(yintercept=0, linetype="dotted") +
scale_x_continuous(breaks=c(20,30,40,50,60,70,80)) +
guides(size=F) +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25)
```
Следующим этапом авторы добавляют подписи шкал и название картинки. Обратите внимание на `\n` внутри подписи к оси *y*, которая задает перенос на следующую строку.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_hline(yintercept=0, linetype="dotted") +
scale_x_continuous(breaks=c(20,30,40,50,60,70,80)) +
xlab("Age at outcome test (years)") +
ylab("Gain for 1 year of education\n(IQ points)") +
guides(size=F) +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25) + ggtitle("Policy Change")
```
Теперь пришло время сделать график более красивым и понятным с помощью изменения подложки, т.е. работы с темой графика. Здесь тема задается сначала как `theme_bw()` --- встроенная в `ggplot2` минималистичная тема, а потом через функцию `theme()`, через которую можно управлять конкретными элементами темы. Здесь это сделано, чтобы передвинуть название графика к центру.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
geom_point(alpha=.55, colour="#BA1825") +
geom_hline(yintercept=0, linetype="dotted") +
theme_bw() +
scale_x_continuous(breaks=c(20,30,40,50,60,70,80)) +
xlab("Age at outcome test (years)") +
ylab("Gain for 1 year of education\n(IQ points)") +
guides(size=F) +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25) + ggtitle("Policy Change")+
theme(plot.title = element_text(hjust=0.5))
```
Готово! Мы полностью воспроизвели график авторов статьи с помощью их открытого кода.
Если вы помните, то в изначальном графике было две картинки. Авторы делают их отдельно, с помощью почти идентичного кода. Нечто похожее можно сделать по-другому, применяя фасетки.
Для этого мы возьмем неотфильтрованный датасет `df`, а с помощью колонки `Design`, на основании которой разделялся датасет для графиков, произведем разделение графиков внутри самого `ggplot` объекта. Для этого нам понадобится функция `facet_wrap()`, в которой с помощью формулы можно задать колонки, по которым будут разделены картинки по вертикали (слева от \~) и горизонтально (справа от \~). Пробуем разделить графики горизонтально:
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=df) +
geom_point(alpha=.55, colour="#BA1825") +
geom_hline(yintercept=0, linetype="dotted") +
theme_bw() +
scale_x_continuous(breaks=c(20,30,40,50,60,70,80)) +
xlab("Age at outcome test (years)") +
ylab("Gain for 1 year of education\n(IQ points)") +
guides(size=F) +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25) + ggtitle("Policy Change")+
theme(plot.title = element_text(hjust=0.5)) +
facet_wrap(~Design)
```
Здесь становится очевидно, почему авторы не включали данные `"School Age Cutoff"` третьим графиком: средний возраст участников этих исследований сильно отличается.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=df %>% filter(Design != "School Age Cutoff")) +
geom_point(alpha=.55, colour="#BA1825") +
geom_hline(yintercept=0, linetype="dotted") +
theme_bw() +
scale_x_continuous(breaks=c(20,30,40,50,60,70,80)) +
xlab("Age at outcome test (years)") +
ylab("Gain for 1 year of education\n(IQ points)") +
guides(size=F) +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25) + ggtitle("Policy Change")+
theme(plot.title = element_text(hjust=0.5)) +
facet_wrap(~Design)
```
Теперь поставим два графика друг над другом, поместив `Design` слева от `~` внутри `facet_wrap()`. Справа нужно добавить точку.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=df %>% filter(Design != "School Age Cutoff")) +
geom_point(alpha=.55, colour="#BA1825") +
geom_hline(yintercept=0, linetype="dotted") +
theme_bw() +
scale_x_continuous(breaks=c(20,30,40,50,60,70,80)) +
xlab("Age at outcome test (years)") +
ylab("Gain for 1 year of education\n(IQ points)") +
guides(size=F) +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25) + ggtitle("Policy Change")+
theme(plot.title = element_text(hjust=0.5)) +
facet_grid(Design~.)
```
Теперь нужно изменить подписи.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=df %>% filter(Design != "School Age Cutoff")) +
geom_point(alpha=.55, colour="#BA1825") +
geom_hline(yintercept=0, linetype="dotted") +
theme_bw() +
scale_x_continuous(breaks=c(20,30,40,50,60,70,80)) +
xlab("Age at outcome test (years)") +
ylab("Gain for 1 year of education\n(IQ points)") +
guides(size=F) +
geom_smooth(method="lm", colour="#BA1825",fill="#BA1825",size=.5, alpha=.25) +
ggtitle("Effect of education as a function of age at the outcome test")+
theme(plot.title = element_text(hjust=0.5)) +
facet_grid(Design~.)
```
Чтобы акцентировать графики, можно раскрасить их в разные цвета в дополнение к фасеткам. Для этого мы переносим `colour =` и `fill =` из параметров соответствующих геомов внутрь эстетик и делаем зависимыми от `Design`. Поскольку эти эстетики (точнее, `colour =`) одинаковы заданы для двух геомов (`geom_point()` и `geom_smooth()`), то мы спокойно можем вынести их в эстетики по умолчанию --- в параметры `aes()` внутри `ggplot()`.
При этом сразу выключим легенды для новых эстетик, потому они избыточны.
```{r}
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2), colour = Design, fill = Design), data=df %>% filter(Design != "School Age Cutoff")) +
geom_point(alpha=.55) +
geom_hline(yintercept=0, linetype="dotted") +
theme_bw() +
scale_x_continuous(breaks=c(20,30,40,50,60,70,80)) +
xlab("Age at outcome test (years)") +
ylab("Gain for 1 year of education\n(IQ points)") +
guides(size=FALSE, colour = FALSE, fill = FALSE) +
geom_smooth(method="lm", size=.5, alpha=.25) +
ggtitle("Effect of education as a function of age at the outcome test")+
theme(plot.title = element_text(hjust=0.5)) +
facet_grid(Design~.)
```
Слишком блеклая палитра? Не беда, можно задать палитру вручную! В `ggplot2` встроены легендарные *Brewer's Color Palettes*, которыми мы и воспользуемся.
Функции для шкал устроены интересным образом: они состоят из трех слов, первое из которых `scale_*_*()`, второе --- эстетика, например, `scale_color_*()`, а последнее слово --- тип самой шкалы, в некоторых случаях - специальное название для используемой шкалы, как и в случае с `scale_color_brewer()`.
```{r}
meta_2_gg <- ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2), colour = Design, fill = Design), data=df %>% filter(Design != "School Age Cutoff")) +
geom_point(alpha=.55) +
geom_hline(yintercept=0, linetype="dotted") +
theme_bw() +
scale_x_continuous(breaks=c(20,30,40,50,60,70,80)) +
xlab("Age at outcome test (years)") +
ylab("Gain for 1 year of education\n(IQ points)") +
guides(size=FALSE, colour = FALSE, fill = FALSE) +
geom_smooth(method="lm", size=.5, alpha=.25) +
ggtitle("Effect of education as a function of age at the outcome test")+
theme(plot.title = element_text(hjust=0.5)) +
facet_grid(Design~.)+
scale_colour_brewer(palette = "Set1")+
scale_fill_brewer(palette = "Set1")
meta_2_gg
```
## Расширения `ggplot2` {#sec-gg_ext}
`ggplot2` стал очень популярным пакетом и быстро обзавелся расширениями - пакетами R, которые являются надстройками над `ggplot2`. Эти расширения бывают самого разного рода, например, добавляющие дополнительные геомы или просто реализующие отдельные типы графиков на языке `ggplot2`.
Я рекомендую посмотреть самостоятельно галерею расширений `ggplot2`: https://exts.ggplot2.tidyverse.org/gallery/
Для примера мы возьмем пакет `hrbrthemes`, который предоставляет дополнительные темы для `ggplot2`, компоненты тем и шкалы.
```{r, eval = FALSE}
install.packages("hrbrthemes")
```
```{r, eval = FALSE}
library(hrbrthemes)
meta_2_gg +
theme_ipsum()
```