5  Grafiken mit ggplot2

Grafiken sind für die Datenanalyse sehr wichtig. Einerseits können wir sie für explorative Datenanalyse einsetzen, um eventuell verborgene Zusammenhänge zu entdecken oder uns einfach einen Überblick zu verschaffen. Andererseits brauchen wir Grafiken, um Resultate darzustellen und anderen zu kommunizieren.

Wir haben schon mehrmals in diesem Skript Grafiken mit dem Package ggplot2 erstellt, ohne uns den Code genauer anzuschauen. In diesem Kapitel werden wir nun die Syntax von ggplot2 kennenlernen.

Hinweis

Es gibt in R verschiedene Möglichkeiten, Grafiken zu erstellen. Mit dem ursprünglichen Grafiksystem (R Base Graphics) kann man sehr schnell einfache Grafiken erstellen. Es ist auch sehr mächtig und flexibel, aber das Problem ist, dass die Syntax etwas archaisch erscheint, und es für Anfänger schwierig ist, Grafiken selber anzupassen.

Im Gegensatz dazu basiert ggplot2 auf einer intuitiven Syntax, der sogenannten Grammar of graphics. Sobald man sich daran gewöhnt hat, kann man mit einer eleganten und konsistenten “Grammatik” sehr komplexe Grafiken erstellen. ggplot2 ist darauf ausgelegt, mit tidy Data zu arbeiten, d.h. wir brauchen Datensätze im long Format. Grafiken werden nun immer nach demselben Prinzip erstellt:

Schritt 1: Wir beginnen mit einem Datensatz und erstellen ein Plot-Objekt mit der Funktion ggplot().

Schritt 2: Wir definieren sogenannte “aesthetic mappings”, d.h. wir bestimmen welche Variablen auf den X-, bzw. Y-Achsen dargestellt werden sollen, und welche Variablen benutzt werden, um die Daten zu gruppieren. Die Funktion, welche wir dafür benutzen heisst aes().

Schritt 3: Wir fügen dem Plot eine oder mehrere “Layers” oder “Schichten” hinzu. Diese Layers definieren, wie etwas dargestellt werden soll, z.B. als Linie oder als Histogramm. Die Funktionen beginnen mit dem Präfix geom_, z.B. geom_line().

Um ggplot2 zu benutzen brauchen wir nun noch einen zusätzlichen Operator: +. Diesen kennen Sie bereits als mathematischen Operator, aber in diesem Zusammenhang bedeutet die Verwendung von +, dass wir einzelne Elemente eines Plot-Objektes zusammenfügen.

Nach dieser etwas abstrakten Einführung illustrieren wir diese Schritte an einem praktischen Beispiel.

Am Ende des letzten Kapitels haben wir den Zusammenhang zwischen psychischem Stress und Geschlecht untersucht. Wir laden nun nochmals den Datensatz:

library(tidyverse)
library(haven)
beispieldaten <- read_sav("data/beispieldaten.sav") |>
    # Faktoren konvertieren und SPSS-Labels zuweisen
    mutate(across(where(is.labelled), haven::as_factor)) |>
    # Labels der Bildungsfaktoren vereinfachen
    mutate(across(c(bildung_vater, bildung_mutter),
                  \(x) fct_recode(x,
                                  Hauptschule = "Hauptschulabschluss oder niedriger",
                                  Realschule = "Realschulabschluss (mittlere Reife)",
                                  Abitur = "Fachabitur, Abitur",
                                  Hochschule = "Fachhochschulabschluss, Universitätsabschluss")))

und erstellen einen Datensatz, der nur die Variablen ID, geschlecht und stress_psychisch enthält.

stress <- beispieldaten |>
  select(ID, geschlecht, stress_psychisch) |>
  drop_na()
stress
# A tibble: 284 × 3
      ID geschlecht stress_psychisch
   <dbl> <fct>                 <dbl>
 1     1 weiblich               1.67
 2     2 männlich               3.5 
 3    10 weiblich               3.67
 4    11 weiblich               1.5 
 5    12 weiblich               2.5 
 6    14 männlich               1   
 7    15 männlich               2.5 
 8    16 weiblich               3.5 
 9    17 männlich               1.67
10    18 männlich               2.5 
# ℹ 274 more rows

In diesem Datensatz haben wir eine numerische Variable, stress_psychisch und eine Gruppierungsvariable, geschlecht. Wir wollen die Verteilung von stress_psychisch zwischen männlichen und weiblichen Jugendlichen vergleichen. Die Verteilung können wir auf verschiedene Arten grafisch darstellen: mit Punkten, einem Boxplot oder einem Violin-Plot. Diese drei Methoden sind in der Sprache von ggplot2 verschiedene geoms und können so benutzt werden: geom_point(), geom_boxplot() oder geom_violin(). Zusätzlich gibt es noch eine Funktion geom_jitter(), welche die Punkte in einem Punktdiagramm nicht aufeinander zeichnet, sondern mit einem räumlichen “jittering” (Flackern) versieht.

Das ggplot2 Package können wir entweder individuell oder als Teil des tidyverse laden:

library(ggplot2)

# oder

library(tidyverse)

5.1 Schritt 1: Plot-Objekt erstellen

Wir beginnen mit einem Datensatz und erstellen ein Plot-Objekt mit der Funktion ggplot(). Diese Funktion hat als erstes Argument einen Dataframe. Dies bedeutet, dass wir den pipe Operator verwenden können:

