6  Zmienne wizualne

Istnieje wiele klasyfikacji zmiennych wizualnych, w tym klasyfikacja Rotha (2017). W tej klasyfikacji zmienne wizualne są opisywane poprzez to czy są:

Dodatkowo, zmienne wizualne mogą być określone poprzez to do jakiego rodzaju zmiennych się odnoszą:

Zmienne wizualne. Źródło: Roth (2017)
Kod
library(dplyr)
library(ggplot2)
library(tidyr)
gm0 = readr::read_csv("data/gapminder_1950_2023.csv")
df1 = select(gm0, Kraj, Rok, Ocz_dl_zycia, PKB_na_osobe)
df0sel = filter(gm0, Rok == 2019)

Zmienne wizualne w ggplot2 są zaimplementowane jako mapowania (aes). Nie zawierają one jednak wszystkich zmiennych wizualnych z klasyfikacji Rotha.

Kod
zw_df = data.frame(nazwa = c("x", "y", "color", "fill", "alpha", "size", "linewidth", "shape"),
                   typ = c("położenie", "położenie", "barwa", "barwa", "barwa", "znak", "znak", "znak"),
                   opis = c("położenie wzdłuż osi X", "położenie wzdłuż osi Y", "kolor linii lub obrysu", "kolor wypełnienia", "przezroczystość", "wielkość", "szerokość linii", "kształt"))
knitr::kable(zw_df, caption = "Zmienne wizualne zaimplementowane w ggplot2")
Zmienne wizualne zaimplementowane w ggplot2
nazwa typ opis
x położenie położenie wzdłuż osi X
y położenie położenie wzdłuż osi Y
color barwa kolor linii lub obrysu
fill barwa kolor wypełnienia
alpha barwa przezroczystość
size znak wielkość
linewidth znak szerokość linii
shape znak kształt

6.1 Relacja między zmiennymi wizualnymi a elementami graficznymi

To jakie zmienne wizualne można użyć w danej sytuacji zależy od rodzaju elementu graficznego, który chcemy zastosować. W przypadku powierzchni możemy użyć jedynie barwy. Dla linii możemy użyć barwy, grubości linii, czy też kształtu linii. Najbardziej uniwersalne są punkty, które mogą być zdefiniowane poprzez barwę, wielkość, czy kształt.

Rysunek 6.1: Relacja między zmiennymi wizualnymi a elementami graficznym. Źródło: Tennekes i Nowosad (2022)

6.2 Barwa

Komputerowa reprezentacja kolorów

Kod
p_cat = hcl.colors(7, "Set3")
p_seq = rev(hcl.colors(7, "Greens"))
p_div = hcl.colors(7, "Temps")
p_cat2 = hcl.colors(7, "Set2")
p_seq2 = rev(hcl.colors(7, "viridis"))
p_div2 = hcl.colors(7, "Earth")
Kod
scales::show_col(p_cat, ncol = 7)

Kod
scales::show_col(p_seq, ncol = 7)

Kod
scales::show_col(p_div, ncol = 7)

Pakiet ggplot2 ma wbudowane różne skale kolorów, jednak są one dość ograniczone. Alternatywą jest pakiet colorspace, który oferuje większą ilość skal kolorów oraz prosty interfejs do ich wykorzystania.

Skale kolorystyczne z pakiety colorspace są wywoływane za pomocą schematu scale_<aesthetic>_<datatype>_<colorscale>(), gdzie:

  • jest nazwą estetyki (fill, color)
  • jest typem zmiennej (discrete, continuous, binned)
  • ustawia typ używanej skali kolorów (qualitative, sequential, diverging, divergingx)

Przykładowo, scale_color_continuous_sequential("Viridis") ustawia skalę kolorów o nazwie Viridis dla estetyki koloru, dla zmiennej ciągłej, w sposób sekwencyjny.

Kod
library(colorspace)

Pakiety wizualizacyjne posiadają określone domyślne skale kolorów. Zazwyczaj są one wybierane na podstawie typu zmiennej, której dotyczy estetyka koloru. Inna skala będzie używana dla zmiennych ciągłych, a inna dla zmiennych dyskretnych.

