Grundprincipperne i ggplot2

Øvelsesdata

I dette kapitel bruger vi data fra gapminder. Gapminder blev grundlagt i Stockholm i 2005 og indsamler global statistik om blandt andet sundhed, økonomisk udvikling og befolkningsudvikling. Du kan få adgang til data fra Gapminder med følgende kommandoer:

install.packages("gapminder")
library(gapminder)
gapminder

I Gapminder findes variablerne country, continent, year, lifeExp (levetid), pop (befolkningsstørrelse) og gdpPercap (BNP per indbygger). Disse data er vores øvelsesdata.

country continent year lifeExp pop gdpPercap
Afghanistan Asia 1952 28.80100 8425333 779.4453
Afghanistan Asia 1957 30.33200 9240934 820.8530
Afghanistan Asia 1962 31.99700 10267083 853.1007
Afghanistan Asia 1967 34.02000 11537966 836.1971
Afghanistan Asia 1972 36.08800 13079460 739.9811
Afghanistan Asia 1977 38.43800 14880372 786.1134
Afghanistan Asia 1982 39.85400 12881816 978.0114
Afghanistan Asia 1987 40.82200 13867957 852.3959
Afghanistan Asia 1992 41.67400 16317921 649.3414
Afghanistan Asia 1997 41.76300 22227415 635.3414
Afghanistan Asia 2002 42.12900 25268405 726.7341
Afghanistan Asia 2007 43.82800 31889923 974.5803
Albania Europe 1952 55.23000 1282697 1601.0561
Albania Europe 1957 59.28000 1476505 1942.2842
Albania Europe 1962 64.82000 1728137 2312.8890
Albania Europe 1967 66.22000 1984060 2760.1969
Albania Europe 1972 67.69000 2263554 3313.4222
Albania Europe 1977 68.93000 2509048 3533.0039
Albania Europe 1982 70.42000 2780097 3630.8807
Albania Europe 1987 72.00000 3075321 3738.9327
Albania Europe 1992 71.58100 3326498 2497.4379
Albania Europe 1997 72.95000 3428038 3193.0546
Albania Europe 2002 75.65100 3508512 4604.2117
Albania Europe 2007 76.42300 3600523 5937.0295

Pakken ggplot2 har en overordnet funktion: ggplot(). Vi begynder med at skrive ggplot() og angive argumentet data. Når den kode køres, får vi følgende figur.

ggplot(data=gapminder)
Tomt gråt rektangel uden akser, resultatet af ggplot med kun data angivet

Ovenfor ser vi kun et tomt gråt rektangel, hvilket skyldes, at grafen mangler to komponenter:

  1. Æstetisk mapping: aes()
  2. Geometriske komponenter: geom()

Vi fortsætter ved at tilføje aes(), hvor vi angiver, at x er year og y er pop. Resultatet er fortsat en tom graf, men nu med x-akse og y-akse specificeret:

ggplot(data=gapminder,
       aes(x=year,
           y=pop))
Tom graf med x-aksen year og y-aksen pop, men uden datapunkter

Grafen er tom, fordi den mangler geom(), som definerer geometriske komponenter. Vi tilføjer geom_points(), som specificerer, at vi vil se punkter på grafen:

ggplot(data=gapminder,
       aes(x=year,
           y=pop)) +
  geom_point()
Punktdiagram med sorte punkter for befolkning per land mod år 1952 til 2007

Det betyder, at man i ggplot() specificerer en datakilde, en æstetisk mapping (aes()) med blandt andet x-akse og y-akse, og derefter tilføjer lag med geometriske komponenter (eksempelvis geom_point()). Således har geom_point() arvet den æstetiske mapping fra ggplot()!

Labels (etiketter)

Etiketter tydeliggør grafens indhold. Den engelske betegnelse er labels, som herefter også bruges her. Labels angives med funktionen labs(). Du kan angive labels for x-akse, y-akse, titel, undertitel, figurforklaring osv. Eksempel følger:

ggplot(data=gapminder,
       aes(x=year,
           y=pop)) +
  geom_point() +
  labs(x="Year",
       y="Population",
       tag="A",
       title="Titeln på grafen",
       subtitle="Undertiteln på grafen",
       caption="Figurförklaring hamnar här.")
Punktdiagram over befolkning mod år med tag A, titel, undertitel, akseetiketter og figurforklaring

Det begynder at blive en del linjer kode nu. For at det skal være tydeligt, hvad der er nyt i hver kodeboks, vil det herefter blive markeret med kommentaren #NYT.

