Data visualization: Introduction

Gggplot2 (сейчас в версии 3.5) – популярный пакет R для визуализаций в анализе данных https://cran.r-project.org/web/packages/ggplot2/index.html

Расширения ggplot2: https://exts.ggplot2.tidyverse.org/gallery/

Gggplot2 разработан командой Х. Уикхема на основе новой грамматики послойной графики.

Каждый график обычно состоит из нескольких слоев (layers), которые располагаются один над другим. У каждого слоя есть три обязательных элемента: данные (data), геом (geom), эстетики (aes(tetics))
и два вспомогательных: статистические трансформации (stat) и регулировка положения (position adjustment).

Кроме слоев, у графика есть:

Пример: пайчарт с распределение по полу

library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.4.0     ✔ purrr   1.0.1
## ✔ tibble  3.2.1     ✔ dplyr   1.1.4
## ✔ tidyr   1.3.0     ✔ stringr 1.5.0
## ✔ readr   2.1.3     ✔ forcats 0.5.2
## Warning: package 'dplyr' was built under R version 4.2.3
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
heroes <- read_csv("https://raw.githubusercontent.com/Pozdniakov/tidy_stats/master/data/heroes_information.csv",
                   na = c("-", "-99"))
## New names:
## • `` -> `...1`
## Warning: One or more parsing issues, call `problems()` on your data frame for details,
## e.g.:
##   dat <- vroom(...)
##   problems(dat)
## Rows: 734 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (8): name, Gender, Eye color, Race, Hair color, Publisher, Skin color, A...
## dbl (3): ...1, Height, Weight
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
str(heroes)
## spc_tbl_ [734 × 11] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ ...1      : num [1:734] 0 1 2 3 4 5 6 7 8 9 ...
##  $ name      : chr [1:734] "A-Bomb" "Abe Sapien" "Abin Sur" "Abomination" ...
##  $ Gender    : chr [1:734] "Male" "Male" "Male" "Male" ...
##  $ Eye color : chr [1:734] "yellow" "blue" "blue" "green" ...
##  $ Race      : chr [1:734] "Human" "Icthyo Sapien" "Ungaran" "Human / Radiation" ...
##  $ Hair color: chr [1:734] "No Hair" "No Hair" "No Hair" "No Hair" ...
##  $ Height    : num [1:734] 203 191 185 203 NA 193 NA 185 173 178 ...
##  $ Publisher : chr [1:734] "Marvel Comics" "Dark Horse Comics" "DC Comics" "Marvel Comics" ...
##  $ Skin color: chr [1:734] NA "blue" "red" NA ...
##  $ Alignment : chr [1:734] "good" "good" "good" "bad" ...
##  $ Weight    : num [1:734] 441 65 90 441 NA 122 NA 88 61 81 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   ...1 = col_double(),
##   ..   name = col_character(),
##   ..   Gender = col_character(),
##   ..   `Eye color` = col_character(),
##   ..   Race = col_character(),
##   ..   `Hair color` = col_character(),
##   ..   Height = col_double(),
##   ..   Publisher = col_character(),
##   ..   `Skin color` = col_character(),
##   ..   Alignment = col_character(),
##   ..   Weight = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>

Запустим функцию ggplot(), задав наш тиббл heroes в качестве данных.

ggplot(data = heroes)

Мы ничего не получили! Это естественно, ведь мы задали только данные по умолчанию, но не задали геометрию и эстетики.

Функция ggplot() не просто отрисовывает график, эта функция создает объект класса ggplot, который можно сохранить и модифицировать в дальнейшем:

almost_empty_ggplot <- ggplot(data = heroes)
almost_empty_ggplot

Возьмем geom_bar() для отрисовки барплота. В качестве эстетик поставим x = Gender и fill = Gender. Поскольку это эстетики, они обозначаются внутри функции параметра mapping = aes() или просто внутри функции aes(). По умолчанию, geom_bar() имеет статистику “count”, что нас полностью устраивает: geom_bar() сам посчитает табличку частот и использует значения Gender для обозначения позиций и заливки, а посчитанные частоты будет использовать для задания высоты столбцов.

ggplot(data = heroes) +
  geom_bar(aes(x = Gender, fill = Gender))

Сейчас мы сделаем один хитрый трюк: поставим значение эстетики x = "", чтобы собрать все столбики в один.

ggplot(data = heroes) +
  geom_bar(aes(x = "", fill = Gender))

Получилось что-то не очень симпатичное, но вполне осмысленное: доли столбца обозначают относительную частоту.

Можно настроить общие параметры геома, не зависящие от данных. Это нужно делать вне функции aes(), но внутри функции для геома.

ggplot(data = heroes) +
  geom_bar(aes(x = "", fill = Gender), width = .2)

А теперь внимание! Подумайте, какого действия нам не хватает, чтобы из имеющегося графика получить пайчарт?

ggplot(data = heroes) +
  geom_bar(aes(x = "", fill = Gender)) +
  coord_polar(theta = "y")

# уберите theta = "y", чтобы понять, что она делает

Нам нужно было всего-лишь поменять систему координат с декартовой на полярную (круговую)! Иначе говоря, пайчарт - это барплот в полярной системе координат.

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