Wir haben also zwei Möglichkeiten. Wir bevorzugen hier die pipe Notation, aber es ist selbstverständlich auch möglich, den Dataframe innerhalb der Funktion als Argument anzugeben. Gleichzeitig weisen wir das Objekt einer Variablen zu, und nennen diese p.

# 1. Variante
p <- ggplot(data = stress)

# 2. Variante
p <- stress |>
  ggplot()

5.2 Schritt 2: Aesthetic mappings

Nun definieren wir mit dem zweiten Argument mapping die “aesthetic mappings”. Diese bestimmen, wie die Variablen benutzt werden, um die Daten darzustellen, und werden mit der Funktion aes() definiert. Wir wollen die Gruppierungsvariable geschlecht auf der X-Achse darstellen und stress_psychisch soll auf der Y-Achse angezeigt werden. Zusätzlich kann aes() weitere Argumente haben: fill, color, shape, linetype, group. Diese werden dazu benutzt, um den Stufen der Gruppierungsvariablen unterschiedliche Farben, Formen, Linien, etc. zuzuweisen.

In diesem Beispiel haben wir die Gruppierungsvariable geschlecht und wir wollen, dass die beiden Stufen von geschlecht verschiedene Farben haben und mit verschiedenen Farben “ausgefüllt” werden.

Hinweis

color ist ein Attribut von Linien oder Punkten, fill ist ein Attribut von Flächen.

Wenn wir die “aesthetic mappings” innerhalb der Funktion ggplot() definieren, gelten sie für alle “Layers”, d.h. für alle Elemente des Plots. Wir könnten diese mappings auch für jede “Layer” separat definieren.

p <- stress |>
  ggplot(mapping = aes(
    x = geschlecht,
    y = stress_psychisch,
    color = geschlecht,
    fill = geschlecht
  ))

p ist nun ein “leeres” Plot-Objekt. Wir können es uns anschauen, aber es wird noch nichts angezeigt, da es noch keine “Layers” enthält. Ein ggplot2 Objekt wird angezeigt, indem wir das Objekt auf der Konsole ausgeben lassen, entweder mit oder ohne print().

p

# oder print(p)

Wir sehen, dass ggplot2 für uns schon die Achsen anhand der Variablennamen beschriftet hat.

5.3 Schritt 3: geoms hinzufügen

Dem Plot-Objekt p können wir nun mit geom_ Funktionen “Layers” hinzufügen. Die Syntax funktioniert so: Wir “addieren” zu dem Plot-Objekt p ein geom: p + geom_.

5.3.1 Punktdiagramm

Wir versuchen zuerst, die Beobachtungen als Punkte darzustellen:

# die Funktion geom_point() hat ein size Argument
p + geom_point(size = 3)

Die Punkte werden nun in verschiedenen Farben dargestellt, aber innerhalb eines Geschlechts werden Punkte eventuell übereinander geplottet, wenn sie denselben Wert haben (overplotting). Für diesen Fall gibt es die Funktion geom_jitter(), welche Punkte mit einem “jittering’ nebeneinander zeichnet:

p + geom_jitter()

geom_jitter() hat ein Argument width, mit dem wir bestimmen können, wie breit die Streuung der Punkte ist.

p + geom_jitter(width = 0.2)

geom_jitter() hat weitere Argumente: size bestimmt den Durchmesser der Punkte, und alpha bestimmt die Transparenz.

p + geom_jitter(width = 0.2, size = 4, alpha = 0.6)

5.3.2 Verteilung grafisch darstellen

Eine weitere Möglichkeit wäre, die zentrale Tendenz und Streuung der Daten mit einem Boxplot- oder Violin-Diagramm darzustellen.

p + geom_boxplot()

In einem Boxplot wird der Median dargestellt, das Rechteck repräsentiert die mittleren 50%, und die “whiskers” zeigen 1.5 * den Interquartilsbereich. Ausreisser werden mit Punkten dargestellt. Um den Median zu sehen, ist es besser, wenn wir das fill Attribut weglassen:

p <- stress |>
  ggplot(mapping = aes(
    x = geschlecht,
    y = stress_psychisch,
    color = geschlecht
  ))

p + geom_boxplot()

Ein Violin-Plot ist ähnlich wie ein Boxplot, zeigt aber nicht die Quantile, sondern ein “kernel density estimate”. Ein Violin-Plot sieht am besten aus, wenn wir das fill Attribut verwenden.

p <- stress |>
  ggplot(mapping = aes(
    x = geschlecht,
    y = stress_psychisch,
    fill = geschlecht
  ))
p + geom_violin()

Wenn wir feststellen, dass ein Mapping nicht für alle “Layers” gelten soll, dann können wir es für jede “Layer” individuell definieren, anstatt in der ggplot() Funktion:

p <- stress |>
  ggplot(mapping = aes(
    x = geschlecht,
    y = stress_psychisch
  ))
p + geom_boxplot(mapping = aes(color = geschlecht))
# oder einfach
p + geom_boxplot(aes(color = geschlecht))

p + geom_violin(aes(fill = geschlecht))

5.3.3 Mehrere Layers kombinieren

Wir können auch mehrere “Layers” verwenden. Wir müssen lediglich mehrere geom_ Funktionen mit einem + zusammenfügen:

p +
  geom_violin(aes(fill = geschlecht)) +
  geom_jitter(width = 0.2, alpha = 0.6)

Übung

Schauen Sie sich die Grafikbeispiele in den vorangegangenen Kapiteln an. Verstehen Sie nun den Code?

