Grunnleggende om ggplot2

Øvingsdata

I dette kapitlet bruker vi data fra gapminder. Gapminder ble grunnlagt i Stockholm i 2005 og samler global statistikk om blant annet helse, økonomisk utvikling og befolkningsutvikling. Du kan få tilgang til data fra Gapminder med følgende kommandoer:

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

I Gapminder finnes variablene country, continent, year, lifeExp (levealder), pop (befolkningsstørrelse) og gdpPercap (BNP per innbygger). Disse dataene er våre øvingsdata.

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 én overordnet funksjon: ggplot(). Vi begynner med å skrive ggplot() og angi argumentet data. Når denne koden kjøres, får vi følgende figur.

ggplot(data=gapminder)
Tomt grått rektangel uten akser, resultatet av ggplot med bare data angitt

Over ser vi bare et tomt grått rektangel, noe som skyldes at grafen mangler to komponenter:

  1. Estetisk mapping: aes()
  2. Geometriske komponenter: geom()

Vi fortsetter ved å legge til aes(), der vi angir at x er year og y er pop. Resultatet er fortsatt en tom graf, men nå med x-akse og y-akse spesifisert:

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

Grafen er tom fordi den mangler geom(), som definerer geometriske komponenter. Vi legger til geom_points(), som spesifiserer at vi vil se punkter i grafen:

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

Det betyr at man i ggplot() spesifiserer en datakilde, estetisk mapping (aes()) med blant annet x-akse og y-akse, og deretter legger til lag med geometriske komponenter (for eksempel geom_point()). Dermed har geom_point() arvet den estetiske mappingen fra ggplot()!

Labels (etiketter)

Etiketter tydeliggjør grafens innhold. Den engelske termen er labels, som heretter også brukes her. Labels angis med funksjonen labs(). Du kan angi labels for x-akse, y-akse, tittel, undertittel, 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.")
Spredningsdiagram av befolkning mot år med tagg A, tittel, undertittel, akseetiketter og figurforklaring

Det begynner å bli en del kodelinjer nå. For at det skal være tydelig hva som er nytt i hver kodeboks, vil det heretter markeres med kommentaren #NYTT.

Ikon av et kodevindu med fargede kodelinjer

Deaktiver scientific notation

Legg merke til y-aksen i forrige graf. Verdiene er presentert med scientific notation, som aktiveres for svært store eller svært små tall. Du kan deaktivere dette med kommandoen:

options(scipen=999)

Som nevnt over arver geom_point() den estetiske mappingen (aes()) fra ggplot(). Vi kan imidlertid overstyre denne mappingen ved å angi aes() i geom_point(). Da vil geom_point() ikke arve mappingen. Følgende graf er altså identisk med den forrige:

ggplot(data=gapminder) +
  # NYTT
  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.")
Spredningsdiagram av befolkning mot år med tagg A, tittel, undertittel, akseetiketter og figurforklaring

Å definere den estetiske mappingen øverst er en fordel, fordi man slipper å gjenta mappingen i hver geom().

La oss fargelegge punktene basert på kontinentet de tilhører. Dette gjøres i aes() ved å spesifisere at color=continent:

