Customizarea graficelor

În această secțiune vom prezenta o serie de metode și tehnici de personalizare/customizare a graficelor atât prin adăugarea de elemente clasice precum titlu, etichetă pentru axe, etc. cât și prin modificarea temelor grafice.

Personalizarea elementelor grafice (axe, titlu, etc.)

Titlurile, etichetele axelor și adnotările textuale (pe grafic, axe, obiecte geometrice și legendă) reprezintă un aspect important în crearea unei figuri prin care vrem să transmitem o serie de informații. Pachetul ggplot2 pune la dispoziție mai multe funcții care permit efectuarea adnotării elementelor grafice fără mare dificultate.

Astfel putem adăuga/modifica titlul unui grafic folosind comanda ggtitle([titlu]):

Figura 1: Exemplu de grafic la care adăugăm un titlu.

sau alternativ putem utiliza funcția labs(title = [titlu]) care în plus permite adăugarea unui subtitlu (subtitle) și a unei surse/credit (caption):

Figura 2: Exemplu de grafic la care adăugăm un titlu și un subtitlu folosind funcția labs(title = [titlu]).

Pentru modificarea etichetelor axelor de coordonate putem folosi sau funcțiile xlab() pentru axa \(x\) și respectiv ylab() pentru axa \(y\) sau comanda labs(x = [eticheta axei x], y = [eticheta axei y]).

Figura 3: Exemplu de schimbare a denumirii axelor de coordonate.

De asemenea, am văzut că putem modifica/adăuga nume axelor și prin specificarea acestuia parametrului name într-o scală (e.g. scale_x_continuous). Atunci când situația impune este recomandat să folosim unităle de măsură ale valorilor variabilelor. Pachetul scales, https://scales.r-lib.org/, pune la dispoziție o serie de funcții ajutătoare, de exemplu atunci când unitățile de măsură sunt $ sau %:

ggplot(gapminder_2018, aes(x = gdpPerCap, y = lifeExp))+ 
  geom_point() + 
  scale_x_continuous(labels = scales::dollar_format(prefix = "", suffix = "$")) + 
  ggtitle("scales::dollar_format")

gapminder_2018 %>% 
  ggplot(aes(x = continent, fill = wb_income)) +
  geom_bar(position = "fill") +
  scale_y_continuous(breaks = seq(0, 1, by = .2), labels = scales::percent)+
  ggtitle("scales::percent")

Figura 4: Exemplu de utilizare a pachetului scales.

Totodată se pot întâlni situații în care axa \(x\) necesită etichete de lungime mai lungă, conducând astfel la o supraaglomerare a acestora, și în acest caz este recomandată rotirea axelor (coord_flip() - poate și ordonarea nivelelor fct_reorder()).

set.seed(1234)

gapminder_2018 %>%
  filter(continent == "Europe") %>% 
  sample_frac(0.5) %>%
  ggplot(aes(x = country, y = lifeExp)) + 
  geom_bar(stat = "identity") + 
  ggtitle("Fara rotirea axelor")

set.seed(1234)

gapminder_2018 %>%
  filter(continent == "Europe") %>% 
  sample_frac(0.5) %>%
  ggplot(aes(x = country, y = lifeExp)) + 
  geom_bar(stat = "identity") + 
  ggtitle("Cu rotirea axelor") + 
  coord_flip()

Figura 5: Exemplu de utilizare a etichetelor pe orizontală pentru a evita supraaglomerarea.

Un alt element grafic important în crearea unei figuri este legenda. Am văzut că în ggplot2 legenda este creată automat atunci când folosim un element estetic precum colour, fill, size, shape, etc. După cum am văzut, legendele și axele sunt cele două elemente de ghidaj (guides) care pot fi modificate prin intermediul funcțiilor de scalare. Spre deosebire de axe, legendele sunt elemente mai complexe deoarece, pe lângă faptul că pot fi poziționate în diverse locuri, ele pot afișa mai multe estetici simultan (e.g. colour, shape) provenite de la mai multe straturi geometrice.