In den bisherigen Beipielen haben wir kein Plot-Objekt erstellt, sondern den Datensatz mit dem pipe Operator an die ggplot() Funktion geschickt, und dann mit + direkt die geoms hinzugefügt. Ausserdem haben wir weitere Funktionen verwendet, wie z.B. theme_classic(), um den Hintergrund weiss darzustellen.

stress |>
  ggplot(mapping = aes(
    x = geschlecht,
    y = stress_psychisch,
    fill = geschlecht
  )) +
  geom_violin() +
  geom_jitter(width = 0.2, alpha = 0.6) +
  theme_classic()

5.4 Geoms für verschiedene Datentypen

Wir fassen zusammen: bisher haben wir gelernt, dass wir einen Plot in mehreren Schritten zusammenstellen. Wir beginnen mit einem Dataframe und definieren mit der ggplot() Funktion ein ggplot2 Objekt. Mit der aes() Funktion weisen wir Variablen eines Dataframes der X-, bzw. der Y-Achse zu und definieren weitere “aesthetic mappings”, z.B. eine farbliche Codierung anhand einer Gruppierungsvariablen. Anschliessen fügen wir dem Plot-Objekt Grafikelemente mit geom_* Funktionen als “Layers” hinzu.

Nun schauen wir uns eine Auswahl an geoms für verschiedene Kombination von Variablen an. Wir können dabei entweder eine Variable auf der X-Achse oder zwei Variablen auf den X- und Y-Achsen darstellen und diese Variablen können entweder kontinuierlich oder kategorial sein.

Hinweis

Wir werden hier nur eine kleine Auswahl der möglichen ggplot2 Funktionen betrachten. Das Package ist sehr umfangreich und hat eine sehr übersichtliche Website, auf der alles dokumentiert ist: ggplot2 Dokumentation

Nachdem Sie dieses Kapitel durchgearbeitet haben, sind Sie in der Lage, selber Lösungen für grafische Darstellungen zu finden. Datenvisualisierung kann ein sehr kreativer Prozess sein und macht Spass! Weitere Beispiele für spezifische Datenanalysemethoden sehen Sie in den nachfolgenden Kapiteln.

Für die folgenden Beispiele verwenden wir die Datensätze beispieldaten und kinderwunsch:

kinderwunsch <- read_sav("data/Kinderwunsch_Schweiz.sav")

kinderwunsch <- kinderwunsch |>
  mutate(geschl = haven::as_factor(geschl))

5.4.1 Eine Variable

Wenn wir nur eine Variable auf der X-Achse grafisch darstellen möchten, müssen wir aber dennoch Werte auf der Y-Achse darstellen. Dies wird oft eine deskriptive Zusammenfassung wie z.B. Häufigkeiten sein.

Kategoriale Variablen

Wenn wir eine kategoriale Variable grafisch darstellen, verwenden wir oft einen bar chart or bar graph. Dieser stellt z.B. Häufigkeiten der verschiedenen Kategorien anhand eines Rechtecks (rectangular bar) dar. Die Funktion, welche dafür verwendet wird, heisst geom_bar().

Als Beispiel wollen wir die Häufigkeiten der vier Bildungsstufen des Vaters plotten.

Hinweis

Wenn wir fill = "lightblue", color = "black" nicht innerhalb der aes() Funktion verwenden, dann werden diese Argumente nicht als Gruppierungsanweisung aufgefasst. Wir können z.B. mit fill = "lightblue" einfach alle Elemente hellblau einfärben.

p <- beispieldaten |>
  select(bildung_vater) |>
  drop_na() |>
  ggplot(aes(x = bildung_vater))
p + geom_bar(fill = "lightblue", color = "black")

Eine Übersicht über die möglichen Farbnamen erhalten Sie mit der Funktion colors(). Es gibt 657 davon, wir zeigen hier mit sample(15) nur 15 zufällig ausgewählte an:

colors() |> sample(15)
 [1] "moccasin"      "steelblue4"    "bisque"        "gray18"       
 [5] "grey61"        "purple1"       "orchid4"       "grey80"       
 [9] "palegreen1"    "darkslateblue" "green"         "aquamarine3"  
[13] "grey39"        "slateblue"     "tan3"         

Auch hier können wir zusätzlich eine Gruppierungsvariable angeben, anhand derer wir die Rechtecke farblich kodieren.

p <- beispieldaten |>
  select(bildung_vater, westost) |>
  drop_na() |>
  ggplot(aes(x = bildung_vater, fill = westost))
p + geom_bar()

Standardmässig kreiert ggplot2 einen stacked Bar Chart, d.h. die Rechtecke werden aufeinander gestapelt. Wenn dies nicht erwünscht ist, können wir das Argument position = "dodge" der Funktion geom_bar() verwenden. Damit teilen wir mit, dass die Bars nebeneinander gezeichnet werden sollen.

p + geom_bar(position = "dodge")

Als dritte Variante können wir position = "identity" verwenden; so werden die Bars übereinander gezeichnet. Da das hintere Rechteck nicht mehr sichtbar ist, verwenden wir das alpha Argument, um die Bars transparent zu machen.

p + geom_bar(position = "identity", alpha = 0.6)

Kontinuierliche Variablen

Falls die Variable, welche wir grafisch darstellen wollen, nicht kategorial, sondern kontinuierlich ist, bietet sich ein Histogramm an; dies erzeugen wir mit der Funktion geom_histogram(). Als Beispiel betrachten wir den psychischen Stress.