Ikon af et kodevindue med farvede kodelinjer

Deaktiver Scientific notation

Bemærk y-aksen på den foregående graf. Værdierne er præsenteret med scientific notation, som aktiveres ved meget store eller meget små tal. Du kan deaktivere dette med kommandoen:

options(scipen=999)

Som nævnt ovenfor arver geom_point() den æstetiske mapping (aes()) fra ggplot(). Vi kan dog tilsidesætte denne mapping ved at angive aes() i geom_point(). Så vil geom_point() ikke arve mappingen. Følgende graf er altså identisk med den foregående:

ggplot(data=gapminder) +
  # NYT
  geom_point(aes(x=year,
                 y=pop)) +
  labs(x="Year",
       y="Population",
       tag="A",
       title="Titeln på grafen",
       subtitle="Undertiteln på grafen",
       caption="Figurförklaring hamnar här.")
Punktdiagram over befolkning mod år med tag A, titel, undertitel, akseetiketter og figurforklaring

Det er en fordel at definere den æstetiske mapping øverst, fordi man så slipper for at gentage mappingen i hver geom().

Lad os farve punkterne efter det kontinent, de tilhører. Det gøres i aes() ved at specificere color=continent:

ggplot(data=gapminder, aes(x=year,
                           y=pop,
                           # NYT
                           color=continent)) +
  geom_point() +
  labs(x="Year",
       y="Population",
       tag="A",
       title="Titeln på grafen",
       subtitle="Undertiteln på grafen",
       caption="Figurförklaring hamnar här.")
Punktdiagram over befolkning mod år med punkterne farvet efter kontinent og signaturforklaringen continent

Til højre for grafen findes farvekodningen, hvis label "continent" skal justeres. Man kan angive en label i labs() med argumentet color.

ggplot(data=gapminder, aes(x=year,
                           y=pop,
                           color=continent)) +
  geom_point() +
  labs(x="Year",
       y="Population",
       tag="A",
       title="Titeln på grafen",
       subtitle="Undertiteln på grafen",
       caption="Figurförklaring hamnar här.",
       # NYT
       color="Kontinent")
Samme punktdiagram farvet efter kontinent, nu med signaturforklaringens overskrift Kontinent

I gapminder findes flere rækker for hvert land. I næste trin skal vi summere befolkningsstørrelsen på hvert kontinent for hvert kalenderår. Til det bruger vi dplyr på følgende måde:

gapminder_summary <- gapminder |>
  # Gruppér efter continent og year
  group_by(continent, year) |>
  # Beregn summen af pop (for hvert kontinent og år)
  summarise(population = sum(pop)) |>
  # Deaktiver grupperingen i data framen
  ungroup()

## Se data
gapminder_summary
continent year population
Africa 1952 237640501
Africa 1957 264837738
Africa 1962 296516865
Africa 1967 335289489
Africa 1972 379879541
Africa 1977 433061021
Africa 1982 499348587
Africa 1987 574834110
Africa 1992 659081517
Africa 1997 743832984
Africa 2002 833723916
Africa 2007 929539692
Americas 1952 345152446
Americas 1957 386953916
Americas 1962 433270254
Americas 1967 480746623
Americas 1972 529384210

Lad os beregne den samlede befolkning på hvert kontinent for hvert kalenderår:

ggplot(gapminder_summary,
       aes(x=year,
       y=population,
       color=continent)) +
  geom_point()
Punktdiagram over samlet befolkning per kontinent og år, hvor Asien ligger højest

Vi tilføjer en linje mellem punkterne for at se tendensen tydeligere:

ggplot(gapminder_summary,
       aes(x=year, y=population, color=continent)) +
  geom_point() +
  # NYT
  geom_line()
Linjediagram med punkter over samlet befolkning per kontinent 1952 til 2007

Med geom_text() kan du tilføje tekst. Nu tilføjer vi geom_text() for at vise befolkningsstørrelsen ved hvert datapunkt. Også geom_text() har æstetisk mapping. Her angiver vi kun argumentet label, som vi definerer som variablen population. X-værdien og y-værdien for geom_text() vil blive arvet fra ggplot().

ggplot(gapminder_summary,
       aes(x=year, y=population, color=continent)) +
  geom_point() +
  geom_line() +
  # NYT
  geom_text(aes(label=population))
Linjediagram per kontinent med befolkningstal som tekst ved hvert punkt, kraftigt overlappende