Przykładowo, ggplot2 dla zmiennych ciągłych stosowuje sekwencyjną skalę kolorów od ciemnego do jasnego niebieskiego. W poniższym przypadku dodatkowo mamy do czynienia z dużymi wartościami prezentowanej zmiennej, w którym to przypadku wartości używają notacji naukowej.1

Kod
gg1 = ggplot(data = df0sel, aes(x = PKB_na_osobe, y = Ocz_dl_zycia,
                                color = Populacja)) + 
    geom_point(size = 1.5) + 
    labs(y = "Oczekiwana długość życia", x = "PKB na osobę")
gg1

Niżej można zobaczyć cztery warianty skali kolorów dla zmiennej ciągłej. Pierwsze dwa korzystają z sekwencyjną, ciągłą skalę kolorów Viridis, z tą różnicą, że wizualizacja po lepiej odwraca kolejność kolorów, a ta po prawej zmienia punkt początkowy i końcowy skali.

options(scipen = 999)
gg1 + scale_color_continuous_sequential("Viridis", rev = FALSE)
gg1 + scale_color_continuous_sequential("Viridis", begin = 0.2, end = 0.8)
Kod

Kod

Następne dwie wizualizacje również korzystają ze skali kolorów Viridis. Tym razem stosują one transformację logarytmiczną dla przedstawianej zmiennej ciągłej. Pozwala ona na lepsze zobrazowanie różnic między wartościami zmiennej. Wizualizacja po lewej stronie posiada sekwencyjną, ciągłą skalę kolorów, a ta po prawej korzysta z sekwencyjnej skali kolorów z podziałem na przedziały.

gg1 + scale_color_continuous_sequential("Viridis", trans = "log10")
gg1 + scale_color_binned_sequential("Viridis", trans = "log10")
Kod

Kod

Kod
cols4all::c4a_gui()

Możliwe jest również zdefiniowanie własnej skali kolorów.

Kod
moja_pal = c("#FCDE9C", "#FAA476", "#F0746E", "#E34F6F", "#DC3977", "#B9257A", 
"#7C1D6F")
gg1 + scale_color_gradientn(colors = moja_pal) # dla zmiennych ciągłych

Kod
# scale_color_manual() -- dla zmiennych dyskretnych

Ślepota barw

Kod
library(colorblindcheck)
palette_check(moja_pal, plot = TRUE)

          name n tolerance ncp ndcp min_dist mean_dist max_dist
1       normal 7  6.671338  21   21 6.671338  31.85827 71.68336
2 deuteranopia 7  6.671338  21   20 6.529856  26.89899 62.60736
3   protanopia 7  6.671338  21   21 8.092279  31.50976 71.15744
4   tritanopia 7  6.671338  21   20 3.751602  22.68229 52.68331

Domyślne wizualizacja zmiennych dyskretnych nadaje unikalny kolor dla każdej wartości zmiennej.

Kod
ggplot(data = df0sel, aes(x = PKB_na_osobe, y = Ocz_dl_zycia, color = Region)) + 
    geom_point(size = 1.5, alpha = 0.8)

Jest to zazwyczaj oczekiwane zachowanie. Czasem jednak celem wizualizacji może być podkreślenie relacji jednej z katogorii z resztą. W takiej sytuacji można stworzyć własną wersję skali kolorów, dla której część kolorów zostanie rozjaśniona i poddana zmniejszeniu nasycenia, a inne zostaną przyciemnione.

Poniższa rycina pokazuje ten proces, gdzie pierwszy wiersz przedstawia oryginalne kolory. W drugim wierszu pierwsze cztery kolory są rozjaśnione, a w trzecim wierszu dodatkowo mają zmniejszoną nasycenie. Ostatni wiersz przedstawia finalną skalę kolorów, gdzie trzy ostatnie kolory są przyciemnione.

oi = palette.colors(n = 8)[-1]
scales::show_col(oi, ncol = 7)
oi[1:4] = lighten(oi[1:4], .4)
scales::show_col(oi, ncol = 7)
oi[1:4] = desaturate(oi[1:4], .8)
scales::show_col(oi, ncol = 7)
oi[5:7] = darken(oi[5:7], .3)
scales::show_col(oi, ncol = 7)
Kod