Figura 6: Exemplu de adăugare a unei legende.

Atunci când vrem să eliminăm legenda sau stratul care nu vrem să apară în legendă avem la dispoziție mai multe variante: folosim funcția guides([nume estetica] = FALSE), funcția scale_[nume estetică]_[tip](guide = FALSE) sau argumentul show.legend = FALSE în elementul geometric:

ggplot(gapminder_2018, aes(x = gdpPerCap, y = lifeExp, colour = continent, size = pop)) +
  geom_point() + 
  ggtitle("Initial")

ggplot(gapminder_2018, aes(x = gdpPerCap, y = lifeExp, colour = continent, size = pop)) +
  geom_point() + 
  scale_color_discrete(guide = FALSE) +
  ggtitle("Initial")

ggplot(gapminder_2018, aes(x = gdpPerCap, y = lifeExp, colour = continent, size = pop)) +
  geom_point() + 
  guides(size = FALSE) +
  ggtitle("Initial")

Figura 7: Exemplu de utilizare a funcției guides.

Pachetul ggplot2 nu va adăuga o legendă în mod automat decât dacă avem o corespondență între un element estetic (colour, size, etc.) și o variabilă dar sunt multe situațiile în care dorim să evidențiem elementele din grafic printr-o legendă. Putem forța apariția unei legende prin maparea în interiorul obiectului geometric (atribuirea) a unei estetici într-o valoare fixată. Mai mult, în cazul în care dorim modificarea aspectului elementelor geometrice din legendă putem folosi funcția guide_legend() cu opțiunea override.aes.

gapminder_all %>% 
  filter(country == "Romania", 
         as.numeric(year) > 1900) %>%
ggplot(aes(x = gdpPerCap, y = lifeExp)) + 
  geom_point() + 
  geom_line() +
  labs(title = "Initial")

gapminder_all %>% 
  filter(country == "Romania", 
         as.numeric(year) > 1900) %>%
ggplot(aes(x = gdpPerCap, y = lifeExp)) + 
  geom_point(aes(colour = "punct")) + 
  geom_line(aes(colour = "linie")) +
  labs(title = "Legenda fortata")

gapminder_all %>% 
  filter(country == "Romania", 
         as.numeric(year) > 1900) %>%
ggplot(aes(x = gdpPerCap, y = lifeExp)) + 
  geom_point(aes(colour = "puncte")) + 
  geom_line(aes(colour = "linie")) +
  # modificam culorile
  scale_color_manual("", guide = "legend",
                     values = c("puncte" = "brown3",
                                "linie" = "gray")) +
  # vrem sa apara doar linie si punct
  guides(color = guide_legend(override.aes = list(linetype = c(1, 0),
                                                  shape = c(NA, 16)))) + 
  labs(title = "Legenda fortata customizata")

Figura 8: Exemplu de utilizare a funcției guide_legend() și customizare a legendei.