Получившийся пайчарт осталось подретушировать, убрав все лишние элементы подложки с помощью самой минималистичной темы theme_void() и добавив название графика:

ggplot(data = heroes) +
  geom_bar(aes(x = "", fill = Gender)) +
  #coord_polar(theta = "y") +
  theme_void() +
  labs(title = "Gender distributions for superheroes")

hapax <- load("/Users/olgaliashevskaia/Documents/Vyshka/DA-for-CL2023/data/HapaxPlato.rdata")
str(hapax)
ggplot(data=hapax, aes(x=hapax, y=words)) +
   geom_point()

Пример №1: Education and IQ meta-analysis

Для этого примера мы возьмем мета-анализ связи количества лет обучения и интеллекта: “How Much Does Education Improve Intelligence? A Meta-Analysis” [@eduiq]. Мета-анализ — это группа статистических методов, которые позволяют объединить результаты нескольких исследований с похожим планом исследованием и тематикой, чтобы посчитать средний эффект между несколькими статьями сразу.

Данные и скрипт для анализа данных в этой статье находятся в открытом доступе: https://osf.io/r8a24/

Полный текст статьи доступен по ссылке.

Существует положительная корреляция между количеством лет, который человек потратил на обучение, и интеллектом. Это может объясняться по-разному: как то, что обучение повышает интеллект, и как то, что люди с высоким интеллекте стремятся получать больше образования. Напрямую в эксперименте это проверить нельзя, поэтому есть несколько квази-экспериментальных планов, которые косвенно указывают на верность той или иной гипотезу. Например, если в стране изменилось количество лет обязательного школьного образования, то повлияло ли это на интеллект целого поколения? Или все-таки дело в Моргенштерне

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

Каждая точка на этом графике — это отдельное исследование, положение по оси x — средний возраст респондентов, а положение по оси y - средний прирост интеллекта согласно исследованию. Размер точки отражает “точность” исследования (грубо говоря, чем больше выборка, тем больше точка). Два графика обозначают два квазиэкспериментальных плана.

Мы сфокусируемся на нижней картинке с “Policy change” — это как раз исследования, в которых изучается изменения интеллекта в возрастных группах после изменения количества лет обучения в школе.

Мы полностью воспроизведем код

library(tidyverse)

Заметьте, данный датасет использует формат хранения данных tsv. Попытайтесь самостоятельно прочитать его.

df <- read_tsv("https://raw.githubusercontent.com/Pozdniakov/tidy_stats/master/data/meta_dataset.txt")
## Rows: 142 Columns: 17
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: "\t"
## chr  (2): Country, Design
## dbl (15): Study_ID, Data_ID, k, n, outcome_test_cat, Effect_size, SE, Outcom...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Давайте посмотрим, как устроен датафрейм df:

df
## # A tibble: 142 × 17
##    Study_ID Data_ID     k Country     n Design      outcome_test_cat Effect_size
##       <dbl>   <dbl> <dbl> <chr>   <dbl> <chr>                  <dbl>       <dbl>
##  1      1         1     4 UK        483 Control Pr…                0     0.778  
##  2      1         1     4 UK        290 Control Pr…                1     0.0771 
##  3      1         1     4 UK        290 Control Pr…                1     0.427  
##  4      1         1     4 UK        288 Control Pr…                1     0.00519
##  5      1         2    13 UK       1017 Control Pr…                0     1.62   
##  6      1         2    13 UK       1022 Control Pr…                1     0.915  
##  7      1         2    13 UK       1021 Control Pr…                1     0.305  
##  8      1         2    13 UK        981 Control Pr…                1     0.719  
##  9      1.5       2    13 UK       1024 Control Pr…                1     1.70   
## 10      1.5       2    13 UK       1023 Control Pr…                1     1.78   
## # ℹ 132 more rows
## # ℹ 9 more variables: SE <dbl>, Outcome_age <dbl>, quasi_age <dbl>,
## #   cpiq_early_age <dbl>, cpiq_age_diff <dbl>, ses_funnel <dbl>,
## #   published <dbl>, Male_only <dbl>, Achievement <dbl>

Каждая строчка — это результат отдельного исследования, при этом одна статья может включать несколько исследований,

В дальнейшем мы будем использовать код авторов статьи и смотреть, строчка за строчкой, как он будет работать.

cpiq <- subset(df, subset=(Design=="Control Prior IQ"))
poli <- subset(df, subset=(Design=="Policy Change"))

Авторы исследования используют subset(), это функция базового R, принцип которой очень похож на filter() 1.

Итак, начнем рисовать сам график. Сначала иницируем объект ggplot с данными poli по умолчанию.

poli |> 
  ggplot() 

Теперь добавим в качестве эстетик по умолчанию координаты: aes(x=Outcome_age, y=Effect_size).

cpiq <- subset(df, subset=(Design=="Control Prior IQ"))
poli <- subset(df, subset=(Design=="Policy Change"))

poli |>
  ggplot(aes(x=Outcome_age, y=Effect_size)) 

Что изменилось? Появилась координатная ось и шкалы. Заметьте, масштаб неслучаен: он строится на основе разброса значений в выбранных колонках. Однако этого недостаточно для отрисовки графика, нехватает геометрии: нужно задать, в какую географическую сущность отобразятся данные.

