7  Tworzenie kompozycji wizualnych

Kompozycja wizualna to sposób prezentacji danych, który ma na celu ułatwienie zrozumienia informacji. Obejmuje to odpowiedni dobór elementów do wizualizacji oraz ich układu. Mówiąc szerzej można taż rozważać wizualizacje danych jako nie tylko wykresy, ale także tabele, mapy, diagramy, a także ich połączenia. Kompozycja wizualna to umiejętność łączenia różnych elementów wizualizacji w spójną całość.

W tej części zajmiemy się tworzeniem kompozycji wizualnych w języku R z wykorzystaniem pakietu ggplot2.

Kod
library(ggplot2)

7.1 Układ wykresu

Styl wizualny wykresu jest definiowany przez zestaw parametrów, które można modyfikować. W ggplot2 można to zrobić za pomocą funkcji theme(), która pozwala na dostosowanie wyglądu wykresu.

Kod
?theme

Modyfikowanie stylu wizualnego wykresu opiera się na określeniu wybranych argumentów funkcji theme(). Większość z tych argumentów oczekuje jednej z poniższych funkcji:

  • element_line(): kwestie związane z liniami, takie jak kolor, grubość, typ linii
  • element_text(): kwestie związane z tekstem, takie jak kolor, rozmiar, rodzinę czy styl fontu
  • element_rect(): kwestie związane z prostokątami, takie jak kolor wypełnienia, kolor obramowania, grubość linii
  • element_blank(): ustala brak elementu

Niektóre argumenty mogą natomiast oczekiwać wartości numerycznych, tekstowych, czy wartośći podanych jednostek.

7.1.1 Położenie legendy

Domyślnie legenda jest umieszczana w miejscu, które jest uznawane za najbardziej odpowiednie przez ggplot2.

Kod
df0sel
# A tibble: 190 × 8
   Kraj     Rok Region Populacja PKB_na_osobe Ocz_dl_zycia CO2_na_osobe Plodnosc
   <chr>  <dbl> <chr>      <dbl>        <dbl>        <dbl>        <dbl>    <dbl>
 1 Austr…  2019 Azja      2.54e7        49400         82.9           NA     1.82
 2 Brunei  2019 Azja      4.38e5        61400         74.4           NA     1.84
 3 Kambo…  2019 Azja      1.62e7         4460         69.9           NA     2.47
 4 Chiny   2019 Azja      1.42e9        16000         77.6           NA     1.64
 5 Fidżi   2019 Azja      9.18e5        13200         68.4           NA     2.45
 6 Hongk…  2019 Azja      7.5 e6        59600         84.5           NA     1.36
 7 Indon…  2019 Azja      2.70e8        11900         71.4           NA     2.29
 8 Japon…  2019 Azja      1.26e8        41700         84.8           NA     1.5 
 9 Kirib…  2019 Azja      1.24e5         1960         60.8           NA     3.53
10 Korea…  2019 Azja      2.58e7         1990         73.2           NA     1.88
# ℹ 180 more rows
Kod
gg1 = ggplot(data = df0sel, aes(x = Ocz_dl_zycia, y = Plodnosc,
                                color = PKB_na_osobe)) + 
    geom_point(size = 4)
gg1

Możemy jednak zmienić jej położenie za pomocą argumentu legend.position, jej ułożenie za pomocą legend.direction, a także szerokość za pomocą legend.key.width.

Kod
gg1 + theme(legend.position = c(0.8, 0.9),
            legend.direction = "horizontal",
            legend.key.width = unit(1.25, "cm"))

Legendy można umiejscawaić także na zewnątrz wykresu.

Kod
gg1 + theme(legend.position = "top",
            legend.key.width = unit(3, "cm"))

7.1.2 Siatka

Styl wizualny siatki można modyfikować za pomocą funkcji element_line() używając argumentów takich jak panel.grid (cała siatka), panel.grid.major (główne linie siatki), panel.grid.minor (pomocnicze linie siatki), panel.grid.major.x (główne linie siatki w osi x), panel.grid.major.y (główne linie siatki w osi y), panel.grid.minor.x (pomocnicze linie siatki w osi x), panel.grid.minor.y (pomocnicze linie siatki w osi y).

gg1 + theme(panel.grid = element_line(color = "grey", 
                                      size = 1.5, linetype = 3))