Ein Histogramm bietet eine grafische Darstellung der Verteilung einer numerischen Variablen. Dazu werden die Werte dieser Variablen in diskrete Intervalle, oder bins, unterteilt. Auf der Y-Achse werden dann, analog zu einem Bar Chart, die Häufigkeiten in den jeweiligen Intervallen dargestellt. Die Bestimmung der Grösse der Intervalle (binwidth) ist kritisch. Wenn wir nichts spezifieren, wählt ggplot2 selber eine binwidth aus, aber wir können diese mit dem Argument binwidth auch selber angeben.

p <- beispieldaten |>
  select(stress_psychisch) |>
  drop_na() |>
  ggplot(mapping = aes(x = stress_psychisch))

# Wir lassen die binwidth automatisch auswählen
p + geom_histogram()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

# Wir bestimmen die binwidth selber
p + geom_histogram(binwidth = 0.5)

Die Bestimmung der binwidth hängt natürlich von der Skala der Variablen ab und sollte weder zu fein noch zu grob sein.

Übung

Probieren Sie mehrere Werte für die binwidth aus. Was ist optimal?

Wenn wir auf der Y-Achse anstelle der absoluten die relativen Häufigkeiten sehen wollen, können wir y = after_stat(density) als Argument der aes() Funktion verwenden.

p <- beispieldaten |>
  select(stress_psychisch) |>
  drop_na() |>
  ggplot(mapping = aes(x = stress_psychisch, y = after_stat(density)))

p + geom_histogram(binwidth = 0.5)

Selbsverständlich gibt es auch für Histogramme die Möglichkeit, eine Gruppierungsvariable zu verwenden.

p <- beispieldaten |>
  select(stress_psychisch, geschlecht) |>
  drop_na() |>
  ggplot(mapping = aes(x = stress_psychisch, fill = geschlecht))

p + geom_histogram(binwidth = 0.5)

Wie beim Bar Chart werden die Histogramme übereinander geplotted (“stacked”). Wollen wir sie aufeinander, verwenden wir position = "identity".

p <- beispieldaten |>
  select(stress_psychisch, geschlecht) |>
  drop_na() |>
  ggplot(mapping = aes(x = stress_psychisch, fill = geschlecht))

p + geom_histogram(
  binwidth = 0.5,
  position = "identity",
  alpha = 0.6
)

Nebeneinander geht auch:

p <- beispieldaten |>
  select(stress_psychisch, geschlecht) |>
  drop_na() |>
  ggplot(mapping = aes(x = stress_psychisch, fill = geschlecht))

p + geom_histogram(
  binwidth = 0.5,
  position = "dodge"
)

5.4.2 Zwei Variablen

Nun stellen wir zwei Variablen eines Datensatzes gemeinsam dar. Auch hier hängen die möglichen geoms vom Datentyp der Variablen ab.

X und Y kontinuierlich

Wenn beide Variablen kontinuierlich sind, können wir deren Zusammenhang anhand eines ‘Scatterplots’ oder eines Liniendiagrams darstellen. Wir verwenden die Funktionen geom_point(), bzw. geom_line().

Als Beispiel wollen wir den Zusammenhang zwischen psychischem Stress und Lebenszufriedenheit visualisieren.

p <- beispieldaten |>
  select(stress_psychisch, leben_gesamt) |>
  drop_na() |>
  ggplot(mapping = aes(x = stress_psychisch, y = leben_gesamt))

p + geom_point(size = 2, alpha = 0.6)

Die size und alpha Argumente haben wir weiter oben bereits kennengelernt, sowie die Möglichkeit, ‘overplotting’ mit der Funktion geom_jitter() zu vermeiden. Sowohl geom_jitter() als auch geom_point() haben auch eine colour oder ein color Argument.

p + geom_jitter(colour = "purple")

Die Gruppierung anhand einer kategorialen Variablen funktioniert auch hier. Wir verwenden sowohl die Farbe als auch die Form der Punkte, um die Kategorien besser unterscheiden zu können.

p <- beispieldaten |>
  select(stress_psychisch, leben_gesamt, geschlecht) |>
  drop_na() |>
  ggplot(mapping = aes(
    x = stress_psychisch,
    y = leben_gesamt,
    color = geschlecht,
    shape = geschlecht
  ))

p + geom_jitter(size = 3, alpha = 0.9)

Mit der Funktion geom_line() können wir Liniendiagramme erstellen. Als Beispiel wollen wir in einem neuen Dataframe die mittlere Schulnote für die verschiedenen Bildungsniveaus des Vaters berechnen, und dann grafisch darstellen. Bevor wir die durchschnittliche Note plotten, konvertieren wir den Faktor bildung_vater zu einer numerischen Variable.

Vertiefung

Wir könnten bildung_vater auch als Faktor verwenden, müssten dann aber eine Gruppierungsvariable für geom_line() definieren. In diesem Fall würden wir group = 1 verwenden, da wir nur eine Gruppe haben: p + geom_line(group = 1)

bildung_vater <- beispieldaten |>
  select(Gesamtnote, bildung_vater) |>
  drop_na() |>
  group_by(bildung_vater) |>
  summarize(Gesamtnote = mean(Gesamtnote)) |>
  mutate(bildung_vater_num = as.numeric(bildung_vater))

bildung_vater
# A tibble: 4 × 3
  bildung_vater Gesamtnote bildung_vater_num
  <fct>              <dbl>             <dbl>