Kod

Kod

Kod

Zastosowanie tej skali kolorów można zobaczyć na kolejnej rycinie. Regionowi “Azja” został przypisany najciemniejszy kolor (siódmy), a pozostałe regiony otrzymały kolory z pierwszych trzech.

Kod
sel_oi = oi[c(7, 3:1)]
names(sel_oi) = c("Azja", "Europa", "Ameryka", "Afryka")
ggplot(data = df0sel, aes(x = PKB_na_osobe, y = Ocz_dl_zycia, color = Region)) + 
    geom_point(size = 1.5, alpha = 0.8) + 
    scale_color_manual(values = sel_oi)

Kod
library(colorblindcheck)
pal = hcl.colors(n = 7, palette = "Temps")
palette_check(pal, plot = TRUE)

          name n tolerance ncp ndcp  min_dist mean_dist max_dist
1       normal 7  15.22036  21   21 15.220363  36.65304 66.55783
2 deuteranopia 7  15.22036  21   12  5.294656  19.22404 41.68858
3   protanopia 7  15.22036  21   12  3.192646  20.36599 42.73267
4   tritanopia 7  15.22036  21   17  8.576467  35.96531 64.52385
Kod
df0az = filter(gm0, Region == "Azja")
Kod
library(forcats)
ggcol3 = ggplot(df0az, aes(Rok, fct_reorder(as.factor(Kraj), Ocz_dl_zycia,
                                            tail, n = 1, .desc = FALSE, .na_rm = TRUE),
                        fill = Ocz_dl_zycia)) +
    geom_tile() +
    labs(y = NULL, x = NULL, fill = NULL, title = "Oczekiwana długość życia") +
    scale_fill_continuous_divergingx(palette = "Temps", mid = 60, rev = TRUE) +
    theme(axis.text = element_text(size = 20))
Kod
# remotes::install_github("clauswilke/colorblindr")
library(colorblindr)
ggcol3c = ggcol3 + 
    scale_fill_continuous_divergingx(palette = "Earth", mid = 60) +
    theme(axis.title.y=element_blank(),
        axis.text.y=element_blank(),
        axis.ticks.y=element_blank()) +
    labs(title = NULL)
cvd_grid(ggcol3c)

Kod
colorspace::specplot(pal)

6.3 Znak

Kod
lty = c("solid", "dashed", "dotted", "dotdash", "longdash", "twodash")
linetypes = data.frame(
  y = seq_along(lty),
  lty = lty
) 
ggplot(linetypes, aes(0, y)) + 
  geom_segment(aes(xend = 5, yend = y, linetype = lty)) + 
  scale_linetype_identity() + 
  geom_text(aes(label = lty), hjust = 0, nudge_y = 0.2) +
  scale_x_continuous(NULL, breaks = NULL) + 
  scale_y_reverse(NULL, breaks = NULL)

Kod
df1sel = filter(df1, Kraj %in% c("Polska", "Niemcy", "Francja", "Włochy", "Hiszpania"))
ggplot(df1sel, aes(Rok, PKB_na_osobe, linetype = Kraj)) + 
  geom_line() +
  scale_linetype_manual(values = c("solid", "dashed", "dotted", "dotdash", "longdash"))

Kod
shapes = data.frame(
  shape = c(0:24),
  x = 0:24 %/% 5,
  y = -(0:24 %% 5)
)
ggplot(shapes, aes(x, y)) + 
  geom_point(aes(shape = shape), size = 5, fill = "red") +
  geom_text(aes(label = shape), hjust = 0, nudge_x = 0.15) +
  scale_shape_identity() +
  expand_limits(x = 4.1) +
  theme_void()

Kod
ggplot(data = df0sel, aes(x = PKB_na_osobe, y = Ocz_dl_zycia, color = Region, shape = Region)) + 
    geom_point(size = 1.5, alpha = 0.8) + 
    scale_shape_manual(values = 21:24)


  1. Przykładowo, 1e+9 oznacza 1 miliard, a 1e-3 oznacza 0,001.↩︎