gg1 + theme(panel.grid.major = element_line(color = "grey",
                                            size = 1.5, linetype = 3))
gg1 + theme(panel.grid.major.x = element_line(color = "grey",
                                              size = 1.5, linetype = 3))
gg1 + theme(panel.grid.minor = element_line(color = "grey", 
                                            size = 1.5, linetype = 3))
Kod

Kod

Kod

Kod

Usunięcie siatki natomiast polega na podaniu funkcji element_blank() jako argumentu.

Kod
gg1 + theme(panel.grid = element_blank())

Inny mechanizm modyfikowania siatek polega na zastosowaniu funkcji scale_x_continuous() lub scale_y_continuous() (w przypadku zmiennych ciągłych) lub scale_x_discrete() lub scale_y_discrete() (w przypadku zmiennych dyskretnych). Pozwalają one, między innymi, na określenie przedziałów siatki, jej wartości, czy też zakresów.

gg1 + scale_x_continuous(breaks = seq(51, 86, 5),
                         minor_breaks = seq(51, 85, 1))
gg1 + scale_x_continuous(breaks = seq(51, 86, 5),
                         minor_breaks = seq(51, 85, 1),
                         limits = c(60, 80))
Kod

Kod

Te funkcje dają także bardziej zaawansowane możliwości modyfikacji siatek, takie jak zmiana etykiet używając argumentu label.

Kod
lata = function(x){
    paste(x, "lat")
}
gg1 + scale_x_continuous(breaks = seq(51, 86, 5), label = lata)

7.1.3 Marginesy

Kod
gg2 = gg1 + theme(plot.background = element_rect(color = "red", linewidth = 1))
gg2

?unit

Kod
gg2 + theme(plot.margin = margin(1, 2, 3, 4, "cm")) #trbl

7.2 Tworzenie własnej dekoracji

Kod
library(showtext)
font_add_google("Roboto")
showtext_auto()
Kod
theme_jn = function(){
    base_family="Roboto"
    grid_col = "#cccccc"
    theme(
        panel.grid.major = element_line(color = grid_col, size = 0.4),
        panel.grid.minor = element_line(color = grid_col, size = 0.12),
        axis.ticks = element_line(size = 0.15),
        text = element_text(family = base_family, size = 11.5),
        panel.background = element_rect(fill = "white", color = NA)
    )
}
Kod
gg1 + theme_jn()

https://ggplot2.tidyverse.org/reference/theme.html

7.3 Tekst

Tekst na wizualizacjach danych może nie tylko służyć do przedstawienia informacji, ale także mieć aspekt estetyczny. Jest on reprezentowany poprzez fonty, czyli zestawy znaków o określonym stylu i rozmiarze. Grupy fontów o podobnym wyglądzie nazywane są rodzinami fontów (ang. font families), np. Arial, Calibri, Helvetica. W ramach tych grup możemy dalej wyróżnić różne style (ang. font faces), takie jak plain, bold, czy italic.

Poniżej można zobaczyć przykład dwóch wizualizacji przedstawiających te same dane, ale z różnymi fontami.

Kod
library(showtext)
font_add_google("Roboto")
showtext_auto()
Kod
gt1 = ggplot(data = df0sel, aes(x = PKB_na_osobe, y = Ocz_dl_zycia,
                                color = Region)) + 
    geom_point(size = 1, alpha = 0.8) +
    labs(y = "Oczekiwana długość życia", x = "PKB na osobę (USD)", color = NULL, title = "Relacje zmiennych", subtitle = "Dane z 2019 roku", caption = "Dane pochodzą z portalu gapminder.org")
gt1

Kod
gt1 +
    theme(plot.title = element_text(family = "Roboto", size = 20, face = "bold"),
          plot.subtitle = element_text(family = "Roboto", size = 16, face = "italic"),
            axis.title = element_text(family = "Roboto", size = 14),
            axis.text = element_text(family = "Roboto", size = 12),
            legend.title = element_text(family = "Roboto", size = 14),
            legend.text = element_text(family = "Roboto", size = 12))

W pakiecie ggplot2 elementy związane ze stylem tekstu są zaimplementowane jako funkcje element_text() czy ggtext::element_markdown().

Kod
# install.packages("ggtext")
library(ggtext)
gt1 +
    theme(plot.title = element_markdown(family = "Roboto", size = 20, face = "bold"))