1 Hauptschule         4.05                 1
2 Realschule          4.38                 2
3 Abitur              4.50                 3
4 Hochschule          4.69                 4
p <- bildung_vater |>
  ggplot(aes(
    x = bildung_vater_num,
    y = Gesamtnote
  ))

p + geom_line()

Wir können nun wie im “Honeymoon oder Hangover”-Beispiel das Liniendiagramm um Punkte ergänzen:

p + geom_line() +
  geom_point(size = 4)

Auch geom_line() hat Argumente, um die Eigenschaften zu ändern. In diesem Fall benützen wir das Argument linteype, welches die Werte "blank", "solid", "dashed", "dotted", "dotdash", "longdash" oder "twodash" anehmen kann.

p + geom_line(linetype = "dashed", linewidth = 2) +
  geom_point(size = 8)

X kategorial und Y kontinuierlich

Wenn eine der Variablen kategorial ist, können wir diese, anstatt sie als Gruppierungsvariable zu verwenden, auf einer Achse darstellen.

Beipiele dafür haben wir oben schon gesehen: dort haben wir die Variablen geschlecht und psychischer Stress dargestellt und die Funktionen geom_boxplot() und geom_violin() benutzt. Wir können aber auch die Funktion geom_bar() für zwei Variablen verwenden. Die Variable auf der Y-Achse wird in diesem Fall für alle Beobachtungen in den Kategieren auf der X-Achse summiert. Da dies keine statistische Transformation benötigt, verwenden wir das Argument stat = 'identity'.

Als Beispiel betrachten wir den Kinderwunsch-Datensatz. In diesem wurden Versuchspersonen gefragt, ob sie ein Kind wollen oder nicht (binäre Antwort). Zusätzlich wurden die Intimität zur eigenen Mutter, die emotionale Einstellung zu Kindern und das Geschlecht erhoben. Auf der Y-Achse stellen wir die absoluten Häufigkeiten einer “Ja”-Antwort dar.

p <- kinderwunsch |>
  ggplot(aes(
    x = geschl,
    y = kind_d,
    fill = geschl
  ))

p + geom_bar(stat = "identity")

Um diese Grafik besser verstehen zu können, berechnen wir zusätzlich noch die relativen Häufigkeiten einer “Ja”-Antwort pro Geschlecht.

kinderwunsch |>
  group_by(geschl) |>
  summarize(
    n = n(),
    Ja = sum(kind_d),
    prop_Ja = sum(kind_d) / n
  )
# A tibble: 2 × 4
  geschl        n    Ja prop_Ja
  <fct>     <int> <dbl>   <dbl>
1 maennlich    44    30   0.682
2 weiblich     56    44   0.786

X und Y kategorial

Zuletzt können die Variablen sowohl auf der X- als auch auf der Y-Achse kategorial sein. In diesem Fall wäre es sinnvoll, die gemeinsamen Häufigkeiten grafisch darzustellen. Dafür gibt es die Funktion geom_count().

Als Beispiel wollen wir die gemeinsame Häufigkeitsverteilung der Bildung des Vaters und der Bildung der Mutter betrachten.

p <- beispieldaten |>
  select(starts_with("bildung")) |>
  drop_na() |>
  ggplot(aes(
    x = bildung_vater,
    y = bildung_mutter
  ))

p + geom_count()

geom_count() zählt die gemeinsamen Häufigkeiten der Kategorien der beiden Variablen und stellt diese als Durchmesser der Punkte dar.

Hinweis

Die Häufigkeitstabelle erhalten wir mit der Funktion table().

table(beispieldaten$bildung_vater, beispieldaten$bildung_mutter)
             
              Hauptschule Realschule Abitur Hochschule
  Hauptschule          23         12      2          3
  Realschule           12         55     17         10
  Abitur                5         11     21          4
  Hochschule            3         16      9         65

Beispiele

Wir betrachten nun 2 Übungsbeispiele.

Arbeitszufriedenheit

Im ersten Beispiel laden wir den Arbeitszufriedenheitsdatensatz vom letzten Kapitel nochmals und schauen uns die Grafik genauer an.

honeymoon <- read_delim("data/honeymoon.csv",
  delim = ";"
)
Rows: 10 Columns: 4
── Column specification ────────────────────────────────────────────────────────
Delimiter: ";"
dbl (4): Firma, Anfang, Drei_Monate, Sechs_Monate

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Wiederholung vom letzten Kapitel:
honeymoon_long_bedingung <- honeymoon |>
  mutate(
    Firma = as_factor(Firma),
    ID = row_number()
  ) |>
  pivot_longer(!c(Firma, ID),
    names_to = "Messzeitpunkt",
    values_to = "Arbeitszufriedenheit"
  ) |>
  mutate(Messzeitpunkt = factor(Messzeitpunkt)) |>
  arrange(Firma, ID) |>
  group_by(Firma, ID) |>
  mutate(Personenmittelwert = mean(Arbeitszufriedenheit)) |>
  group_by(Firma, Messzeitpunkt) |>
  summarize(Bedingungsmittelwert = mean(Arbeitszufriedenheit))
`summarise()` has grouped output by 'Firma'. You can override using the
`.groups` argument.
honeymoon_long_bedingung
# A tibble: 6 × 3
# Groups:   Firma [2]
  Firma Messzeitpunkt Bedingungsmittelwert
  <fct> <fct>                        <dbl>
1 1     Anfang                         9  
2 1     Drei_Monate                    5  
3 1     Sechs_Monate                   7  
4 2     Anfang                        10  
5 2     Drei_Monate                   10.8
6 2     Sechs_Monate                  11  