Funcțiile ajutătoare guide_legend() și guide_colorbar() oferă un control suplimentar asupra detaliilor de prezentare ale unei legende, astfel prima poate fi aplicată atât elementelor estetice discrete cât și celor continue pe când cea de-a doua este aplicată doar elementelor estetice continue (este folosită pentru reprezentarea gamelor continue de culori). Pentru utilizarea acestor funcții, ele trebuie atribuite parametrului guide (i.e. guide = guide_legend(...)) din funcția de scală corespunzătoare esteticii pe care dorim să o modificăm sau mai simplu în interiorul funcției ajutătoare guides (i.e. guides([estetică] = guide_legend(...)).

# guide_legend
# pe doua coloane
ggplot(gapminder_2018, aes(x = gdpPerCap, y = lifeExp, colour = continent)) +
  geom_point() + 
  guides(colour = guide_legend(ncol = 2))
# customizata
ggplot(gapminder_2018, aes(x = gdpPerCap, y = lifeExp, colour = continent)) +
  geom_point() + 
  guides(colour = guide_legend(reverse = TRUE,
                               direction = "horizontal",
    title.position = "top",
    label.position = "bottom",
    label.hjust = 0.5,
    label.vjust = 1,
    label.theme = element_text(angle = 90)))

# guide_colorbar
ggplot(gapminder_2018, aes(x = gdpPerCap, y = lifeExp, colour = pop, size = pop)) +
  geom_point() + 
  scale_x_continuous(name = "Produsul intern brut pe cap de locuitor", 
                     breaks = c(500, 5000, 15000, 25000, 
                                50000, 75000, 100000, 125000), 
                     minor_breaks = c(500, 2500, 7500)) +  
  scale_colour_distiller(palette = "Set1",
                         breaks = c(1e7, 1e8, 5e8, 8e8, 1e9, 1.3e9), 
                         name = "Populatia") 

ggplot(gapminder_2018, aes(x = gdpPerCap, y = lifeExp, colour = pop, size = pop)) +
  geom_point() + 
  scale_x_continuous(name = "Produsul intern brut pe cap de locuitor", 
                     breaks = c(500, 5000, 15000, 25000, 
                                50000, 75000, 100000, 125000), 
                     minor_breaks = c(500, 2500, 7500)) +  
  scale_colour_distiller(palette = "Set1",
                         breaks = c(1e7, 1e8, 5e8, 8e8, 1e9, 1.3e9), 
                         name = "Populatia") + 
  guides(colour = guide_colorbar(barwidth = 2, 
                                 barheight = 10, 
                                 ticks = FALSE), 
         size = FALSE) 

Figura 9: Exemplu de utilizare a funcțiilor guide_legend() și guide_colorbar() pentru customizarea legendei.

Adăugarea elementelor textuale la grafic

Sunt multe situații (e.g. în cazul unor prezentări, rapoarte, proiecte, etc.) în care pentru a face un grafic mai expresiv/ilustrativ este necesară adăugarea de elemente textuale explicative. Aceste elemente pot fi adăugate de cele mai multe ori folosind funcțiile geom_text, geom_label, annotate sau funcțiile corespunzătoare din pachetul ggrepel. Vom prezenta mai jos câteva exemple de grafice în care adăugarea de elemente textuale facilitează înțelegerea mesajului transmis de figură.

Pentru început vom folosi un barplot orizontal în care dorim să adăugăm marcaje în dreptul fiecărei bare:

set.seed(1234)

baza <- gapminder_2018 %>%
  filter(continent == "Europe") %>% 
  sample_frac(0.3) %>%
  ggplot(aes(x = fct_reorder(country, lifeExp), y = lifeExp)) + 
  geom_bar(stat = "identity") + 
  coord_flip() +
  labs(x = "")

baza + 
  geom_text(aes(label = lifeExp), 
            size = 3, nudge_y = 5) 

baza + 
  geom_text(aes(label = lifeExp), size = 3, 
            nudge_y = - 5, color = "white")

Figura 10: Exemplu de utilizare a funcției geom_text().

În cazul în care dorim să atragem atenția doar asupra unei bare atunci:

countries <- c("Moldova", "Georgia", "Italy", "Croatia", "Slovenia", 
              "Ukraine", "Azerbaijan", "Romania", "Turkey", "Macedonia",
              "Belgium", "France", "Finland", "Iceland", "Netherlands", 
              "Lithuania", "Serbia", "Portugal", "Ireland", "Bulgaria", 
              "United Kingdom")

baza_gap <- gapminder_2018 %>%
  filter(country %in% countries) %>% 
  mutate(ID = ifelse(country == "Romania", TRUE, FALSE))

ggplot(baza_gap, aes(x = reorder(country, lifeExp), y = lifeExp, fill = ID)) +
        geom_bar(stat = "identity") +
        coord_flip() +
        scale_fill_manual(values = c("white", "firebrick"), 
                          guide = FALSE) +
        annotate("text", x = "Romania", y = 70, label = "75.5", color = "white") 
Figura 11: Exemplu de utilizare a funcției annotate() pentru a evidenția un element.

Pentru a adăuga etichete la o diagramă cu bare proporțională (pentru care avem ajustată poziția la fill) trebuie să creăm o nouă variabilă care să stocheze locația (suma cumulativă a proporțiilor):

# pregatim setul de date
gapminder_2018_bf <- gapminder_2018 %>% 
  group_by(continent, wb_income) %>%
  # cate elemente am din fiecare wb_income pentru 
  # fiecare continent in parte
  tally() %>%
  # grupez dupa continent
  group_by(continent) %>%
  # determin proportia
  mutate(prc = n / sum(n), 
         label_y = cumsum(prc))
  
  
gapminder_2018_bf %>% 
  ggplot(aes(x = continent, y = prc, fill = fct_rev(wb_income))) +
  geom_bar(stat = "identity", color = "grey40") +
  scale_y_continuous(breaks = seq(0, 1, by = .2), labels = scales::percent) +
  geom_text(aes(label = scales::percent(prc), y = label_y),  
            vjust = 1, color = "white", size = 2.5) +
  scale_fill_manual(values = c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3")) +
  labs(fill = "Categorie Venit")
Figura 12: Exemplu de diagramă cu bare suprapuse adnotată cu elemente textuale.

Teme grafice

Putem schimba întregul aspect al graficului prin modificarea temei (funcția theme()) acestuia. Temele reprezintă o modalitate consistentă de a customiza/personaliza elementele grafice ale unei figuri, elemente care nu fac referire la setul de date (titlu, etichetele axelor, mărimea și tipul fontului, culoarea de fundal, liniile de marcaj - gridlines, legendele, etc.).

Pentru a utiliza o temă trebuie să adăugăm unui obiect de tip ggplot o funcție de temă care are forma generală theme_<nume temă>. Pachetul ggplot2 vine cu o serie de teme predefinite printre care menționăm: theme_grey, theme_linedraw, theme_bw, theme_minimal, theme_void, theme_dark, theme_classic, theme_light. Figura de mai jos prezintă fiecare dintre aceste teme pentru setul de date gapminder_2018:

Figura 13: Ilustrare a temelor predefinite.

Pe lângă temele predefinite în ggplot2 se pot folosi și teme din pachetul ggthemes dezvoltat de Jeffrey Arnold (pentru mai multe detalii vizitați site-ul ggthemes) care cuprinde mai mult de 20 de astfel de teme unele fiind reprezentări fidele ale tematicilor grafice folosite de reviste sau programe consacrate (de exemplu Economist sau Excel). Mai jos sunt prezentate doar patru dintre acestea:

Figura 14: Ilustrare a temelor din pachetul ggthemes.

Pentru a modifica elementele unei temei vom folosi funcția theme(). Această funcție ne permite controlul (aproape total) asupra elementelor grafice care nu țin de setul de date dar prin intermediul cărora figura devine mai expresivă: marimea fontului, culoarea de fundal, fontul și culoarea etichetelor axelor și ale legendei, etc.

Structura generală este theme([element al temei] = element_[nume functie asociată]([proprietati schimbate])) unde

  • [element al temei] face referire la un element grafic precum plot.title (controlează modul de afișare a titlului graficului), axis.ticks.x (elemente de marcaj de pe axa x), legend.key.height (înălțimea elementelor din legendă) etc. Pentru a vizualiza toate elementele grafice ale unei teme ce pot fi modificate se poate consulta documentația https://ggplot2.tidyverse.org/reference/theme.html

  • element_[nume functie asociată] reprezintă o funcție asociată elementelor temei care descrie caracteristicile vizuale ale elementului (toate elementele au asociate o astfel de funcție). Sunt patru astfel de funcții: element_text, element_line, element_rect și element_blank.

  • [proprietati schimbate] reprezintă caracteristicile vizuale ce urmează să fie modificate, i.e. font, family, linetype, colour, size, etc.

Tabelul de mai jos prezintă o serie de caracteristici vizuale pentru fiecare funcție asociată elementelor unei teme grafice:

Tabelul 1: Caracteristici vizuale pentru fiecare funcție asociată elementelor unei teme grafice.
Funcție Caracteristici
element_text() family, face, colour, size (în points), hjust, vjust, angle (în grade), lineheight
element_line() colour, size, linetype, lineend
element_rect() fill, colour, size, linetype
element_blank()

Exemple:

baza <- ggplot(gapminder_2018) + 
  geom_point(aes(x = gdpPerCap, y = lifeExp, color = continent, size = pop),
             alpha = 0.5, show.legend = FALSE) 

baza <- baza + labs(title = "Titlu") + xlab(NULL) + ylab(NULL)
baza + theme(plot.title = element_text(size = 16))
baza + theme(plot.title = element_text(face = "bold", colour = "red"))
baza + theme(plot.title = element_text(hjust = 1))

Figura 15: Exemplu de utilizare a opțiunii element_text() pentru modificarea titlului unui grafic.

baza + theme(panel.grid.major = element_line(colour = "black"))
baza + theme(panel.grid.major = element_line(size = 2))
baza + theme(panel.grid.major = element_line(linetype = "dotted"))

Figura 16: Exemplu de utilizare a opțiunii element_line() pentru modificarea gridului.

baza + theme(plot.background = element_rect(fill = "grey80", colour = NA))
baza + theme(plot.background = element_rect(colour = "red", size = 2))
baza + theme(panel.background = element_rect(fill = "linen"))

Figura 17: Exemplu de utilizare a opțiunii element_rect() pentru evidențiarea unei regiuni.

Mai jos regăsim o serie de elemente care permit controlul vizual al axelor și legendelor împreună cu funcțiile asociate acestora:

Tabelul 2: Elemente care permit controlul vizual al axelor și al legendelor.
Obiect Element Funcție Descriere
axe axis.line element_line() drepte paralele cu axele (nu se văd implicit)
axis.text element_text() etichetele axelor (x și y)
axis.text.x element_text() etichetele axei x
axis.text.y element_text() etichetele axei y
axis.title element_text() titlurile axelor
axis.title.x element_text() titlul axei x
axis.title.y element_text() titlul axei y
axis.ticks element_line() elementele de marcaj ale axelor
axis.ticks.length unit() lungimea elementelor de marcaj
legendă legend.background element_rect() fundalul legendei
legend.key element_rect() fundalul elementelor legendei
legend.key.size unit() mărimea elementelor legendei
legend.key.height unit() înălțimea elementelor legendei
legend.key.width unit() lățimea elementelor legendei
legend.margin unit() marginile legendei
legend.text element_text() etichetele legendei
legend.text.align 0–1 alinierea etichetelor legendei (0 = dreapta, 1 = stânga)
legend.title element_text() titlul legendei
legend.title.align 0–1 alinierea titlului legendei (0 = dreapta, 1 = stânga)

baza + theme(axis.line = element_line(colour = "grey80", size = 1.5))
baza + theme(axis.text = element_text(color = "firebrick", size = 12))
baza + theme(axis.text.x = element_text(angle = -90, vjust = 0.5))

Figura 18: Exemplu de utilizare a opțiunilor element_line() și element_text() în customizarea axelor.

baza2 <- ggplot(gapminder_2018) + 
  geom_point(aes(x = gdpPerCap, y = lifeExp, color = continent, size = pop),
             alpha = 0.5) + 
  guides(size = "none")
             
baza2 + theme(
  legend.background = element_rect(
    fill = "linen", 
    colour = "grey50", 
    size = 1
  )
)
baza2 + theme(
  legend.key = element_rect(color = "grey80"),
  legend.key.width = unit(0.9, "cm"),
  legend.key.height = unit(0.75, "cm")
)
baza2 + theme(
  legend.text = element_text(size = 15),
  legend.title = element_text(size = 15, face = "bold")
)

Figura 19: Exemplu de utilizare a opțiunilor element_line() și element_text() în customizarea legendelor.

Pentru a mai multe detalii și exemple privind elementele grafice ale unei teme ce pot fi modificate se poate consulta documentația https://ggplot2.tidyverse.org/reference/theme.html.