W przypadku wizualizacji danych tekst może pełnić wiele ról.

Zwrócenie uwagi odbiory na poszczególne elementy wizualizacji:

Kod
gt1 + 
    labs(title = "Relacje zmiennych dla krajów <i style='color:#F8766D;'>**afrykańskich**</i> w porównaniu do reszty świata") +
    theme(plot.title = element_markdown(family = "Roboto", size = 20))

Dodanie informacji z danych lub jej podkreślenie:

Kod
library(ggrepel)
df0selb = filter(df0sel, Populacja > 35000000, Populacja < 41000000)
gt1 +
  geom_text_repel(data = df0selb, aes(label = Kraj), min.segment.length = 0, size = 8)

Dodanie zewnętrznej informacji:

Kod
cor(df0sel$PKB_na_osobe, df0sel$Ocz_dl_zycia)
[1] 0.6492043
Kod
gt1 + 
  annotate(geom = "richtext", x = 140000, y = 60, size = 14,
           label = "R^2 = 0,65", fill = NA, label.color = NA)

Dodanie komentarza:

Kod
arrows = data.frame(x1 = 65000, x2 = 3650, y1 = 60, y2 = 55)
gt1 +
  annotate("rect", xmin = 0, xmax = 6000, ymin = 50, ymax = 68, alpha = .1) +
  annotate("text", x = 110000, y = 65, label = "Kraje o niskim PKB na osobę\nmają duże zróżnicowanie wartości\noczekiwanej długości życia", size = 8) + 
  geom_curve(
    data = arrows, aes(x = x1, y = y1, xend = x2, yend = y2),
    arrow = arrow(length = unit(0.08, "inch")), size = 0.5,
    color = "gray20", curvature = -0.3)

7.4 Multiwykresy

7.4.1 Regularne

1D

Kod
ggplot(data = df0sel, aes(x = Ocz_dl_zycia)) + 
    geom_histogram()

2D

Kod
ggplot(data = df0sel, aes(x = Ocz_dl_zycia, y = Plodnosc)) + 
    geom_point()

3D

Kod
ggplot(data = df0sel, aes(x = Ocz_dl_zycia, y = Plodnosc, color = PKB_na_osobe)) + 
    geom_point()

4D

Kod
ggplot(data = df0sel, aes(x = Ocz_dl_zycia, y = Plodnosc, color = PKB_na_osobe, shape = Region)) + 
    geom_point()

5D

Kod
df0sel2$Grupa_pop = df0sel2$Populacja > 10000000
ggplot(data = df0sel2, aes(x = Ocz_dl_zycia, y = Plodnosc, color = PKB_na_osobe, shape = Region)) + 
    geom_point() +
    facet_wrap(~ Grupa_pop)

6D

Kod
ggplot(data = df0sel2, aes(x = Ocz_dl_zycia, y = Plodnosc, color = PKB_na_osobe, shape = Region)) + 
    geom_point() +
    facet_grid(Rok ~ Grupa_pop)

7.4.2 Nieregularne

Kod
gg2 = ggplot(data = df0sel, aes(x = Ocz_dl_zycia)) + 
    geom_histogram()
gg2

Kod
gg3 = ggplot(data = df0sel, aes(x = Plodnosc)) + 
    geom_histogram()
gg3

Kod
library(cowplot)
plot_grid(gg2, gg3, ncol = 2)

Kod
plot_grid(gg2, gg3, nrow = 2, labels = "AUTO")

Kod
plot_grid(gg2, gg1, ncol = 2, rel_widths = c(1, 2))

Kod
pg1 = plot_grid(gg2, gg3, ncol = 2)
plot_grid(pg1, gg1, nrow = 2)

Kod
ggdraw(gg1) + draw_plot(gg2, 0.5, 0.65, 0.3, 0.3) 

7.5 Zapisywanie wykresów

Kod
ggsave("wykres.png", plot = g1, width = 10, height = 10, units = "cm", dpi = 300)
ggsave("wykres.tiff", plot = g1, width = 10, height = 10, units = "cm", dpi = 300)
ggsave("wykres.svg", plot = g1, width = 10, height = 10, units = "cm")
Kod
png("wykres.png", width = 10, height = 10, units = "cm", res = 300)
g1
dev.off()