Wir stellen nun anhand eines Liniendiagrams die mittlere Arbeitszufriedenheit über die Messzeitpunkte hinweg dar, und zwar benützen wir als Gruppierungsfaktor die Firma. group = Firma ist hier wichtig, die anderen beiden Argumente, color = Firma und linetype = Firma sind nur aus ästhetischen Gründen da und könnten weggelassen werden.

Vertiefung

Wie weiter oben schon angedeutet, braucht ggplot2 das group Argument, wenn wir ein Liniendiagramm mit einer kategorialen Variable auf der X-Achse erstellen wollen.

p <- honeymoon_long_bedingung |>
  ggplot(aes(
    x = Messzeitpunkt,
    y = Bedingungsmittelwert,
    color = Firma,
    linetype = Firma,
    group = Firma
  ))

p + geom_point(size = 4) +
  geom_line(linewidth = 2)

Liniendiagramme werden oft benutzt, um den zeitlichen Verlauf einer Variablen darzustellen. Dies bedeutet, dass wir auf der X-Achse die Zeit darstellen, wie in diesem Beispiel. Meistens wird Zeit jedoch als kontinuierliche Variable verwendet und nicht, wie hier, als Faktor.

Wide vs. Long: Bildung der Eltern

Anhand des nächsten Beipiels betrachten wir den Unterschied zwischen dem long und dem wide Format. Wir haben, als wir die gemeinsame Häufigkeitsverteilung der Bildung des Vaters und der Mutter dargestellt haben, bildung_vater und bildung_mutter als separate Variablen verwendet, um sie auf separaten Achsen darzustellen. Wir könnten jedoch auch bildung_vater und bildung_mutter als Stufen eines Messwiederholungsfaktors eltern (key) zusammenfassen, und die Bildungsniveaus als Messvariable bildung (value), d.h. als key/value Paar. Dies machen wir, wenn wir bildung als Variable auf einer Achse verwenden wollen und eltern als Gruppierungsvariable.

Dies ist vielleicht nicht ganz einfach zu verstehen, deshalb betrachten wir gleich ein konkretes Beispiel. Wir wollen nun, ähnlich wie oben, die mittlere Schulnote für die verschiedenen Bildungsniveaus der Eltern grafisch darstellen. Diesmal machen wir dies für beide Elternteile. Wir wollen jedoch unterschiedliche Linien für Vater und Mutter. Nun ist es wichtig, dass wir einen long Datensatz bilden.

bildung <- beispieldaten |>
  # Variablen auswählen
  select(Gesamtnote, bildung_vater, bildung_mutter) |>
  # Fehlende Werte ausschliessen
  drop_na() |>
  # wide zu long
  pivot_longer(!Gesamtnote, names_to = "eltern", values_to = "bildung") |>
  # Präfix bildung_ bei eltern-Variable entfernen
  mutate(eltern = str_replace(eltern, ".*_", "")) |>
  # zu Faktoren konvertieren
  mutate(
    eltern = factor(eltern, levels = c("mutter", "vater")),
    bildung = factor(bildung, levels = c(
      "Hauptschule", "Realschule",
      "Abitur", "Hochschule"
    ))
  ) |>
  # Gruppieren: zuerst Eltern, dann Bildungsniveaus
  group_by(eltern, bildung) |>
  # Mittlere Note berechnen
  summarize(Gesamtnote = mean(Gesamtnote))
`summarise()` has grouped output by 'eltern'. You can override using the
`.groups` argument.
bildung
# A tibble: 8 × 3
# Groups:   eltern [2]
  eltern bildung     Gesamtnote
  <fct>  <fct>            <dbl>
1 mutter Hauptschule       4.10
2 mutter Realschule        4.37
3 mutter Abitur            4.51
4 mutter Hochschule        4.73
5 vater  Hauptschule       4.08
6 vater  Realschule        4.38
7 vater  Abitur            4.50
8 vater  Hochschule        4.69
p <- bildung |>
  ggplot(aes(
    x = bildung,
    y = Gesamtnote,
    colour = eltern,
    linetype = eltern,
    group = eltern
  ))

p + geom_line(linewidth = 2) +
  geom_point(size = 4)

Hinweis

An diesem Beispiel wir deutlich, dass ein grosser Teil der Arbeit mit ggplot2 darin besteht, die Daten zuerst ins ‘richtige’ Format zu bringen. Wenn dies getan ist, ist es jedoch relativ einfach, eine Grafik zu erstellen. Dies ist ein nicht zu unterschätzender Vorteil von ggplot2.

5.5 Facets

Bisher haben wir Gruppierungsvariablen dazu benutzt, um unterschiedliche Farben/Formen/Linien für die Kategorien der Gruppierungsvariable innerhalb eines Plots zu erzeugen. Manchmal ist dies jedoch zu unübersichtlich.

Wollen wir zum Beispiel ein Histogram der Schulnoten erstellen, und zwar für jede Stufe der Bildung der Mutter, dann wäre die Grafik völlig überladen.

p <- beispieldaten |>
  select(Gesamtnote, bildung_mutter) |>
  drop_na() |>
  ggplot(mapping = aes(
    x = Gesamtnote,
    fill = bildung_mutter
  ))

p + geom_histogram(
  binwidth = 0.8,
  position = "dodge"
)

Eine offensichtliche Lösung wäre, die Histogramme für die Bildungsniveaus der Mutter in separaten Grafiken darzustellen.