ggplot(aes(x=Outcome_age, y=Effect_size), data=poli) +
        geom_point() 

Готово! Это и есть основа картинки. Добавляем размер:

poli
## # A tibble: 30 × 17
##    Study_ID Data_ID     k Country      n Design     outcome_test_cat Effect_size
##       <dbl>   <dbl> <dbl> <chr>    <dbl> <chr>                 <dbl>       <dbl>
##  1        5       8     1 Sweden  142221 Policy Ch…                0        1.31
##  2        6       9     1 Norway  107223 Policy Ch…                0        3.69
##  3        7      10     2 USA      11427 Policy Ch…                1        4.95
##  4        7      10     2 USA      11427 Policy Ch…                1        2.85
##  5        9      12     4 UK        2385 Policy Ch…                1        5.1 
##  6        9      12     4 UK        2057 Policy Ch…                1        4.65
##  7        9      12     4 UK        1963 Policy Ch…                1        0.15
##  8        9      12     4 UK        1701 Policy Ch…                1        0.75
##  9       10      13     2 UK        1860 Policy Ch…                1        2.61
## 10       10      13     2 UK        1860 Policy Ch…                1        3.54
## # ℹ 20 more rows
## # ℹ 9 more variables: SE <dbl>, Outcome_age <dbl>, quasi_age <dbl>,
## #   cpiq_early_age <dbl>, cpiq_age_diff <dbl>, ses_funnel <dbl>,
## #   published <dbl>, Male_only <dbl>, Achievement <dbl>
ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
        geom_point() 

Перед нами возникла проблема оверплоттинга: некоторые точки перекрывают друг друга, поскольку имеют очень близкие координат. Авторы графика решают эту проблему очевидным способом: добавляют прозрачности точкам. Заметьте, прозрачность задается для всех точек одним значением, поэтому параметр alpha задается вне функции aes().

ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
        geom_point(alpha=.55) 

Совершенно так же задается и цвет. Он задается одинаковым для всех точек с помощью HEX-кода.

ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
        geom_point(alpha=.55, colour="#BA1825")

Теперь добавим регрессионную прямую с доверительными интервалами на график. Это специальный геом geom_smooth() со специальной статистикой, который займет второй слой данного графика.

ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
        geom_point(alpha=.55, colour="#BA1825") +
        geom_smooth()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'
## Warning: The following aesthetics were dropped during statistical transformation: size
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?

По умолчанию geom_smooth() строит кривую линию. Поставим method = "lm" для прямой.

ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
        geom_point(alpha=.55, colour="#BA1825") +
        geom_smooth(method="lm")
## `geom_smooth()` using formula = 'y ~ x'
## Warning: The following aesthetics were dropped during statistical transformation: size
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?

Теперь нужно поменять цвет: ярко синий цвет, используемый по умолчанию здесь попросту мешает восприятию графика.

ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
        geom_point(alpha=.5, colour="darkgreen") +
        geom_smooth(method="lm", colour="darkgreen")
## `geom_smooth()` using formula = 'y ~ x'
## Warning: The following aesthetics were dropped during statistical transformation: size
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?

Авторы графика перекрашивают серую полупрозначную область тоже. В этом случае используется параметр fill =, а не colour =, но цвет используется тот же.

ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
        geom_point(colour="darkgreen", alpha=.55) +
        geom_smooth(method="lm", colour="darkgreen", fill="darkgreen")
## `geom_smooth()` using formula = 'y ~ x'
## Warning: The following aesthetics were dropped during statistical transformation: size
## ℹ This can happen when ggplot fails to infer the correct grouping structure in
##   the data.
## ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
##   variable into a factor?