Vi gentager nøjagtig samme procedure, men med geom_label() i stedet for geom_text(). Med geom_label() får teksten en baggrund, der gør den mere synlig:

Linjediagram per kontinent med befolkningstal i indrammede etiketter, som overlapper hinanden

Begge ovenstående grafer er suboptimale, fordi labels er placeret for tæt på hinanden. Vi har brug for en funktion, der automatisk kan adskille labels. Det findes i ggrepel-pakken, hvis funktion geom_text_repel() automatisk adskiller tekst. Pakken fjerner datapunkter, som ikke kan visualiseres på en rimelig måde. Resultatet er som regel tilfredsstillende:

install.packages("ggrepel")
library(ggrepel)
ggplot(gapminder_summary,
       aes(x=year, y=population, color=continent)) +
  geom_point() +
  geom_line() +
  # NYT
  geom_text_repel(aes(label=population))
Linjediagram per kontinent, hvor befolkningstallene er placeret fra hinanden med ggrepel og hjælpelinjer

Det er stadig utydeligt. Muligvis bliver det tilstrækkeligt informativt kun at angive tallene for det første og det sidste år. Det kan gøres ved at beregne det maksimale henholdsvis minimale antal mennesker på hvert kontinent. Disse beregninger udføres og gemmes i objekterne max_population og min_population, som derefter også bruges i ggplot():

## Beregner hvert kontinents største population
max_population <- gapminder_summary %>%
  group_by(continent) %>%
  arrange(continent, desc(population)) %>%
  distinct(continent, .keep_all = T)

## Beregner hvert kontinents mindste population
min_population <- gapminder_summary %>%
  group_by(continent) %>%
  arrange(continent, population) %>%
  distinct(continent, .keep_all = T)

## Visualiserer
ggplot(gapminder_summary,
       aes(x=year, y=population, color=continent)) +
  geom_point() +
  # NYT
  # Tekst for maksimumværdier
  geom_label_repel(data=max_population,
                  aes(x=year, y=population, color=continent, label=population)) +
  # NYT
  # Tekst for minimumværdier
  geom_label_repel(data=min_population,
                  aes(x=year, y=population, color=continent, label=population))
Punktdiagram per kontinent med etiketter for laveste og højeste befolkning, placeret med geom_label_repel

Vi skal nu bruge geom_hline() og geom_vline() til at tilføje vandrette og lodrette referencelinjer. De placeres som regel bag datapunkterne, hvilket betyder, at de skal deklareres før datapunkterne (geom_point()) i koden. Vi placerer linjer på to niveauer på y-aksen (specificeres med argumentet yintercept) som vist nedenfor:

ggplot(data=gapminder, aes(x=year,
                           y=pop,
                           color=continent)) +
  geom_point() +

  # NYT
  geom_hline(aes(yintercept=1000000000)) +
  geom_hline(aes(yintercept=500000000)) +

  labs(x="Year",
       y="Population",
       tag="A",
       title="Titeln på grafen",
       subtitle="Undertiteln på grafen",
       caption="Figurförklaring hamnar här.")
Punktdiagram over befolkning per kontinent med vandrette referencelinjer ved 500 millioner og 1 milliard

Med geom_rect() kan et felt defineres på følgende måde:

ggplot(data=gapminder, aes(x=year,
                           y=pop,
                           color=continent)) +
  # NYT
  geom_rect(aes(xmin=1960, xmax=1990, ymin=500000000, ymax=100000000)) +

  geom_point() +

  labs(x="Year",
       y="Population",
       tag="A",
       title="Titeln på grafen",
       subtitle="Undertiteln på grafen",
       caption="Figurförklaring hamnar här.")
Punktdiagram over befolkning per kontinent med et mørkt rektangel over årene 1960 til 1990

Med geom_segment() kan du markere særligt interessante punkter. Funktionen har argumentet arrow til at tegne pile.

Bemærk, at y-aksen udviser et meget stort interval med stor forskel mellem datapunkterne. I sådanne scenarier er det en fordel at logaritmere variablen. Vi logaritmerer y-aksen ved at tilføje funktionen scale_y_log10(), som logaritmerer alle y-værdier. Det betyder, at du kan udføre matematiske beregninger i forbindelse med visualiseringen (datakilden ændres ikke).

ggplot(data=gapminder, aes(x=year,
                           y=pop,
                           color=continent)) +
  geom_point() +
  # NYT
  scale_y_log10()
Punktdiagram over befolkning per kontinent mod år med logaritmisk y-akse