Genau dies können wir mit den Funktionen facet_wrap() und facet_grid() machen.

Mit facet_wrap() erstellen wir so eine Grafik für jede Kategorie der Gruppierungsvariable:

p <- beispieldaten |>
  select(Gesamtnote, bildung_mutter) |>
  drop_na() |>
  ggplot(mapping = aes(
    x = Gesamtnote,
    fill = bildung_mutter
  )) +
  facet_wrap(~bildung_mutter)

p + geom_histogram(binwidth = 0.8)

Hinweis

Das ~ (Tilde) Zeichen bedeutet hier ungefähr: in Abhängigkeit oder als “Funktion” von.

Wenn wir zwei Gruppierungsvariablen haben, können wir mit facet_grid() ein Raster erzeugen.

p <- beispieldaten |>
  select(Gesamtnote, bildung_mutter, bildung_vater) |>
  drop_na() |>
  ggplot(mapping = aes(x = Gesamtnote)) +
  facet_grid(bildung_mutter ~ bildung_vater)

p + geom_histogram(
  binwidth = 0.8,
  fill = "steelblue4"
)

Hier werden die Stufen von bildung_mutter in den Zeilen dargestellt, die Stufen von bildung_vater in den Spalten.

Als zweites Beispiel können wir den Notenschnitt in Abhängigkeit der Bildung der Eltern als Liniendiagram darstellen, und zwar in separaten Plots für Väter und Mütter getrennt. Anhand dieses Beispiels sehen wir, dass wir facet_grid() auch dann verwenden können, wenn wir nur eine Gruppierungsvariable haben, und zwar um die Anzahl Zeilen bzw. Spalten festzulegen.

Wenn wir die Gruppierung in den Zeilen haben wollen, schreiben wir facet_grid(Gruppierungsvariable ~ .), wenn sie in den Spalten wollen, schreiben wir facet_grid(. ~ Gruppierungsvariable). Der Punkt . bedeutet hier, dass wir für die Zeilen/Spalten keine Gruppierungsvariable verwenden.

p <- bildung |>
  ggplot(aes(
    x = bildung,
    y = Gesamtnote,
    group = eltern
  ))

p + geom_line(linewidth = 2, color = "pink") +
  geom_point(size = 4, color = "steelblue3") +
  # Stufen von 'eltern' in die Zeilen
  facet_grid(eltern ~ .)

p <- bildung |>
  ggplot(aes(
    x = bildung,
    y = Gesamtnote,
    group = eltern
  ))

p + geom_line(linewidth = 2, color = "pink") +
  geom_point(size = 4, color = "steelblue3") +
  # Stufen von 'eltern' in die Spalten
  facet_grid(. ~ eltern)

5.6 Farben und Themes

Bisher hat ggplot2 automatisch für uns Farben gewählt, wenn wir Farben für eine Gruppierung verlangt haben. Die Standard Farbpalette ist jedoch für Farbenblinde äusserst schlecht geeignet. Es gibt viele Farbpaletten, welche wir verwenden könnten.

Wir definieren hier jedoch eine eigene, für Farbenblinde geeignete Farbpalette.

palette <- c(
  "#000000", "#E69F00",
  "#56B4E9", "#009E73",
  "#F0E442", "#0072B2",
  "#D55E00", "#CC79A7"
)

Wir definieren hier also einen Vektor von acht Hex Codes. Folglich dürfte unsere Gruppierungsvariable nicht mehr als acht Kategorien haben.

Hinweis

Sie können hier natürlich eine eigene Farbpalette erstellen, indem Sie entweder Hex-Codes oder Farbennamen angeben.

Die Palette verwenden wir so:

  • um Formen auszufüllen, verwenden wir
scale_fill_manual(values = palette)
  • für Linien und Punkte verwenden wir:
scale_colour_manual(values = palette)

Als Beispiel plotten wir nochmals den Zusammenhang zwischen stress_psychisch und leben_gesamt, diesmal mir unserer Farbpalette.

p <- beispieldaten |>
  select(stress_psychisch, leben_gesamt, geschlecht) |>
  drop_na() |>
  ggplot(mapping = aes(
    x = stress_psychisch,
    y = leben_gesamt,
    color = geschlecht,
    shape = geschlecht
  ))

p + geom_jitter(size = 3, alpha = 0.9) +
  scale_colour_manual(values = palette)

Wir könnten die Farben auch so ‘von Hand’ bestimmen:

p + geom_jitter(size = 3, alpha = 0.9) +
  scale_colour_manual(values = c("pink2", "steelblue3"))

Ein weiterer heikler Punkt ist der graue Hintergrund, den ggplot2 automatisch wählt. Diesen können wir am einfachsten ändern, indem wir ein theme definieren. Es gibt zwei solcher themes, welche einen weissen Hintergrund haben: theme_bw() und theme_classic(). Diese unterscheiden sich darin, dass theme_classic() keine grid lines zeichnet, sondern nur die linke und die untere Achse.

p <- beispieldaten |>
  select(stress_psychisch, leben_gesamt, geschlecht) |>
  drop_na() |>
  ggplot(mapping = aes(
    x = stress_psychisch,
    y = leben_gesamt,
    color = geschlecht,
    shape = geschlecht
  ))

p + geom_jitter(size = 3, alpha = 0.9) +
  scale_colour_manual(values = palette) +
  theme_bw()