Регрессионную линию авторы немного утоньшают с помощью параметра size =.

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()` using formula = 'y ~ x'

Чтобы сместить фокус в сторону точек, авторы добавляют прозрачности для всего geom_smooth().

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)
## `geom_smooth()` using formula = 'y ~ x'

На шкале присутствует 0, и по умолчанию он никак не обозначен. Это легко исправить с помощью вспомогательного геома geom_hline().

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)
## `geom_smooth()` using formula = 'y ~ x'

Оттенить эту линию можно, сделав ее пунктирной.

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)
## `geom_smooth()` using formula = 'y ~ x'

Авторы графика вручную задают деления шкалы по оси x.

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)
## `geom_smooth()` using formula = 'y ~ x'

С помощью функции guides() убирают легенду с картинки.

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)
## Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
## of ggplot2 3.3.4.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `geom_smooth()` using formula = 'y ~ x'

Следующим этапом авторы добавляют подписи шкал и название картинки. Обратите внимание на \n внутри подписи к оси y, которая задает перенос на следующую строку.

ggplot(aes(x=Outcome_age, y=Effect_size, size=1/(SE^2)), data=poli) +
        geom_point(alpha=.55, colour="darkgreen") +
        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="darkgreen",fill="darkgreen",size=.5, alpha=.25) + 
    ggtitle("Policy Change")
## `geom_smooth()` using formula = 'y ~ x'

Теперь пришло время сделать график более красивым и понятным с помощью изменения подложки, т.е. работы с темой графика. Здесь тема задается сначала как theme_bw() — встроенная в ggplot2 минималистичная тема, а потом через функцию theme(), через которую можно управлять конкретными элементами темы. Здесь это сделано, чтобы передвинуть название графика к центру.

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), limits = c(0, 90)) +
        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))
## `geom_smooth()` using formula = 'y ~ x'

Готово! Мы полностью воспроизвели график авторов статьи с помощью их открытого кода.

Если вы помните, то в изначальном графике было две картинки. Авторы делают их отдельно, с помощью почти идентичного кода. Нечто похожее можно сделать по-другому, применяя фасетки.

Для этого мы возьмем неотфильтрованный датасет df, а с помощью колонки Design, на основании которой разделялся датасет для графиков, произведем разделение графиков внутри самого ggplot объекта. Для этого нам понадобится функция facet_wrap(), в которой с помощью формулы можно задать колонки, по которым будут разделены картинки по вертикали (слева от ~) и горизонтально (справа от ~). Пробуем разделить графики горизонтально:

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)
## `geom_smooth()` using formula = 'y ~ x'

Здесь становится очевидно, почему авторы не включали данные "School Age Cutoff" третьим графиком: средний возраст участников этих исследований сильно отличается.

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)
## `geom_smooth()` using formula = 'y ~ x'

facet_grid

Теперь поставим два графика друг над другом, поместив Design слева от ~ внутри facet_wrap(). Справа нужно добавить точку.

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~.)
## `geom_smooth()` using formula = 'y ~ x'

Теперь нужно изменить подписи.

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~.)
## `geom_smooth()` using formula = 'y ~ x'

Чтобы акцентировать графики, можно раскрасить их в разные цвета в дополнение к фасеткам. Для этого мы переносим colour = и fill = из параметров соответствующих геомов внутрь эстетик и делаем зависимыми от Design. Поскольку эти эстетики (точнее, colour =) одинаковы заданы для двух геомов (geom_point() и geom_smooth()), то мы спокойно можем вынести их в эстетики по умолчанию — в параметры aes() внутри ggplot().

При этом сразу выключим легенды для новых эстетик, потому они избыточны.

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~.)
## `geom_smooth()` using formula = 'y ~ x'

Слишком блеклая палитра? Не беда, можно задать палитру вручную! В ggplot2 встроены легендарные Brewer’s Color Palettes, которыми мы и воспользуемся.

Функции для шкал устроены интересным образом: они состоят из трех слов, первое из которых scale_*_*(), второе — эстетика, например, scale_color_*(), а последнее слово — тип самой шкалы, в некоторых случаях - специальное название для используемой шкалы, как и в случае с scale_color_brewer().

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
## `geom_smooth()` using formula = 'y ~ x'

Расширения ggplot2

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

Я рекомендую посмотреть самостоятельно галерею расширений ggplot2: https://exts.ggplot2.tidyverse.org/gallery/

Для примера мы возьмем пакет hrbrthemes, который предоставляет дополнительные темы для ggplot2, компоненты тем и шкалы.

#install.packages("hrbrthemes")
library(hrbrthemes)
meta_2_gg +
  theme_ipsum()
## `geom_smooth()` using formula = 'y ~ x'

Сохранение данных

Функция ggsave() (аналог в базовом R - save())

plot <- ggplot(data=iris, aes(x=Sepal.Length, y=Sepal.Width)) + 
  geom_point(aes(shape=Species, color=Species))


# setwd("/Users/olgaliashevskaia/Desktop")

ggsave("plot1.png")
## Saving 7 x 5 in image
ggsave(plot, file="plot2.png")
## Saving 7 x 5 in image
ggsave(plot, file="plot3.png", width=6, height=4)

Excersizes

Пример №2: Vowel duration and intonation in Cantonese

Датасет homo: исследование связи между акустическим сигналом и восприятием гендерной ориентации говорящих (кантонский диалект китайского языка). В экспериментах использовался перцептивный тест и сбор суждений относительно 14 говорящих.

  • [s] duration (s.duration.ms)
  • vowel duration (vowel.duration.ms)
  • fundamental frequencies mean (F0) (average.f0.Hz)
  • fundamental frequencies range (f0.range.Hz)
  • percentage of homosexual impression (perceived.as.homo)
  • percentage of heterosexal impression (perceived.as.hetero)
  • speakers orientation (orientation)
  • speakers age (age)
homo <- read_csv("https://raw.githubusercontent.com/LingData2019/LingData2020/master/data/orientation.csv")
## Rows: 14 Columns: 10
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (2): speaker, orientation
## dbl (8): s.duration.ms, vowel.duration.ms, average.f0.Hz, f0.range.Hz, perce...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(homo)
## # A tibble: 6 × 10
##   speaker s.duration.ms vowel.duration.ms average.f0.Hz f0.range.Hz
##   <chr>           <dbl>             <dbl>         <dbl>       <dbl>
## 1 A                61.4             113.           120.        52.5
## 2 B                63.9             126.           100.       114  
## 3 C                55.1             127.           115.       103. 
## 4 D                78.1             119.           127.        58.8
## 5 E                64.7              93.7          131.        37.4
## 6 F                67               128.           151.        42  
## # ℹ 5 more variables: perceived.as.homo <dbl>, perceived.as.hetero <dbl>,
## #   perceived.as.homo.percent <dbl>, orientation <chr>, age <dbl>

Scatterplot

s.duration.ms vs. vowel.duration.ms

homo |>
   ggplot(aes(s.duration.ms, vowel.duration.ms)) +
   geom_point()

Scatterplot: color

Добавьте цвет по столбцу color = orientation

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms,
             color = orientation)) +
  geom_point()

Scatterplot: shape

Вместо цвета shape

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms,
             shape = orientation)) +
  geom_point(color = "darkred")

Scatterplot: size

Используйте параметр size в эстетике, чтобы отобразить возраст:

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms,
             size = age)) +
  geom_point()

Измените цвет точек и добавьте метки:

Scatterplot: текст

homo |>
  mutate(label = ifelse(orientation == "homo","⚣", "⚤")) |> 
  ggplot(aes(s.duration.ms, vowel.duration.ms, label = label, fill = orientation)) +
  geom_label()

Используйте цвет (color) вместо заполнения (fill):

homo |>
  mutate(label = ifelse(orientation == "homo","⚣", "⚤")) |> 
  ggplot(aes(s.duration.ms, vowel.duration.ms, label = label, color = orientation)) +
  geom_text()

Title, subtitle, caption

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms)) +
  geom_point()+
  labs(title = "Length of [s] vs. length of vowels",
       subtitle = "based on 14 speakers of Cantonese",
       caption = "data from [Hau 2007]")

Axis labels

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms)) +
  geom_point()+
  xlab("duration of [s] in ms")+
  ylab("vowel duration in ms")

Theme

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms)) +
  geom_point()+
  xlab("duration of [s] in ms") +
  ylab("vowel duration in ms") +
  theme_minimal()

(Log) scale

freq <- read_csv("https://raw.githubusercontent.com/LingData2019/LingData/master/data/freqrnc2011_1000.csv")
## Rows: 1000 Columns: 3
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): lemma
## dbl (2): freq_ipm, rank
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
freq |>
  ggplot(aes(rank, freq_ipm)) +
  geom_point(alpha = .3) +
  labs(x = "rank", y = "ipm") +
  theme_minimal()

Use scale_y_log10() to transform the y axis:

freq |>
  ggplot(aes(1:1000, freq_ipm)) +
  geom_point(alpha = .3) +
  geom_smooth(method=lm)+
  xlab("rank") +
  ylab("log(ipm)") +
  scale_y_log10() +
  scale_x_log10() +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

rugs

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms, color = orientation)) +
  geom_point() +
  geom_rug() +
  theme_minimal()

lines

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms)) +
  geom_point() +
  geom_hline(yintercept = mean(homo$vowel.duration.ms))+
  geom_vline(xintercept = 60) +
  theme_minimal()

Change line types and color:

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms)) +
  geom_point() +
  geom_hline(yintercept = 120, linetype = 4) +
  geom_vline(xintercept = 60, color = "blue") +
  theme_minimal()

Annotate!

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms)) +
  geom_point()+
  annotate(geom = "rect", xmin = 77, xmax = 79,
           ymin = 117, ymax = 122, fill = "red", alpha = 0.2) + 
  annotate(geom = "text", x = 75, y = 125,
           label = "Who is that?\n Outlier?") +
  theme_minimal()

Ablines

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms)) +
  geom_point() +
  geom_hline(yintercept = 120, linetype = 4) +
  geom_vline(xintercept = 60, color = "blue") +
  geom_smooth(method = "lm") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

Try geom_smooth() without arguments now!

Facets

facet_wrap – cf. group_by in dplyr

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms)) +
  geom_point() +
#  geom_hline(yintercept = 120, linetype = 4) +
#  geom_vline(xintercept = 60, color = "blue") +
#  geom_smooth(method = "lm") +
  facet_wrap(orientation~.)

  theme_minimal()
## List of 94
##  $ line                      :List of 6
##   ..$ colour       : chr "black"
##   ..$ linewidth    : num 0.5
##   ..$ linetype     : num 1
##   ..$ lineend      : chr "butt"
##   ..$ arrow        : logi FALSE
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_line" "element"
##  $ rect                      :List of 5
##   ..$ fill         : chr "white"
##   ..$ colour       : chr "black"
##   ..$ linewidth    : num 0.5
##   ..$ linetype     : num 1
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_rect" "element"
##  $ text                      :List of 11
##   ..$ family       : chr ""
##   ..$ face         : chr "plain"
##   ..$ colour       : chr "black"
##   ..$ size         : num 11
##   ..$ hjust        : num 0.5
##   ..$ vjust        : num 0.5
##   ..$ angle        : num 0
##   ..$ lineheight   : num 0.9
##   ..$ margin       : 'margin' num [1:4] 0points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : logi FALSE
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ title                     : NULL
##  $ aspect.ratio              : NULL
##  $ axis.title                : NULL
##  $ axis.title.x              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 2.75points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.title.x.top          :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 0
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 2.75points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.title.x.bottom       : NULL
##  $ axis.title.y              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 1
##   ..$ angle        : num 90
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 2.75points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.title.y.left         : NULL
##  $ axis.title.y.right        :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 0
##   ..$ angle        : num -90
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 0points 2.75points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text                 :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : chr "grey30"
##   ..$ size         : 'rel' num 0.8
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.x               :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 2.2points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.x.top           :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 0
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 2.2points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.x.bottom        : NULL
##  $ axis.text.y               :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 1
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 2.2points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.y.left          : NULL
##  $ axis.text.y.right         :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 0
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 0points 2.2points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.ticks                : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ axis.ticks.x              : NULL
##  $ axis.ticks.x.top          : NULL
##  $ axis.ticks.x.bottom       : NULL
##  $ axis.ticks.y              : NULL
##  $ axis.ticks.y.left         : NULL
##  $ axis.ticks.y.right        : NULL
##  $ axis.ticks.length         : 'simpleUnit' num 2.75points
##   ..- attr(*, "unit")= int 8
##  $ axis.ticks.length.x       : NULL
##  $ axis.ticks.length.x.top   : NULL
##  $ axis.ticks.length.x.bottom: NULL
##  $ axis.ticks.length.y       : NULL
##  $ axis.ticks.length.y.left  : NULL
##  $ axis.ticks.length.y.right : NULL
##  $ axis.line                 : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ axis.line.x               : NULL
##  $ axis.line.x.top           : NULL
##  $ axis.line.x.bottom        : NULL
##  $ axis.line.y               : NULL
##  $ axis.line.y.left          : NULL
##  $ axis.line.y.right         : NULL
##  $ legend.background         : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ legend.margin             : 'margin' num [1:4] 5.5points 5.5points 5.5points 5.5points
##   ..- attr(*, "unit")= int 8
##  $ legend.spacing            : 'simpleUnit' num 11points
##   ..- attr(*, "unit")= int 8
##  $ legend.spacing.x          : NULL
##  $ legend.spacing.y          : NULL
##  $ legend.key                : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ legend.key.size           : 'simpleUnit' num 1.2lines
##   ..- attr(*, "unit")= int 3
##  $ legend.key.height         : NULL
##  $ legend.key.width          : NULL
##  $ legend.text               :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : 'rel' num 0.8
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ legend.text.align         : NULL
##  $ legend.title              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 0
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ legend.title.align        : NULL
##  $ legend.position           : chr "right"
##  $ legend.direction          : NULL
##  $ legend.justification      : chr "center"
##  $ legend.box                : NULL
##  $ legend.box.just           : NULL
##  $ legend.box.margin         : 'margin' num [1:4] 0cm 0cm 0cm 0cm
##   ..- attr(*, "unit")= int 1
##  $ legend.box.background     : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ legend.box.spacing        : 'simpleUnit' num 11points
##   ..- attr(*, "unit")= int 8
##  $ panel.background          : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ panel.border              : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ panel.spacing             : 'simpleUnit' num 5.5points
##   ..- attr(*, "unit")= int 8
##  $ panel.spacing.x           : NULL
##  $ panel.spacing.y           : NULL
##  $ panel.grid                :List of 6
##   ..$ colour       : chr "grey92"
##   ..$ linewidth    : NULL
##   ..$ linetype     : NULL
##   ..$ lineend      : NULL
##   ..$ arrow        : logi FALSE
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_line" "element"
##  $ panel.grid.major          : NULL
##  $ panel.grid.minor          :List of 6
##   ..$ colour       : NULL
##   ..$ linewidth    : 'rel' num 0.5
##   ..$ linetype     : NULL
##   ..$ lineend      : NULL
##   ..$ arrow        : logi FALSE
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_line" "element"
##  $ panel.grid.major.x        : NULL
##  $ panel.grid.major.y        : NULL
##  $ panel.grid.minor.x        : NULL
##  $ panel.grid.minor.y        : NULL
##  $ panel.ontop               : logi FALSE
##  $ plot.background           : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ plot.title                :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : 'rel' num 1.2
##   ..$ hjust        : num 0
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 5.5points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ plot.title.position       : chr "panel"
##  $ plot.subtitle             :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 0
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 5.5points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ plot.caption              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : 'rel' num 0.8
##   ..$ hjust        : num 1
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 5.5points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ plot.caption.position     : chr "panel"
##  $ plot.tag                  :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : 'rel' num 1.2
##   ..$ hjust        : num 0.5
##   ..$ vjust        : num 0.5
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ plot.tag.position         : chr "topleft"
##  $ plot.margin               : 'margin' num [1:4] 5.5points 5.5points 5.5points 5.5points
##   ..- attr(*, "unit")= int 8
##  $ strip.background          : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ strip.background.x        : NULL
##  $ strip.background.y        : NULL
##  $ strip.clip                : chr "inherit"
##  $ strip.placement           : chr "inside"
##  $ strip.text                :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : chr "grey10"
##   ..$ size         : 'rel' num 0.8
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 4.4points 4.4points 4.4points 4.4points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ strip.text.x              : NULL
##  $ strip.text.y              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : num -90
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ strip.switch.pad.grid     : 'simpleUnit' num 2.75points
##   ..- attr(*, "unit")= int 8
##  $ strip.switch.pad.wrap     : 'simpleUnit' num 2.75points
##   ..- attr(*, "unit")= int 8
##  $ strip.text.y.left         :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : num 90
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  - attr(*, "class")= chr [1:2] "theme" "gg"
##  - attr(*, "complete")= logi TRUE
##  - attr(*, "validate")= logi TRUE

Another option: facet_grid

homo |>
  ggplot(aes(s.duration.ms, vowel.duration.ms, colour = orientation, fill = orientation)) +
  geom_point() +
#  geom_hline(yintercept = 120, linetype = 4) +
#  geom_vline(xintercept = 60, color = "blue") +
#  geom_smooth(method = "lm") +
  facet_grid(orientation~.)

  theme_minimal()
## List of 94
##  $ line                      :List of 6
##   ..$ colour       : chr "black"
##   ..$ linewidth    : num 0.5
##   ..$ linetype     : num 1
##   ..$ lineend      : chr "butt"
##   ..$ arrow        : logi FALSE
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_line" "element"
##  $ rect                      :List of 5
##   ..$ fill         : chr "white"
##   ..$ colour       : chr "black"
##   ..$ linewidth    : num 0.5
##   ..$ linetype     : num 1
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_rect" "element"
##  $ text                      :List of 11
##   ..$ family       : chr ""
##   ..$ face         : chr "plain"
##   ..$ colour       : chr "black"
##   ..$ size         : num 11
##   ..$ hjust        : num 0.5
##   ..$ vjust        : num 0.5
##   ..$ angle        : num 0
##   ..$ lineheight   : num 0.9
##   ..$ margin       : 'margin' num [1:4] 0points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : logi FALSE
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ title                     : NULL
##  $ aspect.ratio              : NULL
##  $ axis.title                : NULL
##  $ axis.title.x              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 2.75points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.title.x.top          :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 0
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 2.75points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.title.x.bottom       : NULL
##  $ axis.title.y              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 1
##   ..$ angle        : num 90
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 2.75points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.title.y.left         : NULL
##  $ axis.title.y.right        :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 0
##   ..$ angle        : num -90
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 0points 2.75points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text                 :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : chr "grey30"
##   ..$ size         : 'rel' num 0.8
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.x               :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 2.2points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.x.top           :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : num 0
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 2.2points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.x.bottom        : NULL
##  $ axis.text.y               :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 1
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 2.2points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.text.y.left          : NULL
##  $ axis.text.y.right         :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 0
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 0points 2.2points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ axis.ticks                : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ axis.ticks.x              : NULL
##  $ axis.ticks.x.top          : NULL
##  $ axis.ticks.x.bottom       : NULL
##  $ axis.ticks.y              : NULL
##  $ axis.ticks.y.left         : NULL
##  $ axis.ticks.y.right        : NULL
##  $ axis.ticks.length         : 'simpleUnit' num 2.75points
##   ..- attr(*, "unit")= int 8
##  $ axis.ticks.length.x       : NULL
##  $ axis.ticks.length.x.top   : NULL
##  $ axis.ticks.length.x.bottom: NULL
##  $ axis.ticks.length.y       : NULL
##  $ axis.ticks.length.y.left  : NULL
##  $ axis.ticks.length.y.right : NULL
##  $ axis.line                 : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ axis.line.x               : NULL
##  $ axis.line.x.top           : NULL
##  $ axis.line.x.bottom        : NULL
##  $ axis.line.y               : NULL
##  $ axis.line.y.left          : NULL
##  $ axis.line.y.right         : NULL
##  $ legend.background         : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ legend.margin             : 'margin' num [1:4] 5.5points 5.5points 5.5points 5.5points
##   ..- attr(*, "unit")= int 8
##  $ legend.spacing            : 'simpleUnit' num 11points
##   ..- attr(*, "unit")= int 8
##  $ legend.spacing.x          : NULL
##  $ legend.spacing.y          : NULL
##  $ legend.key                : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ legend.key.size           : 'simpleUnit' num 1.2lines
##   ..- attr(*, "unit")= int 3
##  $ legend.key.height         : NULL
##  $ legend.key.width          : NULL
##  $ legend.text               :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : 'rel' num 0.8
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ legend.text.align         : NULL
##  $ legend.title              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 0
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ legend.title.align        : NULL
##  $ legend.position           : chr "right"
##  $ legend.direction          : NULL
##  $ legend.justification      : chr "center"
##  $ legend.box                : NULL
##  $ legend.box.just           : NULL
##  $ legend.box.margin         : 'margin' num [1:4] 0cm 0cm 0cm 0cm
##   ..- attr(*, "unit")= int 1
##  $ legend.box.background     : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ legend.box.spacing        : 'simpleUnit' num 11points
##   ..- attr(*, "unit")= int 8
##  $ panel.background          : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ panel.border              : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ panel.spacing             : 'simpleUnit' num 5.5points
##   ..- attr(*, "unit")= int 8
##  $ panel.spacing.x           : NULL
##  $ panel.spacing.y           : NULL
##  $ panel.grid                :List of 6
##   ..$ colour       : chr "grey92"
##   ..$ linewidth    : NULL
##   ..$ linetype     : NULL
##   ..$ lineend      : NULL
##   ..$ arrow        : logi FALSE
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_line" "element"
##  $ panel.grid.major          : NULL
##  $ panel.grid.minor          :List of 6
##   ..$ colour       : NULL
##   ..$ linewidth    : 'rel' num 0.5
##   ..$ linetype     : NULL
##   ..$ lineend      : NULL
##   ..$ arrow        : logi FALSE
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_line" "element"
##  $ panel.grid.major.x        : NULL
##  $ panel.grid.major.y        : NULL
##  $ panel.grid.minor.x        : NULL
##  $ panel.grid.minor.y        : NULL
##  $ panel.ontop               : logi FALSE
##  $ plot.background           : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ plot.title                :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : 'rel' num 1.2
##   ..$ hjust        : num 0
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 5.5points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ plot.title.position       : chr "panel"
##  $ plot.subtitle             :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : num 0
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 0points 0points 5.5points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ plot.caption              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : 'rel' num 0.8
##   ..$ hjust        : num 1
##   ..$ vjust        : num 1
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 5.5points 0points 0points 0points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ plot.caption.position     : chr "panel"
##  $ plot.tag                  :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : 'rel' num 1.2
##   ..$ hjust        : num 0.5
##   ..$ vjust        : num 0.5
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ plot.tag.position         : chr "topleft"
##  $ plot.margin               : 'margin' num [1:4] 5.5points 5.5points 5.5points 5.5points
##   ..- attr(*, "unit")= int 8
##  $ strip.background          : list()
##   ..- attr(*, "class")= chr [1:2] "element_blank" "element"
##  $ strip.background.x        : NULL
##  $ strip.background.y        : NULL
##  $ strip.clip                : chr "inherit"
##  $ strip.placement           : chr "inside"
##  $ strip.text                :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : chr "grey10"
##   ..$ size         : 'rel' num 0.8
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : NULL
##   ..$ lineheight   : NULL
##   ..$ margin       : 'margin' num [1:4] 4.4points 4.4points 4.4points 4.4points
##   .. ..- attr(*, "unit")= int 8
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ strip.text.x              : NULL
##  $ strip.text.y              :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : num -90
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  $ strip.switch.pad.grid     : 'simpleUnit' num 2.75points
##   ..- attr(*, "unit")= int 8
##  $ strip.switch.pad.wrap     : 'simpleUnit' num 2.75points
##   ..- attr(*, "unit")= int 8
##  $ strip.text.y.left         :List of 11
##   ..$ family       : NULL
##   ..$ face         : NULL
##   ..$ colour       : NULL
##   ..$ size         : NULL
##   ..$ hjust        : NULL
##   ..$ vjust        : NULL
##   ..$ angle        : num 90
##   ..$ lineheight   : NULL
##   ..$ margin       : NULL
##   ..$ debug        : NULL
##   ..$ inherit.blank: logi TRUE
##   ..- attr(*, "class")= chr [1:2] "element_text" "element"
##  - attr(*, "class")= chr [1:2] "theme" "gg"
##  - attr(*, "complete")= logi TRUE
##  - attr(*, "validate")= logi TRUE

Note that color and fill depend on orientation and are put within the main aes

Categorical data

Barplots

homo |>
  ggplot(aes(orientation)) +
  geom_bar()

Make barplots of age for each speaker:

homo |>
  ggplot(aes(speaker, age)) +
  geom_col()

Fill bars by orientation:

homo |>
  ggplot(aes(speaker, age, fill = orientation)) +
  geom_col()

Aggregated data

The count statistics is use by default here.

homo |>
  ggplot() +
  geom_bar(aes(age, fill = orientation))

Plot all data in one bar:

homo |>
  ggplot() +
  geom_bar(aes(x="", fill = orientation), width = 0.2)

homo |>
  mutate(age = as.factor(age)) |>
  ggplot() +
  geom_bar(aes(x="", fill = age), width = 0.2)

NB the width argument of the barplot.

Piechart

homo |>
  mutate(age = as.factor(age)) |>
  ggplot() +
  geom_bar(aes(x="", fill = age)) +
  coord_polar(theta = "y") +
  theme_void()

Boxplots

theme_set(theme_bw()) # set black-and-white theme
homo |>
  ggplot(aes(orientation, s.duration.ms)) +
  geom_boxplot()

Boxplots: add points

homo |>
  ggplot(aes(orientation, s.duration.ms)) +
  geom_boxplot()+
  geom_point()

Jitter

homo |>
  ggplot(aes(orientation, s.duration.ms)) +
  geom_violin() +
  geom_jitter(width = 0.1)

Density plot

homo |>
  ggplot(aes(x=s.duration.ms, fill=orientation)) +
  geom_density(alpha=0.7, position="stack")+
#  geom_vline()+
#  scale_color_manual(values=c("#999999", "#000000"))+
#   scale_color_grey()+
    theme_classic()

Другое решение

homo |>
  ggplot(aes(x=s.duration.ms, color=orientation)) +
  geom_density() + 
  geom_vline(xintercept = mean(homo$s.duration.ms[homo$orientation=="hetero"]))+
  geom_vline(xintercept = mean(homo$s.duration.ms[homo$orientation=="homo"]))

Save as file

The plot can be stored as a variable. One can recycle it many times with different options. In order to create a pdf file, put the file name

pdf("plot.pdf")
homo |>
  ggplot(aes(orientation, s.duration.ms)) +
  geom_boxplot() +
  geom_jitter(width = 0.2)
dev.off()
## quartz_off_screen 
##                 2

References:


  1. Кстати, именно функция subset() вдохновила Уикхема на создание filter().↩︎