ggplot(data=gapminder, aes(x=year,
                           y=pop,
                           # NYTT
                           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.")
Spredningsdiagram av befolkning mot år med punktene farget etter kontinent og tegnforklaringen continent

Til høyre for grafen finnes fargekodingen, der labelen "continent" må justeres. Man kan angi 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.",
       # NYTT
       color="Kontinent")
Samme spredningsdiagram farget etter kontinent, nå med tegnforklaringens overskrift Kontinent

I gapminder finnes det flere rader for hvert land. I neste trinn skal vi summere befolkningsstørrelsen på hvert kontinent for hvert kalenderår. For å gjøre dette bruker vi dplyr slik:

gapminder_summary <- gapminder |>
  # Grupper på continent og year
  group_by(continent, year) |>
  # Beregn summen av 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

La oss beregne den totale befolkningen på hvert kontinent for hvert kalenderår:

ggplot(gapminder_summary,
       aes(x=year,
       y=population,
       color=continent)) +
  geom_point()
Punktdiagram over total befolkning per kontinent og år, der Asia ligger høyest

Vi legger til en linje mellom punktene for å se trenden tydeligere:

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

Med geom_text() kan du legge til tekst. Nå legger vi til geom_text() for å vise befolkningsstørrelsen ved hvert datapunkt. Også geom_text() har estetisk mapping. Her angir vi bare argumentet label, som vi definerer som variabelen population. X-verdien og y-verdien for geom_text() vil arves fra ggplot().

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

Vi gjentar nøyaktig samme prosedyre, men med geom_label() i stedet for geom_text(). Med geom_label() får teksten en bakgrunn som gjør den mer synlig:

Linjediagram per kontinent med befolkningstall i innrammede etiketter som overlapper hverandre

Begge grafene over er suboptimale fordi labels er plassert for nær hverandre. Vi trenger en funksjon som automatisk kan skille labels fra hverandre. Dette finnes i ggrepel-pakken, der funksjonen geom_text_repel() automatisk skiller tekst fra hverandre. Pakken fjerner datapunkter som ikke kan visualiseres på en rimelig måte. Resultatet er som regel tilfredsstillende:

install.packages("ggrepel")
library(ggrepel)
ggplot(gapminder_summary,
       aes(x=year, y=population, color=continent)) +
  geom_point() +
  geom_line() +
  # NYTT
  geom_text_repel(aes(label=population))
Linjediagram per kontinent der befolkningstallene er plassert fra hverandre med ggrepel og hjelpelinjer

Det er fortsatt utydelig. Muligens blir det tilstrekkelig informativt å bare rapportere tallene for første og siste år. Dette kan gjøres ved å beregne henholdsvis maksimalt og minimalt antall mennesker på hvert kontinent. Disse beregningene gjøres og sendes til objektene max_population og min_population, som deretter også brukes i ggplot():

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

## Beregner hvert kontinents minste befolkning
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() +
  # NYTT
  # Tekst for maksverdier
  geom_label_repel(data=max_population,
                  aes(x=year, y=population, color=continent, label=population)) +
  # NYTT
  # Tekst for minimumsverdier
  geom_label_repel(data=min_population,
                  aes(x=year, y=population, color=continent, label=population))
Punktdiagram per kontinent med etiketter for laveste og høyeste befolkning, plassert med geom_label_repel

Vi skal nå bruke geom_hline() og geom_vline() for å legge til horisontale og vertikale referanselinjer. Disse plasseres gjerne bak datapunktene, noe som innebærer at de må deklareres før datapunktene (geom_point()) i koden. Vi plasserer linjer på to nivåer på y-aksen (spesifiseres med argumentet yintercept) slik:

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

  # NYTT
  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.")
Spredningsdiagram av befolkning per kontinent med horisontale referanselinjer ved 500 millioner og 1 milliard

Med geom_rect() kan et felt defineres, slik:

ggplot(data=gapminder, aes(x=year,
                           y=pop,
                           color=continent)) +
  # NYTT
  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.")
Spredningsdiagram av befolkning per kontinent med et mørkt rektangel over årene 1960 til 1990

Med geom_segment() kan du markere særlig interessante punkter. Funksjonen har argumentet arrow for å tegne piler.

Legg merke til at y-aksen viser et svært stort intervall med stor forskjell mellom datapunktene. I slike scenarioer er det en fordel å logaritmere variabelen. Vi logaritmerer y-aksen ved å legge til funksjonen scale_y_log10(), som logaritmerer alle y-verdier. Det innebærer at du kan gjøre matematiske beregninger i forbindelse med visualiseringen (datakilden endres ikke).

ggplot(data=gapminder, aes(x=year,
                           y=pop,
                           color=continent)) +
  geom_point() +
  # NYTT
  scale_y_log10()
Spredningsdiagram av befolkning per kontinent mot år med logaritmisk y-akse