p <- beispieldaten |>
  select(stress_psychisch, leben_gesamt, geschlecht) |>
  drop_na() |>
  ggplot(mapping = aes(
    x = stress_psychisch,
    y = leben_gesamt,
    color = geschlecht,
    shape = geschlecht
  ))

p + geom_jitter(size = 3, alpha = 0.9) +
  scale_colour_manual(values = palette) +
  theme_classic()

5.7 Beschriftungen

Wir können nun auch noch mit xlab() und ylab() die Beschriftungen der X/Y-Achsen ändern, und mit der Funktion ggtitle() dem Plot einen Titel geben. Mit der Funktion labs() können wir zusätzlich noch den Titel der Legende ändern.

Zuletzt wollen wir auch die Schriftgrösse ändern, da die Standardgrösse oft zu klein erscheint. Dies erreichen wir mit dem Argument base_size = SCHRIFTGRÖSSE) der theme_* Funktionen.

p <- beispieldaten |>
  select(stress_psychisch, leben_gesamt, geschlecht) |>
  drop_na() |>
  ggplot(mapping = aes(
    x = stress_psychisch,
    y = leben_gesamt,
    color = geschlecht,
    shape = geschlecht
  ))

p + geom_jitter(size = 3, alpha = 0.9) +
  scale_colour_manual(values = palette) +
  theme_classic(base_size = 14) +
  ggtitle("Zusammenhang zwischen Stress und Zufriedenheit") +
  xlab("Psychischer Stress") +
  ylab("Zufriedenheit") +
  # Titel der color- und shape-Legende ist "Geschlecht"
  labs(
    color = "Geschlecht",
    shape = "Geschlecht"
  )

5.8 Grafiken speichern

Wenn wir eine schöne Grafik erstellt haben, wollen wir sie natürlich speichern. Dies können wir mit der Funktion ggsave() machen. Die Funktion nimmt als Argumente den Dateinamen, den Namen des Plot Objekts und weitere Eigenschaften, wie die gewünschte Höhe und Breite des Plots. Diese können z.B. in “cm” angebenen werden, mit dem Argument units = "cm". Um die Grafik zu speichern, müssen wir also unser fertiges ggplot2 Objekt einer Variablen zugewiesen haben:

p <- beispieldaten |>
  select(stress_psychisch, leben_gesamt, geschlecht) |>
  drop_na() |>
  ggplot(mapping = aes(
    x = stress_psychisch,
    y = leben_gesamt,
    color = geschlecht,
    shape = geschlecht
  ))

# Wir nennen die Grafik 'my_plot'
my_plot <- p + geom_jitter(size = 3, alpha = 0.9) +
  scale_colour_manual(values = palette) +
  theme_classic(base_size = 14) +
  ggtitle("Zusammenhang zwischen Stress und Zufriedenheit") +
  xlab("Psychischer Stress") +
  ylab("Zufriedenheit") +
  labs(
    color = "Geschlecht",
    shape = "Geschlecht"
  )

my_plot kann nun gespeichert werden:

ggsave(
  filename = "my_plot.png",
  plot = my_plot
)

Die Grafik kann auch in den Formaten eps, ps, tex, pdf, jpeg, tiff, svg und wmf gespeichert werden. Dazu muss lediglich die Endung .png ersetzt werden, beispielsweise durch .pdf. Je nach Anwendungszweck lohnt es sich, auf ein anderes Dateiformat zu setzen. Insbesondere wenn die Grafiken vergrössert werden sollen, lohnt es sich, auf ein Vektorgrafikformat wie .svg oder .wmf zu setzen.

5.9 Übungsaufgabe

In dieser Übungsaufgabe wollen wir die sechs Selbstwirksamkeitsskalen im beispieldaten Datensatz untersuchen. Wir gehen davon aus, dass diese in einem Zusammenhang mit dem Gesamtnotenschnitt stehen. Bevor wir eine multiple Regression rechnen, wollen wir versuchen, Zusammenhänge zwischen diesen Variable grafisch darstellen.

selbstwirksamkeit_wide <- beispieldaten |>
  select(ID, Gesamtnote, starts_with("swk_")) |>
  drop_na()

selbstwirksamkeit_wide
# A tibble: 279 × 8
      ID Gesamtnote swk_neueslernen swk_lernregulation swk_motivation
   <dbl>      <dbl>           <dbl>              <dbl>          <dbl>
 1     2          4            5                  4              4.6 
 2    10          5            5                  4.88           6   
 3    11          4            4.57               5.38           5.4 
 4    12          5            5.33               5.29           4.67
 5    14          4            4.86               4.5            4.6 
 6    15          4            4                  4.38           5.6 
 7    16          5            4.83               6.25           5   
 8    17          4            6.14               5.62           6   
 9    18          4            5.14               4.62           4.4 
10    19          4            5.43               4.62           5.2 
# ℹ 269 more rows
# ℹ 3 more variables: swk_durchsetzung <dbl>, swk_sozialkomp <dbl>,
#   swk_beziehung <dbl>

Versuchen Sie, Zusammenhänge zwischen den sechs Selbstwirksamkeitsskalen zu entdecken, indem Sie diese grafisch darstellen. Versuchen Sie zum Beispiel, für jedes Variablenpaar einen Scatterplot (Punktdiagram) zu erstellen.

p <- selbstwirksamkeit_wide |>
  ggplot()
p + geom_jitter(aes(x = swk_neueslernen, y = swk_durchsetzung), alpha = 0.6, size = 2)

p + geom_jitter(aes(x = swk_neueslernen, y = swk_motivation), alpha = 0.6, size = 2)