Wie wir Grafiken mit ggplot2 erstellen, modifizieren und exportieren
Letzte Änderung am 23. März 2021
Einleitung
Zur Verbreitung von wissenschaftlichen Untersuchungen ist es sinnvoll, Rohdaten oder Ergebnisse von statistischen Analysen übersichtlich darzustellen, um diese besser kommunizieren zu können. Dabei sind Grafiken zumeist die Methode der Wahl. Gute Grafiken sind anschaulich, wirkungsvoll und lassen einen wichtige Informationen auf einen Blick erfassen.
Es gibt drei führende Pakete zum Erstellen von Grafiken in R:
plot()
oder hist()
)Im Rahmen dieses Kapitels werden wir uns auf ggplot2 konzentrieren. Mit diesem Paket haben wir eine große Bandbreite an Möglichkeiten, das Aussehen der Grafiken zu verändern. Außerdem können wir auf eine große Anzahl von verschiedenen Grafiken zurückgreifen wie z.B. den Violinenplot (siehe Abb. unten). Ein Violinenplot zeigt die Dichteverteilung eines metrischen Merkmals in Abhängigkeit eines kategorialen Merkmals.
Zuerst schauen wir uns die grundlegende Funktionsweise von ggplot an. Dann lernen wir, wie wir (bekannte) Grafiken erstellen und modifizieren können. Abschließend erfahren wir, wie wir Grafiken exportieren können. Am Ende befindet sich außerdem eine Auswahl weiterführender Hilfen, die für die Visualisierung der eigenen Daten nützlich sein können.
# Paket installieren ...
install.packages("ggplot2", dependencies=TRUE)
# ... und laden
library(ggplot2)
Um uns die Anwendung von ggplot2 zu erleichtern, können wir ein Cheatsheet in deutsch oder englisch herunterladen.
Auf der R-Studio Website gibt es noch mehr Cheat Sheets, v.a. zu tidyverse
Paketen.
Alternativ finden wir die englischen Cheat Sheets auch direkt in R-Studio. Dazu gehen wir in der Menüleiste am oberen Bildschirmrand auf Help –> Cheatsheets.
Im Rahmen des Kapitels werden wir hauptsächlich mit dem Datensatz ChickWeight
arbeiten, der standardmäßig im Basispaket datasets enthalten ist. Wir laden den Dataframe folgendermaßen in unseren Workspace:
data(ChickWeight)
Die Daten stammen aus einem Experiment, in dem der Einfluss des Futters auf das Wachstum von Küken untersucht wurde.
weight Time Chick Diet
1 42 0 1 1
2 51 2 1 1
3 59 4 1 1
4 64 6 1 1
5 76 8 1 1
6 93 10 1 1
Der Datensatz enthält vier Variablen. Mehr Informationen zu den Variablen finden wir hier.
weight
: Körpergewicht eines Kükens in GrammTime
: Tage seit der Geburt des KükensChick
: Identifikationsnummer des KükensDiet
: Nummer der Futtergruppe (1, 2, 3, 4)
Für den Abschnitt zu Visualisierungsmöglichkeiten bei mehr als drei Variablen nutzen wir zusätzlich noch den Datensatz mpg, welcher im Paket ggplot2 enthalten ist.
Wir laden diesen folgendermaßen in unser Environment:
mpg <- mpg
# A tibble: 6 x 11
manufacturer model displ year cyl trans drv cty hwy fl
<chr> <chr> <dbl> <int> <int> <chr> <chr> <int> <int> <chr>
1 audi a4 1.8 1999 4 auto(l… f 18 29 p
2 audi a4 1.8 1999 4 manual… f 21 29 p
3 audi a4 2 2008 4 manual… f 20 31 p
4 audi a4 2 2008 4 auto(a… f 21 30 p
5 audi a4 2.8 1999 6 auto(l… f 16 26 p
6 audi a4 2.8 1999 6 manual… f 18 26 p
# … with 1 more variable: class <chr>
?mpg
.
Bevor wir uns in ggplot2 vertiefen, wollen wir überprüfen, ob unsere Daten in der benötigten Datenstruktur vorliegen. Außerdem lernen wir einige Webseiten kennen, die uns dabei helfen, uns für eine Art der Visualisierung unserer Daten zu entscheiden.
Bevor es losgehen kann, müssen wir erst einmal sicherstellen, dass unsere Daten auch in adäquater Form vorliegen. Um Grafiken mit ggplot()
erzeugen zu können, müssen alle genutzen Variablen in einem gemeinsamen Dataframe vorliegen.
Das können wir folgendermaßen überprüfen:
is.data.frame(ChickWeight)
[1] TRUE
Falls unser Datensatz nicht als Dataframe vorliegt, könnten wir ihn so umwandeln:
ChickWeight <- as.data.frame(ChickWeight)
In Abhängigkeit der eigenen Fragestellung müssen die Daten im Long- bzw. Wide-Format vorliegen. Schauen wir uns den Unterschied einmal an der Fragestellung, wie sich das Gewicht der Küken (weight
) zu unterschiedlichen Zeitpunkten (Time
) verändert, an.
Wenn wir Time
als Prädiktor für weight
aufnehmen möchten, müssen die Daten im Long-Format vorliegen, damit Time
auch als eigene Variable kodiert ist.
Wenn wir uns hingegen für eine Veränderung des Gewichts von Tag 0 (Time 0
) zu Tag 2 (Time 2
) interessieren, müssen die Daten im Wide-Format vorliegen, damit die Gewichtsdaten zu den Messzeitpunkten in einzelnen Spalten (Variablen) vorliegen.
Wenn wir wissen wollen, wie wir Daten vom Wide- ins Long-Format (oder vice versa) bekommen, dann können wir unser Kapitel dazu anschauen.
Außerdem kann es Probleme geben, wenn nominal- oder ordinalskalierte Variablen (z.B. Diet
) im Datensatz nicht als Faktor vorliegen. Mit str()
können wir uns anschauen, in welchem Daten- bzw. Objekttyp die Variablen eines Dataframes vorliegen.
str(ChickWeight) # überprüfen
Classes 'nfnGroupedData', 'nfGroupedData', 'groupedData' and 'data.frame': 578 obs. of 4 variables:
$ weight: num 42 51 59 64 76 93 106 125 149 171 ...
$ Time : num 0 2 4 6 8 10 12 14 16 18 ...
$ Chick : Ord.factor w/ 50 levels "18"<"16"<"15"<..: 15 15 15 15 15 15 15 15 15 15 ...
$ Diet : Factor w/ 4 levels "1","2","3","4": 1 1 1 1 1 1 1 1 1 1 ...
- attr(*, "formula")=Class 'formula' language weight ~ Time | Chick
.. ..- attr(*, ".Environment")=<environment: R_EmptyEnv>
- attr(*, "outer")=Class 'formula' language ~Diet
.. ..- attr(*, ".Environment")=<environment: R_EmptyEnv>
- attr(*, "labels")=List of 2
..$ x: chr "Time"
..$ y: chr "Body weight"
- attr(*, "units")=List of 2
..$ x: chr "(days)"
..$ y: chr "(gm)"
Mit factor()
können wir einzelne Spalten faktorisieren.
ChickWeight$Diet <- factor(ChickWeight$Diet) # umwandeln
# Diet lag bereits vorher als ungeordneter Faktor vor
Falls wir noch auf der Suche nach einer informativen und ansprechenden Grafik für unsere Daten seid, können wir uns einen Überblick über geeignete Grafiken auf from Data to Viz verschaffen.
Für mehr Inspiration (sowie teilweise auch den R-Code) können wir auf den folgenden zwei Seiten nachschauen:
Auf R Graph Gallery finden wir verschiedene Grafiken nach Oberbegriffen (z.B. Zusammenhänge, Korrelation) sortiert und unterteilt in bestimmte Arten von Grafiken (z.B. Histogramm, Korrelogramm). Wenn wir auf die jeweilige Grafik klicken, kommen wir auf eine Seite, auf der es eine Definition und mehrere Beispielgrafiken, teils mit R-Code, gibt. Wir finden hier auch nicht nur Grafiken, die mit ggplot erstellt wurden.
Auf Top 50 ggplot2 Visualizations - The Master List finden wir auch eine gute Übersicht möglicher ggplot-Grafiken für verschiedene Anliegen (z.B. Korrelation, Variation, Veränderung) mit den dazugehörigen R-Codes. Wir finden hier auch sehr einzigartige Visualisierungen wie z.B. Dendrogramme.
Das gg in ggplot()
steht für grammar of graphics. ggplot strebt einen intuitiven Ansatz zur Erstellung von Grafiken an. Die essenziellen drei Komponenten, die wir zur Erstellung jeder Grafik benötigen, sind:
data
.aes()
festgelegt. Dazu gehören u.a. die x- und y-Dimensionen, Farben und Größe.geom()
festgelegt. Die Formen, mit denen die Daten dargestellt werden, sind z.B. Punkte, Linien, Balken.Die Besonderheit an ggplot ist, dass wir verschiedene Ebenen übereinander legen. Diese Ebenen verbinden wir syntaktisch jeweils mit einem +
. Das Grundgerüst ist dabei das Koordinatensystem (1. Ebene) und die Art der Grafik, die wir mittels geometrischer Objekt festlegen (2. Ebene). Zusätzlich können wir diese beiden Ebenen modifizieren (z.B. Farben der geometrischen Objekte ändern) und ebenso neue Ebenen ergänzen (z.B. Beschriftung).
Die Ebenen können wir auch als Teilfunktionen begreifen, die eine Grafik konstituieren. Was wir z.B. bei plot(..., main="...")
in einer Funktion realisieren können (das Koordinatensystem, die Art der Grafik - ein Streudiagramm - und die Beschriftung) machen wir in ggplot mit mehreren Teilfunktionen (ggplot() + geom_point() + ggtitle()
).
Das Koordinatensystem spezifizieren wir mit ggplot()
. Dem Parameter data
übergeben wir den Datensatz (1). Das ästhetische Mapping (2) legen wir mit aes()
fest. Diesem geben wir die Namen der Variablen, die auf der x- und y-Achse dargestellt werden sollen.
Wie bei anderen Funktionen können wir die Parameterbezeichnung data
, x
und y
weglassen und nur die Argumente (d.h. die Namen des Datensatzes bzw. der Variablen) angeben. Bei x und y müssen wir dann aber unbedingt die Reihenfolge einhalten: ggplot(ChickWeight, aes(Time, weight))
.
Bisher wurde nur das Koordinatensystem mit den jeweiligen Achsengrenzen der Variablen erstellt. Wenn wir die Daten nun plotten möchten, müssen wir noch festlegen, wie die Daten dargestellt werden sollen (z.B. die Häufigkeit als Balken, die bivariate Verteilung als Punkte). Dazu nutzten wir geometrische Objekte(3), die wir mit den geom
-Funktionen festlegen (z.B. geom_line()
für Linien oder geom_bar()
für Balken). Damit ergänzen wir den Plot um eine neue Ebene.
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_point()
Nachdem wir uns nun mit dem grundlegenden Aufbau von ggplot()
vertraut gemacht haben, schauen wir uns nun an, wie wir spezifische Arten von Grafiken erstellen.
Zuerst schauen wir uns an, wie wir die Häufigkeitsverteilung einer Variablen visualisieren können.
Hierfür nehmen wir die Variable Diet
. Diese kodiert die Gruppen, in denen das Futter der Küken variiert wurde.
Später erfahren wir noch, wie wir die Reihenfolge und Benennung von kategorialen Variablen ändern können.
Um ein Balkendiagramm zu erzeugen, nutzen wir geom_bar()
. Standardmäßig werden mit geom_bar()
absolute Häufigkeiten (count) geplottet.
Wir können folgendermaßen auch die relativen Häufigkeiten (prob) plotten:
Mit + coord_flip()
können wir das Diagramm um 90° nach rechts kippen. Jetzt ist auf der x-Achse die Häufigkeit und auf der y-Achse die Gruppierung abgebildet.
ggplot(data=ChickWeight, aes(x=Diet)) +
geom_bar() +
coord_flip()
Wir können auch sogenannte gestapelte Balkendiagramme (stacked bar plots) anfertigen.
Dazu müssen erst einen neuen Dataframe erzeugen, in welchem die Gruppennamen sowie die (absoluten) Häufigkeiten der Variablen je als neue Variable gespeichert sind.
# Häufigkeitstabelle von Diet erstellen und in Dataframe konvertieren:
freq_Diet <-as.data.frame(table(ChickWeight$Diet))
# (optional) Spalten umbenennen:
colnames(freq_Diet) <- c("Futtergruppe", "Häufigkeit")
Mit diesem Dataframe erstellen wir nun ein gestapeltes Balkendiagramm:
Mehr Informationen zur Erstellung von gestapelten sowie auch gruppierten Balkendiagrammen finden wir hier. Beide Grafiken eignen sich auch gut, um Gruppierungskombinationen mit noch einer weitere kategorialen Variablen darzustellen.
Um ein Kreisdiagramm zu erstellen, müssen wir, analog zur Erstellung von gestapelten Balkendiagrammen, zuerst einen neuen Dataframe erzeugen, in welchem die Namen und (absoluten) Häufigkeiten der Gruppen der Variablen als neue Variablen vorhanden sind.
# Häufigkeitstabelle von Diet erstellen und in Dataframe konvertieren:
freq_Diet <-as.data.frame(table(ChickWeight$Diet))
# (optional) Spalten umbenennen:
colnames(freq_Diet) <- c("Futtergruppe", "Häufigkeit")
Auch das weitere Vorgehen überschneidet sich weitest mit dem der Erstellung eines gestapelten Balkendiagrammes. Wir müssen lediglich noch + coord_polar("y")
ergänzen, damit die Anteile der Häufigkeiten in Polarkoordinaten überführt werden.
ggplot(data=freq_Diet, aes(x="", y=Häufigkeit, fill=Futtergruppe)) +
geom_bar(stat = "identity") +
coord_polar("y")
Auf dieser Seite finden wir weitere Hilfe zur Erstellung und Modifikation von Kreisdiagrammen.
Beispielhaft schauen wir uns hierfür das Gewicht der Küken (weight
) an.
Mit Hilfe von Histogrammen können wir uns die Verteilung einer metrischen Variablen anschauen. Um ein Histogramm zu erstellen, nutzen wir geom_histogram()
.
Bevor wir eine metrische Variable zum Zwecke der grafischen Darstellung kategorisieren, sollten wir uns Gedanken über die Anzahl und Breite der Kategorien machen, in die wir die Daten einteilen möchten. Es ist meist nicht sinnvoll, die Häufigkeiten der Rohwerte von metrischen Variablen zu plotten, weil identische Messwerte selten vorliegen. Daher ist es sinnvoll, die Rohdaten zu gruppieren. Dazu müssen wir uns Gedanken über die Intervallgröße dieser Kategorien machen (“Problem der Kategorisierung”). Durch Vergrößerung gehen (relevante) Informationen verloren; durch Verkleinerung bleiben (zu viele) idiosynkratische Merkmale erhalten.
Prinzipiell gilt:
Schauen wir uns die ungefähre Anzahl der Kategorien nach der Sturges Regel an. Dazu nutzen wir die Funktion nclass.Sturges(1:Stichprobengröße).
# Gibt es fehlende Werte?
anyNA(ChickWeight$weight)
[1] FALSE
## wenn nein:
nclass.Sturges(1:nrow(ChickWeight))
[1] 11
## wenn ja:
# nclass.Sturges(1:table(is.na(ChickWeight$weight))[[2]])
# table(is.na(ChickWeight$weight))[[2]] gibt uns die Anzahl der vorhanden werte
Nach Sturges Regel sollten wir für weight
in etwa 11 Kategorien bilden.
min(ChickWeight$weight) # kleinster Wert
[1] 35
max(ChickWeight$weight) # größter Wert
[1] 373
# Variationsbreite (Streuung) = größter minus kleinster Wert:
max(ChickWeight$weight) - min(ChickWeight$weight)
[1] 338
# bei Vorhandensein von fehlenden Werten:
# min(..., na.rm=TRUE) bzw. max(..., na.rm=TRUE)
Es ist sinnvoll, zuerst nach Sturges Regel eine Anzahl an Bins zu errechnen, und diese dann ggf. in Abhängigkeit der Variationsbreite des Merkmals zu verändern.
Generell sollten wir nicht mehr als 20 Kategorien bilden. Diese sollten im Regelfall auch ungefähr die gleiche Breite aufweisen.
Wir erstellen ein Histogramm von weight
mit den empfohlenen 11 Kategorien, welche wir mit dem Argument bins
erstellen.
ggplot(data=ChickWeight, aes(x=weight)) +
geom_histogram(bins=11)
Grundsätzlich ist es, sowohl in base graphics als auch in ggplot, problematisch, den wahren Range (d.h. die Variationsbreite) einer Variablen darzustellen. Ohne explizite Eingabe der Grenzen werden diese leider nicht berücksichtigt. Das kann zu einem falschen Eindruck der Variationsbreite der Variablen führen.
Dieses Problem demonstrieren wir einmal an obigem Beispiel.
Mit geom_vline(xintercept)
erstellen wir zwei vertikale Linien, die den kleinsten bzw. größten beobachteten Wert von weight
markieren. Diese nutzen wir zur Veranschaulichung der Problematik.
ggplot(data=ChickWeight, aes(x=weight)) +
geom_histogram(bins=11) +
geom_vline(xintercept=min(ChickWeight$weight)) + # kleinster Wert
geom_vline(xintercept=max(ChickWeight$weight)) # größter Wert
Wie wir sehen haben wir zwar die gewünschten 11 Kategorien, aber diese sind leider so eingeteilt, dass die äußeren Kategorien über den kleinsten und größten beobachteten Wert von weight
hinausgehen (d.h. sie sind zu groß).
Um das Problem in den Griff zu bekommen, nutzen wir das Argument breaks
, mit dem wir die Kategoriengrenzen manuell festlegen. Diesem übergeben wir die Funktion seq(from, to, by)
, die uns eine reguläre Sequenz erstellt (damit wir nicht alle Grenzen einzeln eingeben müssen). Auch hier wollen wir wieder die empfohlenen 11 Kategorien haben, daher teilen wir den Range (373-35) von weight
durch 11.
ggplot(data=ChickWeight, aes(x=weight)) +
geom_histogram(breaks=seq(35, 373, (373-35)/11)) +
geom_vline(xintercept=min(ChickWeight$weight)) + # kleinster Wert
geom_vline(xintercept=max(ChickWeight$weight)) # größter Wert
Wie wir an den vertikalen Linien sehen, hält die Breite der Kategorien insgesamt jetzt den Range von weight
ein.
Weiterführend finden wir zusätzliche Modifikationen für Histogramme auf dieser Seite.
Die Dichteverteilung einer kontinuierlichen Variablen können wir mit geom_density()
einzeichnen. Per Default wird dafür die Gaußsche Dichtefunktion (kernel = "gaussian"
) genutzt.
ggplot(data=ChickWeight, aes(x=weight)) + geom_density()
Nun schauen wir uns die grafische Darstellung von Zusammenhängen zwischen zwei Variablen an.
Später erfahren wir noch, wie wir die Reihenfolge und Benennung von kategorialen Variablen ändern können.
Wenn wir uns die Merkmalsverteilung einer metrischen Variable in Abhängigkeit einer kategorialen Variablen anschauen möchten, können wir Boxplots nutzen.
In Boxplots werden mehrere deskriptiv-statistische Kennwerte dargestellt:
Einen Boxplot erhalten wir mit geom_boxplot()
. Wenn wir fehlende Werte in unseren Daten haben, müssen wir noch das Argument na.rm = TRUE
ergänzen, welches diese (aus der Grafik) entfernt.
Wir schauen uns das Gewicht der Küken (weight
) zum zweiten Messzeitpunkt (Time 2
) in Abhängigkeit der Fütterung (Diet
) an.
ggplot(data=ChickWeight[ChickWeight$Time == 2,], aes(x=Diet, y=weight)) +
geom_boxplot()
ggplot()
auswählen?
Wenn wir nicht alle Daten, sondern nur solche, auf die bestimmte Bedingungen zutreffen, auswählen wollen, können wir dazu die Indexierung mittels der eckigen Klammern []
nutzen.
Für unser Beispiel wollen wir uns nur die Daten des zweiten Messzeitpunkts (Time 2
) anschauen. Formal würde (nur) die Bedingung so aussehen: ChickWeight$Time == 2
. Diese wollen wir noch auf unseren Dataframe anwenden; dazu nutzen wir die eckigen Klammern: ChickWeight[ChickWeight$Time == 2,]
.
Mit dem Komma legen wir fest, welche der beiden Dimensionen unseres Dataframes wir meinen (Zeile, Spalte). Weil wir Fälle mit bestimmten Ausprägungen (Bedingungen) auswählen wollen, setzen wir das Komma am Ende.
Zu beachten ist außerdem, dass wir die Indexierung bereits in der grundlegenden Ebene, d.h. in ggplot(data, ...)
, festlegen müssen. Zu Beginn haben wir gelernt, dass wir durch die Übergabe des Dataframes an ggplot(data, ...)
das grundlegende Koordinatensystem erstellen und damit die Anzahl der Datenpunkte (Beobachtungen) festlegen. Folglich würde es nicht funktionieren, die Selektion erst in einer späteren Ebene vorzunehmen, weil die an Anzahl der Datenpunkte sich unterscheiden würde.
Der Violinenplot ist eine Variante von Boxplots, in dem die Dichtefunktion eines metrischen Merkmals grafisch dargestellt wird. Diese wird an der Senkrechten zur x-Achse gespiegelt.
Um die Grafiken mit dem Boxplot aus dem letzten Abschnitt vergleichen zu können, schauen wir uns hier ebenfalls das Gewicht der Küken (weight
) zum zweiten Messzeitpunkt (Time 2
) in Abhängigkeit der Diät (Diet
) an.
Generell können wir den Violinenplot mit geom_violin()
erstellen. Zusätzlich gibt es hier einen sehr wichtigen Parameter: scale
. Mit diesem legen wir fest, ob die einzelnen “Violinen” in der gleichen Größe (area
; voreingestellt) oder in Größen proportional zur Anzahl der Beobachtungen in jeder Gruppe (count
) dargestellt werden sollen.
Wir schauen uns unsere Daten einmal in beiden Modi an.
ggplot(data= ChickWeight[ChickWeight$Time == 2,], aes(x=Diet, y=weight)) +
geom_violin(scale = "area")
ggplot(data=ChickWeight[ChickWeight$Time == 2,], aes(x=Diet, y=weight)) +
geom_violin(scale = "count")
Wir sehen, dass die Größen derselben “Violinen” sich in den beiden Grafiken unterscheiden. Das liegt daran, dass bei ersterer Grafik (scale=area
) die Anzahl der Beobachtungen pro Gruppe unbeachtet bleibt, während sie in zweiter Grafik (scale=count
) über die relative Größe der “Violinen” visualisiert wird. Wenn wir gleich große Gruppen hätten, würden wir keinen Unterschied zwischen den beiden Grafiken erkennen.
Im Folgenden schauen wir uns die finalen (Time 21
) Mittelwerte des Gewichts der Küken (weight
) in den einzelnen Futtergruppen (Diet
) an. Dabei wollen wir außerdem die 95%-Konfidenzintervalle für die einzelnen Mittelwerte einzeichnen.
Wenn wir aggregierte Daten einer statistischen Analyse (z.B. Gruppenmittelwerte bei einem \(t\)-Test) darstellen möchten, ist ein weit verbeiteter Ansatz, Balkendiagramme mit Fehlerbalken zu nutzen.
Das Problem bei diesem Ansatz ist, dass Personen dazu neigen, die Werte (innerhalb des 95% Konfidenzintervalls), die innerhalb der Balken liegen, als statistisch wahrscheinlicher wahrzunehmen als jene, die außerhalb der Balken liegen. Dieses Phänomen ist als Within-the-bar Bias bekannt. Es ist daher sinnvoll, für die Darstellung von Mittelwerten keine Balkendiagramme zu nutzen, sondern auf geeignetere Visualisierungen zurückzugreifen.
Beispielsweise vermitteln einzelne Punkte (für die Mittelwerte) mit Fehlerbalken die relevanten Informationen besser.
Generell um statistische Kennwerte zu ergänzen, nutzen wir die Funktion stat_summary()
(mehr dazu hier). Mit fun.data="mean_cl_normal"
bekommen wir Mittelwerte mit 95% Konfindenzintervallen.
ggplot(data=ChickWeight[ChickWeight$Time == 21,], aes(x=Diet, y=weight)) +
stat_summary(fun.data="mean_cl_normal")
Wenn wir zusätzlich noch unsere Informationsdichte erhöhen wollen, d.h. nicht nur den Mittelwert, sondern die Verteilung in den einzelnen Gruppen visualisieren wollen, können wir Violinenplots integrieren.
ggplot(data=ChickWeight[ChickWeight$Time == 21,], aes(x=Diet, y=weight)) +
geom_violin(scale="count") + stat_summary(fun.data="mean_cl_normal")
# Reihenfolge beachten! erst Violinenplot, dann Mittelwerte darüber
Mit Streudiagrammen (Scatterplots) können wir Wertepaare zweier kontinuierlicher Variablen grafisch darstellen. Das machen wir mit geom_point()
.
Wir schauen uns im Folgenden den Zusammenhang von Zeit seit dem Schlüpfen (Time
) und dem Gewicht in Gramm (weight
) der Küken an.
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_point()
Wenn wir sehr große Datensätze haben, könnten wir das Problem haben, dass wir individuelle Daten optisch nicht mehr gut unterscheiden können, weil viele Datenpunkte übereinander liegen. Dieses Problem nennt man auch Overplotting. Im Folgenden schauen wir uns einige Möglichkeiten an, wie wir das Problem beheben können.
Hierfür schauen wir uns wieder den Zusammenhang von Zeit seit dem Schlüpfen (Time
) und dem Gewicht in Gramm (weight
) der Küken an.
Wir können die Formen verkleinern mit dem Parameter size
(Breite der Linie in mm), …
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_point(size = 0.1)
… die Form ändern mit dem Parameter shape
, …
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_point(shape = 1)
… oder die Form transparent machen mit dem Parameter alpha
(0 < alpha
< 1).
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_point(alpha = 0.1)
“Jittern” heißt, dass wir etwas zufälliges Rauschen einfügen, damit die Datenpunkte etwas voneinander abweichen.
Weil wir hiermit aber einen falschen Eindruck von den Daten vermitteln könnten, sollten wir die Verwendung von gejitterten Daten immer kennzeichnen.
Per default werden die Punkte in 80% der Breite der implizierten Bins (z.B. der Bin von Time 0
) geplottet, sodass die Bins optisch noch gut voneinander zu trennen sind. Mit dem Parameter width
können wir die Breite anpassen.
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_jitter()
# mehr (aber zu viel) Variation:
ggplot(data=ChickWeight, aes(x=Time, y=weight)) + geom_jitter(width=1)
Es gibt noch weitere Möglichkeiten Overplotting zu vermeiden. Auf dieser Seite finden wir noch andere Beispiele sowie ihre Umsetzung in R.
Wir können unsere bivariaten Daten zusätzlich noch durch eine Funktion beschreiben lassen. Häufig nutzen wir dafür Regressionsmodelle.
Die Regressionsgerade eines einfachen linearen Regressionsmodells können wir mit geom_smooth(method="lm")
über unser Streudiagramm legen.
Per default wird nicht nur eine Regressionsgerade eingezeichnet, sondern auch das 95%-Konfidenzintervall um die Gerade gelegt.
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_point() +
geom_smooth(method="lm")
Mit der Funktion ggPredict()
aus dem Paket ggiraphExtra können wir interaktive Streudiagramme mit Regressionsgeraden (von einfachen und multiplen linearen Regressionsmodellen) erstellen. Hierfür benötigen wir die Basisfunktion ggplot()
gar nicht (aber ggiraphExtra baut auf ggplot2 auf). Allerdings müssen wir zuerst manuell mit lm()
die Regression rechnen und das Ergebnisobjekt an ggPredict()
übergeben.
Im Folgenden schauen wir uns an, wie wir interaktive Streudiagramme mit einer Regressionsgeraden (eines einfachen linearen Regressionsmodells) erstellen können.
Weitere Hilfe zu ggPredict()
, z.B. wie wir gruppenspezifische Ergebnisse einer multiplen linearen Regression mit Interaktion oder einer logistischen Regression visualisieren können, findet wir hier.
Interaktiv bedeutet, dass wir in den Plot reinzoomen, uns die Funktion der Regressionsgerade sowie die ID und die Prädiktor- und Kriteriumswerte der Personen anzeigen lassen können. Um den Plot interaktiv zu machen, müssen wir interactive=TRUE
festlegen.
Auch hier können wir ein 95%-iges Konfidenzintervall um die Regressionsgerade legen, indem wir se=TRUE
spezifizieren. Per Default ist hier (im Gegensatz zu geom_smooth
) kein Konfidenzintervall eingezeichnet. Die Visualisierung der (Un)Genauigkeit unseres Regressionsmodells ist allerdings zu empfehlen.
# install.packages("ggiraphExtra", dependencies=TRUE)
# Regression rechnen:
reg_ggP <- lm(formula = weight ~ Time, data = ChickWeight)
# Ergebnisobjekt plotten
library(ggiraphExtra)
ggPredict(reg_ggP, se = TRUE, interactive = TRUE)
ggPredict()
teilweise noch fehleranfällig, weil von ggiraphExtra
erst eine Beta-Version vorliegt (Stand: Version 0.2.9). Es kann u.a. zu Problemen kommen, wenn man ordinalskalierte (ordered factors) oder nominalskalierte Variablen (factors) ins Modell aufnimmt. Trotzdem ist das interaktive Streudiagramm ein sinnvolles Feature, z.B. für Ergebnispräsentation in html-Dokumenten. Man sollte die Weiterentwicklung des Pakets ggiraphExtra also verfolgen.
Wie wir bis hier gesehen haben, können wir bis zu zwei Variablen sehr gut in einer Grafik visualisieren. Wenn wir aber mehr (kategoriale) Variablen aufnehmen wollen, müssen wir darauf achten, dass die zusätzlichen Informationen nicht zu Lasten der Verständlichkeit der Grafik sind.
Mit Facetten erstellen wir eine Matrix aus (Unter)Grafiken einer Art für verschiedene Gruppen. Mit einem Alluvial Plot können wir die Häufigkeiten der Zugehörigkeit zu verschiedenen Gruppen visualisieren.
Beide Grafiken können genutzt werden, wenn wir Variablen visualisieren wollen, von denen mindestens eine kategorial ist. Wir können mit ihnen mehr als drei Variablen visualisieren, wenn alle (Alluvial Plot) bzw. alle bis auf maximal zwei (Facetten Plots) Variablen kategorial sind. Werte kontinuierliche Variablen können wir auch in Kategorien einteilen um sie hier zu nutzen.
Im Folgenden nutzen wir auch den Datensatz mpg, weil dieser mehr kategoriale Varialen enhält als ChickWeight.
Wenn wir uns eine Art von Grafik (z.B. Streudiagramm) in Abhängigkeit einer kategorialen Variablen (d.h. für verschiedene Gruppen) separat anschauen wollen, können wir + facet_grid()
nutzen. Mit dieser Funktion bekommen wir “Facetten”, d.h. (Unter)Grafiken für jede Ausprägung der kategorialen Variablen.
Wir können außerdem entscheiden, wie die Facetten angeordnet sein sollen:
Wir schauen uns die Gewichtszunahme (weight
) über die Zeit (Time
) getrennt für die einzelnen Experimentalgruppen (Diet
) an.
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_point() +
facet_grid(Diet ~ .) # Zeilen
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_point() +
facet_grid( ~ Diet) # Spalten
Wir können auch Facetten für mehrere kategoriale Variablen erstellen. Das Produkt der Anzahl der Ausprägungen der Variablen gibt die Menge der (Unter)Grafiken an (z.B. drei Variablen mit jeweils zwei Ausprägungen: 2 x 2 x 2 = 8 Grafiken). Wenn wir mehr als zwei Variablen haben, werden Variablen, die zusammen in einer Zeile bzw. einer Spalte gestaffelt werden, mit einem +
verbunden. Damit die einzelnen Grafiken aber noch übersichtlich bleiben, sollten wir ihre Anzahl tendenziell gering halten.
Wir schauen uns aus dem Datensatz mpg Histogramme des Hubraums (displ
) der Autos an. Die Histogramme sind nach Jahr (year
), Antrieb (drv
) und Brennstofftrieb (fl
) gestaffelt.
ggplot(data=mpg, aes(x=displ)) +
geom_histogram() +
facet_grid(fl ~ year + drv) # Zeilen ~ Spalten
Weitere Informationen zu facet_grid()
, z.B. wie man hier die Achsengrenzen und -beschriftung anpassen kann, befinden sich auf dem Cheat Sheet.
Mehr Informationen dazu, wie wir nur Plots für einige Gruppen erstellen, erhalten wir später.
Alluvial Plots sind eine Art von Flussdiagrammen, die die Zugehörigkeit zu mehreren Gruppierungen visualisieren. Sie stellen somit auch eine grafische Alternative zu Kontingenztabellen dar.
Wir wollen das Gewicht der Küken zum ersten und letzten Messzeitpunkt (Time
) in Abhängigkeit der Fütterung (Diet
) visualisieren. Dazu werden wir die Werte der metrischen Variablen weight
in zwei Gruppen aufteilen: größer bzw. kleiner als der Mittelwert.
Für die Visualisierung längsschittlicher Daten müssen wir die folgenden Schritte durchlaufen: Zuerst wählen wir die relevanten Fälle, d.h. jene zum ersten und letzten Messzeitpunkt, aus und speichern sie in einem neuen Dataframe (optional; nur wenn wir nicht alle Zeitpunkte visualisieren wollen). Diesen wandeln wir dann ins Wide-Format um. Danach erstellen wir eine neue Variable, die kodiert, ob das Gewicht eines Kükens zu einem gewissen Zeitpunkt kleiner oder größer als der Mittelwert ist. Zuletzt entfernen die nicht länger benötigten Variablen aus dem Dataframe.
library(dplyr)
# neuer Datensatz nur mit Werten von erster und letzter Messung:
copy <- filter(ChickWeight, Time == 0 | Time == 21)
# ins Wide-Format bringen:
library(tidyr)
cont_Chick <- spread(copy, key="Time", value="weight")
# Die Variablen 0 und 21 wurden erzeugt. Diese müssen wir bei der Indexierung
# immer mit ".." umschließen, da sie sonst nicht als solche erkannt werden.
# für beide Messzeitpunkte eine neue Variable erstellen:
cont_Chick$t0 <- case_when(cont_Chick$"0" < mean(cont_Chick$"0", na.rm=TRUE) ~ "kleiner",
cont_Chick$"0" > mean(cont_Chick$"0", na.rm=TRUE) ~ "größer")
cont_Chick$t21 <- case_when(cont_Chick$"21" < mean(cont_Chick$"21", na.rm=TRUE) ~ "kleiner",
cont_Chick$"21" > mean(cont_Chick$"21", na.rm=TRUE) ~ "größer")
# nicht mehr benötigte Variablen raus:
cont_Chick <- select(cont_Chick, -"0", -"21", -Chick)
# Die ID-Variable "Chick" benötigen wir nicht mehr und sie würde uns die
# im Anschluss erstellte Kontingenztabelle unnötig erweitern.
Für jeden Alluvial Plot (egal ob längsschnittliche oder querschnittliche Daten genutzt wurden) müssen wir die Daten in eine Kontingenztabelle überführen (damit die Häufigkeiten der Gruppierungskombinationen explizit in einer neuen Spalte gespeichert werden) und anschließend wieder in einen Dataframe umwandeln.
# Kontingenztabelle erstellen
cont_Chick <- table(cont_Chick)
# in Dataframe umwandeln
cont_Chick <- as.data.frame(cont_Chick)
Um besser zu verstehen, warum das sein muss, können wir uns den Datensatz vor und nach der Umwandlung anschauen.
Vor der Umwandlung steht jede Zeile für eine Beobachtung (d.h. ein Küken).
Diet | t0 | t21 |
---|---|---|
1 | größer | kleiner |
1 | kleiner | kleiner |
1 | größer | kleiner |
1 | größer | kleiner |
1 | kleiner | größer |
1 | kleiner | kleiner |
1 | kleiner | größer |
1 | größer | NA |
1 | größer | kleiner |
1 | kleiner | kleiner |
1 | größer | kleiner |
1 | kleiner | kleiner |
1 | kleiner | kleiner |
1 | kleiner | größer |
1 | kleiner | NA |
1 | kleiner | NA |
1 | größer | kleiner |
1 | kleiner | NA |
1 | größer | kleiner |
1 | kleiner | kleiner |
2 | kleiner | größer |
2 | kleiner | kleiner |
2 | größer | kleiner |
2 | größer | kleiner |
2 | kleiner | größer |
2 | größer | größer |
2 | kleiner | kleiner |
2 | kleiner | größer |
2 | kleiner | größer |
2 | größer | kleiner |
3 | größer | größer |
3 | kleiner | größer |
3 | kleiner | kleiner |
3 | kleiner | größer |
3 | kleiner | größer |
3 | kleiner | größer |
3 | kleiner | kleiner |
3 | kleiner | größer |
3 | größer | größer |
3 | kleiner | größer |
4 | größer | kleiner |
4 | größer | größer |
4 | größer | kleiner |
4 | größer | NA |
4 | kleiner | kleiner |
4 | kleiner | größer |
4 | kleiner | kleiner |
4 | kleiner | größer |
4 | kleiner | größer |
4 | kleiner | größer |
Nach der Umwandlung (in eine Kontingenztabelle und wieder zurück in einen Dataframe) steht in jeder Zeile eine Kombination (welche Futtergruppe Diet
und jeweils kleiner oder größer zum ersten und zum zweiten Messzeitpunkt) und die Anzahl der Beobachtungen.
Diet | t0 | t21 | Freq |
---|---|---|---|
1 | größer | größer | 0 |
2 | größer | größer | 1 |
3 | größer | größer | 2 |
4 | größer | größer | 1 |
1 | kleiner | größer | 3 |
2 | kleiner | größer | 4 |
3 | kleiner | größer | 6 |
4 | kleiner | größer | 4 |
1 | größer | kleiner | 7 |
2 | größer | kleiner | 3 |
3 | größer | kleiner | 0 |
4 | größer | kleiner | 2 |
1 | kleiner | kleiner | 6 |
2 | kleiner | kleiner | 2 |
3 | kleiner | kleiner | 2 |
4 | kleiner | kleiner | 2 |
Bevor wir den Alluvial Plot erstellen, laden wir das benötigte Paket ggalluvial und überprüfen, ob unser neu erstellter Dataframe auch wirklich im korrekten Alluvial Format vorliegt.
library(ggalluvial)
is_alluvia_form(cont_Chick)
[1] TRUE
Es ist zu empfehlen, den nachfolgenden Code für die eigenen Daten größtenteils zu kopieren. Lediglich in Zeilen (a) und (d) müssen die Argumente von fill
bzw. limit
angepasst werden.
ggplot(data=cont_Chick, aes(y=Freq, axis1=t0, axis2=t21)) +
# (a) grundlegender Alluvial Plot:
geom_alluvium(aes(fill=Diet)) +
# (b) Balken zur Visualisierung der Häufigkeiten in t0 und t21:
geom_stratum(fill="white", width=1/12) +
# (c) Einfügen der Gruppennamen von t0 und t21:
geom_label(stat = "stratum", aes(label=after_stat(stratum))) +
# (d) Einfügen bzw. Ändern der Benennung der Variablen t0 und 21:
scale_x_discrete(limits = c("erste Messung", "letzte Messung"),
# (e) Verringern der Fläche außerhalb der (äußersten) Strata:
expand = c(.05, .05))
Um den Plot (und den Code) besser zu verstehen, werden wir noch einige wichtige Begriffe erläutern:
t0
und t21
)t0
und t21
jeweils größer
und kleiner
)Diet
)Die verschiedenen Strata zeigen uns, wie groß die Anteile an Küken sind, die ihr Gewicht gehalten oder verändert haben (jeweils im Vergleich zum Mittelwert zum jeweiligen Zeitpunkt). Wir können auch vergleichen, ob sich die Trends in den Gruppen von Diet
unterscheiden z.B. hat Gruppe 3 den größten Zuwachs bekommen (erste Messung: kleiner; letzte Messung: größer).
Mehr Informationen und weitere Beispiele für Alluvial Plots finden wir auch in der Dokumentation des Pakets.
Im folgenden Abschnitt werden noch weitere Erweiterungen vorgestellt z.B. wie wir einen Titel und andee Achsenbeschriftungen einfügen können.
Nachfolgend schauen wir uns einige Möglichkeiten der Modifikation von Grafiken an. Als Beispiele nutzen wir dafür die bisher erstellten Grafiken.
Wenn wir in der R-Dokumentation nach ggplot2-specs suchen, finden wir eine Übersicht der ästhetischen Spezifikationen wie z.B. Farben, Linientypen, Punktformen, Schriftarten, Textausrichtung etc.
Nachfolgend schauen wir an, wie wir bestimmte Elemente bzw. Teile von Elementen einer Grafik farblich hervorheben können und welche Möglichkeiten es gibt (einzelne) Farben und Farbpaletten zu nutzen.
Um unsere Farbwahl auch für Menschen mit verschiedenen Sehschwächen geeignet zu gestalten, können wir verschiedene Farbwahrnehmungen mit Coblis simulieren.
Es gibt zwei Parameter, mit denen wir jeweils festlegen können, ob Elemente farblich umrandet und/oder komplett ausgefüllt werden sollen.
col
, color
oder colour
: farbliche Umrandung eines Elementscol
, color
oder colour
). Es gibt aber auch viele Funktionen, in denen alle Parameter funktionieren (und dasselbe bewirken). Genauere Informationen erhalten wir in der Dokumentation der jeweiligen Funktion.fill
: farbliche Füllung eines ElementsWeil col
und fill
separate Parameter sind, können wir auch beide gleichzeitig nutzen, um sowohl die Umrandung als auch die Ausfüllung eines Elements (geom) verschieden einzufärben.
Allerdings gibt es Elemente (geoms), die davon ausgenommen sind. Bei Linien (z.B. geom_line()
) und Text (z.B. geom_text()
) können wir nur col
nutzen. Bei Punkten (z.B. geom_point()
) gibt es einige Formen (21-24, siehe unten), die col
und fill
nutzen können. Alle anderen Formen können auch nur col
nutzen. Die Punktform legen wir mit dem Argument shape
fest.
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_point(shape=23, fill="red", col="blue")
Zusäzlich können wir zwei Farbmodi unterscheiden:
aes()
aes()
Nachfolgend finden wir eine Übersicht, in der die jeweiligen Unterschiede zwischen col
und fill
sowie statischen und variablen Farben verdeutlicht werden sollen. Der Code zur Erstellung der jeweiligen Grafik befindet sich über den Abbildungen.
statisch | Variablenausprägung | |
---|---|---|
fill
|
||
col
|
Wir können in R standardmäßig implementierte (“built-in”) Farben sowie Hex-Farben nutzen.
Auf dieser Seite finden wir eine Übersicht der built-in Farben. Diese können wir einfach mit ihrem Namen auswählen z.B. "red"
oder "seagreen1"
.
Diese built-in Farben sind nicht mit allen Anwendungen in R kompatibel. Beispielsweise können wir "seagreen"
, aber nicht "seagreen1"
in der Funktion kable()
aus dem Paket knitr, welche eine Tabelle erstellt, nutzen.
Darüber hinaus können wir auch Hex-Farben (mit dem Hexadezimal-Zahlensystem kodierte Faben) nutzen, die aus einem # und einer 6-stelligen Zeichenfolge bestehen, z.B. "#53FD9F"
. Dabei werden jeweils zwei Zahlen genutzt um rot, blau und grün zu kodieren (RGB). Beispielsweise können wir hier eigene Farben erstellen und uns den Hex-Code kopieren.
Wir können einzelne Elemente einer Grafik transparent machen. Wenn wir die Grafik exportieren wollen, müssen wir nicht nur in der Erstellung der Grafik sondern auch beim Export angeben, dass ein bestimmtes Element transparent sein soll. Nur Grafiken im PNG-Format können transparent sein.
Folgendermaßen können wir beispielsweise den Hintergrund der Grafik (plot.background
) transparent machen:
ggplot(ChickWeight[ChickWeight$Time == 8,], aes(Diet, weight)) +
geom_violin(scale = "count") +
theme(plot.background = element_rect(fill = "transparent")) # Erstellung
ggsave("Grafik_transp.png", bg = "transparent") # Export
Um zu sehen, dass die sonst weiße Hintergrundfläche der Grafik nun transparent ist, müssten wir die Grafik runterladen, weil der Hintegrund hier auch weiß ist.
Es ist zu empfehlen bei der farblichen Kodierung von Variablenausprägungen auf Farbpaletten zurückgreifen, da diese bereits gut durchdachte Farbkombinationen enthalten. Die Idee dahinter ist bei kategorialen Variablen verschiedene Farben und bei metrischen Variablen ähnliche Farben für ähnliche Ausprägungen zu nutzen.
Neben den voreingestellten Farbpaletten, die wir automatisch nutzen, wenn wir col
oder fill
innerhalb von aes()
spezifizieren, können wir auch weitere Farbpaletten nutzen.
Auf colorbrewer2.org finden wir jeweils angemessene Farbkombinationen zur Kodierung von kategorialen Daten. Teilweise sind diese Farbpaletten bereits in ggplot2 implementiert z.B. in der Funktion scale_colour_brewer()
bzw. scale_fill_brewer()
, mit der wir Farbanpassungen vornehmen können.
Für kategoriale Daten ist type="qual"
(qualitative) am besten geeignet, da es Paletten mit sehr unterschiedliche Farben nutzt. Es gibt noch "seq"
(sequential) und "div"
(diverging), mit denen wir eine Reihenfolge bzw. eine Grenze farblich darstellen können. Für mehr Informationen lohnt es sich auf der Webseite nachzuschauen. Dort sehen wir z.B. auch die verschiedenen Paletten, die wir mit dem Parameter palette
ändern können.
ggplot(ChickWeight[ChickWeight$Time == 8,], aes(Diet, weight)) +
geom_violin(scale = "count", aes(fill=Diet)) +
scale_fill_brewer(type="qual")
Wenn wir Farben, Punktformen o.ä. auf Variablen(ausprägungen) anwenden (d.h. diese in aes()
spezifizieren), wird automatisch eine Legende erstellt.
Es gibt verschiedene Aspekte von Legenden, die wir ändern können. Im Folgenden schauen wir uns Text und Positionierung an. Weitere Modifikationen finden wir hier.
Wir können sowohl den Titel als auch die einzelnen Elemente einer Legende anders benennen.
Wenn es sich um eine kategoriale Variable handelt, die wir mit fill
farblich kodiert haben, können wir die Änderungen mit scale_fill_discrete(name, labels)
vornehmen.
Dem Parameter name
übergeben wir den Titel der Legende, labels
einen Vektor mit den Namen der Bezeichnung der Ausprägungen.
Analog dazu nutzen wir scale_color_discrete()
, wenn wir das Argument color
(zur farblichen Umrandung) genutzt haben.
Wenn wir eine metrische Variable farblich kodiert haben, nutzen wir scale_fill_continuous
bzw. scale_colour_continuous ()
.
Die Position der Legende können wir mit theme(legend.position)
ändern. Wir können zwischen oben ("top"
), unten ("bottom"
), links ("left"
) und rechts ("right"
) wählen. Mit "none"
können wir die Legende entfernen.
Im Folgenden schauen wir uns an, wie wir Titel und Achsenbeschriftung, sowie Beschriftungen in der Grafik ändern bzw. hinzufügen können.
Um dem Plot (nur) eine Überschrift zu verpassen nutzen wir ggtitle()
.
ggplot(data=ChickWeight, aes(x=Time, y=weight)) + geom_point() +
ggtitle("Gewichtsveränderung über die Zeit")
Wenn wir (nur) die Beschriftung der x- oder y-Achse ändern möchten (z.B. weil diese anders benannt werden sollen als die Variablen im Datensatz), können wir xlab()
bzw. ylab()
nutzen.
ggplot(data=ChickWeight, aes(x=Time, y=weight)) + geom_point() +
xlab("Zeit in Tagen") + ylab("Gewicht in Gramm")
Wir können sowohl eine Überschrift als auch Achsenbeschriftungen ergänzen, indem wir labs(title, x, y)
nutzen. Außerdem können wir der Grafik mit caption
noch eine Bildunterschrift verpassen.
ggplot(data=ChickWeight, aes(x=Time, y=weight)) + geom_point() +
labs(title ="Gewichtsveränderung über die Zeit",
x = "Zeit in Tagen", y = "Gewicht in Gramm", caption="Weitere Notizen")
Mit geom_text()
(Bsp. 1 und 2) oder geom_label()
(Bsp. 3 und 4) können wir Ausprägungen einer Variablen als Text in unsere Grafik einfügen.
Allerdings können wir die Funktionen nicht bei Facetten anwenden.
Beide Funktionen machen fast das Gleiche. Optisch unterscheiden sie sich dadurch, dass geom_label()
den Text zusätzlich mit einem weißen Feld hinterlegt.
Unseren Text übergeben wir an aes(label)
, welches sowohl in ggplot()
(Bsp. 1 und 3), als auch in geom_text()
bzw. geom_label()
(Bsp. 2 und 4) spezifiziert werden kann.
Zusätzlich können wir die Ausrichtung des Texts anpassen. Dazu nutzen wir hjust
(horizontal) und vjust
(vertikale) (Bsp. 2 und 4), welchen wir einen Wert zwischen 0 und 1 übergeben (Default: hjust=0.5
und vjust=0.5
). Wir können auch Werte außerhalb von 0 bis 1 spezifizieren; allerdings wird in der Dokumentation davon abgeraten (ohne weitere Begründung; mehr Informationen dazu in der Dokumentation von ggplot2-specs).
Beispiel 1 und 2 zeigen die Gewichtsveränderung (x-Achse: Time
, y-Achse: weight
) über die Zeit von Küken 1 (ChickWeight$Chick == 1
). Wir beschriften die Datenpunkte mit den jeweiligen Messzeitpunkten (aes(label=Time)
).
ggplot(data=ChickWeight[ChickWeight$Chick == 1,],
aes(x=Time, y=weight, label=Time)) +
geom_point() +
geom_text()
ggplot(data=ChickWeight[ChickWeight$Chick == 1,],
aes(x=Time, y=weight)) +
geom_point() +
geom_text(aes(label=Time), vjust=1)
Beispiel 3 und 4 zeigen die Gewichtsverteilung der Küken in den Futtergruppen (x-Achse: Diet
, y-Achse: weight
) zum letzten Messzeitpunkt (Time 21
). Wir beschriften die Datenpunkte mit den jeweiligen Nummern der Küken (aes(label=Chick)
).
ggplot(data=ChickWeight[ChickWeight$Time == 21,],
aes(x=Diet, y=weight, label=Chick)) +
geom_point() + geom_label()
ggplot(data=ChickWeight[ChickWeight$Time == 21,],
aes(x=Diet, y=weight)) +
geom_point() + geom_label(aes(label=Chick), hjust=1.2)
Es gibt noch weitere Parameter, mit denen wir u.a. das Padding des Textes, die Rundung des Textfeldes, die Schriftgröße oder -farbe ändern können.
Weitere Hilfe zur Nutzung und Modifikation von geom_text()
und geom_label()
finden wir hier.
Wenn wir einen eigenen Text in eine Grafik schreiben wollen, können wir annotate(geom = "text")
und annotate(geom = "label")
nutzen. Analog zu den Funktionen im vorhergehenden Abschnitt sind beide geom
-Optionen gleich, außer dass der Text bei "label"
mit einem weißen Feld hinterlegt wird.
Anders als bei den Funktionen im vorhergehenden Abschnitt müssen wir eine Position festlegen (weil unser Text nicht zu bestehenden Datenpunkten gematcht wird). Dafür können wir x
und y
nutzen. Diese entsprechen den Maßen der Einheiten der jeweiligen Achsen.
Beispiel 1 zeigt die Gewichtsveränderung (x-Achse: Time
, y-Achse: weight
) über die Zeit von Küken 1 (ChickWeight$Chick == 1
).
Beispiel 2 zeigt die Gewichtsverteilung der Küken in den Futtergruppen (x-Achse: Diet
, y-Achse: weight
) zum letzten Messzeitpunkt (Time 21
).
ggplot(data=ChickWeight[ChickWeight$Chick == 1,],
aes(x=Time, y=weight)) +
geom_point() +
annotate(geom = "text", label="Küken 1", x=5, y=200)
ggplot(data=ChickWeight[ChickWeight$Time == 21,],
aes(x=Diet, y=weight)) +
geom_point() +
annotate(geom="label", label="Messzeitpunkt 21", x=1.7, y=370)
Wir können die Darstellung des Textes (wie im vorhergehenden Abschnitt) auch anpassen. Mehr Informationen dazu in der Dokumentation von annotate()
.
Der Einfachheit halber nutzen wir zur Kodierung von Gruppen häufig Zahlen, wie z.B. bei Diet
: 1, 2, 3, 4. In einer Grafik hingegen ist es aber sinnvoller, Begriffe zu nutzen.
Mit levels()
können wir uns nicht nur die verschiedenen Stufen eines Faktors ausgeben lassen. Wir können die Funktion außerdem nutzen, um die Benennung der Stufen zu ändern.
Im Folgenden ändern wir die Benennungen der Gruppen von Diet
.
ChickWeight$Diet_name <- ChickWeight$Diet # Kopie von `Diet` als neue Spalte
levels(ChickWeight$Diet_name) <- c("Diät 1", "Diät 2",
"Diät 3", "Diät 4")
ggplot(data=ChickWeight, aes(x=Diet_name, y=weight)) + geom_boxplot()
Die Reihenfolge von Faktorstufen ist bei Wortketten standardmäßig alphabetisch und bei Zahlen aufsteigend. Wenn wir das ändern möchten, müssen wir die Sortierung des Faktors ändern (dadurch ändert sich nicht die Sortierung der Variablen im Datensatz).
Die Sortierung des Faktors können wir mit der Funktion factor()
ändern. Dem Parameter x
übergeben wir die Variable; levels
einen character-Vektor mit der neuen Sortierung.
Wir ändern die Reihenfolge der Gruppen von Diet
.
ggplot(data=ChickWeight, aes(x=Diet, y=weight)) +
geom_boxplot() +
ggtitle("unbearbeitet")
ggplot(data=ChickWeight, aes(x=Diet_sort, y=weight)) +
geom_boxplot() +
ggtitle("bearbeitet")
Manchmal ist es sinnvoll, die Achsengrenzen von x- und y-manuell zu ändern, beispielsweise wenn wir Grafiken einzelner Gruppen vergleichen möchten.
Im Abschnitt zu Facetten haben wir gelernt, wie wir einzelne Grafiken für jede Ausprägung einer kategorialen Variablen (bzw. für jede Kombination von Ausprägungen mehrerer kategorialer Veriablen) erstellen können. Diese werden jedoch kleiner dargestellt, je mehr Gruppierungen es gibt. Wenn wir ohnehin nur spezifische Gruppen vergleich wollen, können wir für diese manuell Grafiken erstellen.
Wenn wir uns für die Gewichtszunahme (weight
) in den Gruppen (Diet
) zu Beginn (Time 0
) und zum Ende (Time 21
) des Experiments interessieren, dann können manuell beide Grafiken erstellen.
ggplot(data=ChickWeight[ChickWeight$Time == 0,], aes(x=Diet, y=weight)) +
geom_point()
ggplot(data=ChickWeight[ChickWeight$Time == 21,], aes(x=Diet, y=weight)) +
geom_point()
Das Problem hierbei ist, dass sich die Streubreiten von weight
, und damit die Achsengrenzen von y, zu beiden Zeitpunkten stark unterscheiden. Dadurch könnten wir einen falschen Eindruck von den Daten bekommen.
Um das zu verhindern, können wir die Achsengrenzen anpassen. Das machen wir mit xlim()
und ylim()
. Diesen übergeben wir jeweils einen numerischen Vektor, der den unteren und den oberen Grenzwert der Achse enthält.
ggplot(data=ChickWeight[ChickWeight$Time == 0,], aes(x=Diet, y=weight)) +
geom_point() + ylim(c(0,375))
ggplot(data=ChickWeight[ChickWeight$Time == 21,], aes(x=Diet, y=weight)) +
geom_point() + ylim(c(0,375))
Bei genauer Betrachtung fällt weiterhin auf, dass unsere beiden y-Achsen nicht bei 0 beginnen, obwohl wir das mit ylim(c(0,...))
scheinbar so festgelegt haben.
Standardmäßig werden Achsen (kontinuierlicher Variablen) etwas erweitert. Dieses Verhalten können wir z.B. mit scale_y_continuous(expand = c(0, 0))
auf der y-Achse (und scale_x_coninuous()
auf der x-Achse) ändern.
Etwaige Änderungen der Achsengrenzen müssen wir dann aber auch in scale_y_continuous(limits)
machen.
ggplot(data=ChickWeight[ChickWeight$Time == 21,], aes(x=Diet, y=weight)) +
geom_point() + scale_y_continuous(limits=c(0,375), expand = c(0, 0))
Wir können auch Geraden einzeichnen (z.B. alternativ zu Achsengrenzen oder um manuell Regressionsgeraden einzuzeichnen).
Vertikale Gerade können wir mit geom_vline(xintercept)
einfügen; horizontale mit geom_hline(yintercept)
.
Dabei können wir den Parametern xintercept
und yintercept
auch mehrere Werte übergeben. Dazu packen wir diese in einen Vektor mit c()
.
Wir schauen uns dazu eines der Beispiele aus dem Abschnitt vorher an: die Gewichtszunahme (weight
) in den Gruppen (Diet
) zum Ende (Time 21
) des Experiments. Wir zeichnen jeweils den kleinsten und größten Wert, sowie den Mittelwert (über alle Diet
-Gruppen) von weight
als (horizontale) Gerade ein.
# Maxima von weight (zu Time 21) herausfinden:
min <- min(ChickWeight$weight, na.rm=TRUE)
max <- min(ChickWeight$weight, na.rm=TRUE)
ggplot(data=ChickWeight[ChickWeight$Time == 21,], aes(x=Diet, y=weight)) +
geom_point() +
# Min & Max:
geom_hline(yintercept=c(min, max)) +
# Mittelwert:
geom_hline(yintercept=mean(ChickWeight$weight[ChickWeight$Time == 21]), col="red")
So können wir die Verteilungen von weight
in den Diet
-Gruppen zu den zwei Zeitpunkten besser vergleichen. Wir können nicht nur sehen, dass die Verteilung an Tag 21 wesentlich breiter gestreut ist als an Tag 0 (weil dadurch die Achsengrenzen angepasst werden), sondern auch …
Manchmal möchten wir in einer bestehende Grafik noch zusätzliche statistische Kennwerte einfügen. Dazu können wir stat_summary()
nutzen. Damit können wir verschiedenen Kennwerte von y (z.B. den Mittelwert, den Median, oder den minimalen und maximalen Wert) in eine Grafik einzeichnen.
Als Beispiel schauen wir uns ein Streudiagramm des Gewichts der Küken (weight
) in Abhängigkeit ihrer Diät (Diet
) am 10. Tag (Time 10
) an.
Per Default wird mit stat_summary()
(ohne Spezifikation von Argumenten) der Mittelwert (als Punkt), umschlossen vom Standardfehler des Mittelwerts (als Linie), eingezeichnet.
Wir können dem Parameter fun
aber auch die Argumente "median"
oder "mean"
übergeben. Letzteres zeichnet ebenfalls den Mittelwert ein (aber ohne Standardfehler). Wenn wir die Extrema einfügen wollen, nutzen wir fun.min = "min"
und fun.max = "max"
.
ggplot(data=ChickWeight[ChickWeight$Time == 10,], aes(x=Diet, y=weight)) +
geom_point() + stat_summary(col="blue") + # Mittelwert + SE: blau
stat_summary(fun = "median", col="red", pch=3) # Median: rotes Kreuz
No summary function supplied, defaulting to `mean_se()`
ggplot(data=ChickWeight[ChickWeight$Time == 10,], aes(x=Diet, y=weight)) +
stat_summary(fun = "mean", fun.min = "min", fun.max = "max")
Mehr Informationen zur Funktion finden wir in der R-Dokumentation von stat_summary()
bzw. eine ausführlichere Version davon mit Beispielabbildungen hier.
Es gibt einige Optionen, den Hintergrund des Koordinatensystems zu verändern.Standarmäßig haben wir einen grauen Hintergrund mit weißen Rasterlinien; das entspricht der Einstellung theme_grey()
.
Wenn wir beispielsweise einen weißen Hintergrund mit grauen Rasterlinien haben möchten, nutzen wir dafür theme_minimal()
.
ggplot(data=ChickWeight, aes(x=Time, y=weight)) +
geom_point() +
theme_minimal()
Wir können unsere Palette möglicher Motiven mit dem Paket ggthemes erweitern. In diesem finden wir u.a. die Funktion theme_tufte()
, mit der Grafiken nach dem Vorbild von Edward Tufte auf das Notwendige reduziert werden (z.B. weißer Hintergrund, keine unnötigen Rasterlinien eingezeichnet etc.).
Wenn wir unsere Grafik erstellt haben, müssen wir diese noch exportieren, um sie außerhalb von R nutzen zu können.
Grundsätzlich gibt es zwei Möglichkeiten dafür:
Funktionen
Im Folgenden schauen wir uns an, wie wir den Export-Button und die Funktion ggsave()
, mit der wir mit ggplot2 erstellte Grafiken exportieren können, nutzen.
Eine Übersicht über das Exportieren von Grafiken mit Funktionen aus dem Basispaket grDevices, finden wir hier. Mit diesen Funktionen können wir Grafiken, die mit einem beliebigen R-Paket erstellt wurden, exportieren.
Wenn wir mehrere Garfiken exportieren wollen, müssen wir darauf achten, dass wir diesen unterschiedliche Namen zuweisen. Sowohl innerhalb von R als auch beim Export werden bestehende Objekte mit dem gleichen Namen überschrieben.
Hinweis für Windows-NutzerInnen: Manchmal kann es zu Probleme mit Aliasing kommen. Das ist, wenn Grafikelemente und/oder Text nach dem Export “krisselig” dargestellt werden.
Um das zu verhindern, können wir das Paket Cairo, mit dessen gleichnamiger Funktion, nutzen. Mehr Informationen zur Anwendung können wir auf R-bloggers finden.
Wenn wir die Grafik mittels der grafischen Benutzeroberfläche exportieren möchten, klicken wir auf Export im unteren rechten Panel. Wenn wir diese als Bild exportieren wollen, wählen wir Save as Image… aus.
Daraufhin öffnet sich ein neues Fenster, in dem wir einige Einstellungen tätigen können.
Bildformat
Hier können wir wählen, in welchem Bildformat wir unsere Grafik abspeichern wollen. Standardmäßig wird sie als PNG gespeichert. Weitere Optionen sind JPEG, TIFF, BMP, SVG und EPS.
Verzeichnis
Wenn wir auf den Button Directory… klicken, können wir festlegen, wo die Grafik gespeichert werden soll. Standardmäßig wird sie in der obersten Ordnerstruktur, bzw. wenn wir ein Workind Directory gesetzt haben in diesem, gespeichert.
Name der Datei
Hier können wir unserer Datei einen Namen geben. Standardmäßig wird als Name Rplot vorgeschlagen, wenn wir in dem aktuellen Verzeichnis keine andere Datei (im gleichen Format) besitzen, die den gleichen Namen trägt. Wenn es bereits eine solche Datei gibt, wird als Name für die neue Datei automatisch Rplot01 (bzw. Rplot02 usw.) vorgeschlagen.
Größe bearbeiten
Für die Bearbeitung der Größe der Grafik haben wir zwei Möglichkeiten:
Bei beiden Möglichkeiten können wir entweder willkürlich die Maße anpassen oder das Verhältnis zwischen Breite und Höhe beibehalten indem wir ein Häkchen bei Maintain aspect ratio setzen.
Wenn wir eine gewisse Größe in cm haben wollen, können wir die px-Angaben z.B. auf dieser Seite umrechnen lassen.
Die Auflösung der Grafik können wir in dem Export-Fenster nicht ändern. Standardmäßig beträgt diese 72 dpi.
ggsave()
Mit ggplot2 erstellte Grafiken können wir mit ggsave()
exportieren. Schauen wir uns einmal an, was wir in der Funktion spezifizieren können.
ggsave(
filename = "Grafik.png", # Name und Bildformat der zu exportierenden Grafik
path = "/Users/...",
plot = last_plot(), # letzter Plot als Default oder alternativ Name der Grafik in R
width = 12, # Breite
height = 9, # Höhe
units = "cm", # in welcher Einheit angegeben
dpi = 72 # Default; äquivalent zu: dpi = "screen"
)
Das gewünschte Bildformat können wir einfach als Endung an den Namen der Grafik anhängen. Beides übergeben wir in " "
an das Argument filename
. Wir haben hier mehr Auswahl an möglichen Outputformaten. Es gibt z.B. PNG, PDF, JPEG, TIFF, EPS, PS, TEX und SVG.
Wir legen mit dem Argument plot
fest, welche Grafik wird exportieren wollen. Das funktioniert nur wenn wir diese vorher in einer Variablen gespeichert haben (z.B. name <- ggplot(...)
). Dann erscheint der Name auch im Global Environment (oberer rechtes Panel in R). Standarmäßig wird der zuletzt erstellte Plot exportiert, wenn wir das Argument plot
weglassen (oder plot=last_plot()
eingeben).
Das Verzeichnis, in dem die Grafik abgelegt werden soll, können wir mit dem Argument path
festlegen. Diesem übergeben wir den Ordnerpfad, welchen wir auch in " "
setzen müssen. Standardmäßig wird die Grafik in der obersten Ordnersturktur bzw. im derzeitigen Working Directory (wenn eins gesetzt wurde) abgelegt.
Die Größe der zu exportierenden Grafik können wir mit width
und height
ändern. Mit units
legen wir dabei fest, in welcher Einheit die Maße sein sollen. Wenn wir width
, height
und units
weglassen, wird die Größe des current graphics device genutzt. Diese ist von unseren jeweiligen globalen Computer-Einstellungen abhängig.
Im Gegensatz zur Möglichkeiten des Exportierens via Export-Button können wir in ggsave()
mit dem Argument dpi
die Bildauflösung verändern. Zusäzlich zu der Standardeinstellung von 72 dpi (screen), können wir 300 dpi (print) und 320 dpi (retina) nutzen. Wir können sowohl die Zahl, als auch die Bezeichnung in " "
angeben.
qplot()
Es gibt im Paket ggplot2 auch eine einfacher handzuhabende Variante zu ggplot()
- das ist qplot()
(“quick plot”). Diese ist an den Aufbau der base graphics Funktion plot()
angelehnt. Mit ihr können wir verschiedenen Grafiken erstellen, aber sie ist weniger flexibel und modifizierbar als ggplot()
.
Wir erstellen exemplarisch ein Streudiagramm vom Gewicht der Küken (weight
) in Abhängigkeit der Zeit (Time
) und ergänzen eine Überschrift sowie Achsenbeschriftungen.
qplot(x=Time, y=weight, data=ChickWeight, geom="point",
main="Gewichtsveränderung über die Zeit",
xlab="Zeit in Tagen",
ylab="Gewicht in Gramm")
Für mehr Informationen zu qplot()
(vor allem zu den möglichen Argumenten) können wir hier nachschauen.
Wenn wir mehrere Grafiken haben, die wir in einer gemeinsamen Datei speichern wollen, dann müssen wir dafür auf zusätzliche Pakete zurückgreifen. Diese Seite gibt einen guten Überblick darüber, welche Funktionen wir dafür nutzen können. Am Ende der Seite unter Alternative options befindet sich eine kleine Tabelle, in der die Pakete mit ihren Funktionen hinsichtlich ihrer ggsave()
-Kompatibilität und Möglichkeit zur Anordnung der Plots verglichen werden.
Um eine möglichst exakte Replikation der Funktionen zu gewährleisten gibt es im folgenden relevante Angaben zum System (R-Version, Betriebssystem, geladene Pakete mit Angaben zur Version), mit welchem diese Seite erstellt wurde.
R version 4.0.3 (2020-10-10)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.2 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0
locale:
[1] LC_CTYPE=de_DE.UTF-8 LC_NUMERIC=C
[3] LC_TIME=de_DE.UTF-8 LC_COLLATE=de_DE.UTF-8
[5] LC_MONETARY=de_DE.UTF-8 LC_MESSAGES=de_DE.UTF-8
[7] LC_PAPER=de_DE.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=de_DE.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods
[7] base
other attached packages:
[1] ggalluvial_0.12.3 gdtools_0.2.3 ggiraphExtra_0.3.0
[4] lsr_0.5 knitr_1.31 kableExtra_1.3.4
[7] dplyr_1.0.5 ggplot2_3.3.3
loaded via a namespace (and not attached):
[1] httr_1.4.2 tidyr_1.1.3 sass_0.3.1
[4] jsonlite_1.7.2 viridisLite_0.3.0 splines_4.0.3
[7] bslib_0.2.4 Formula_1.2-4 assertthat_0.2.1
[10] highr_0.8 latticeExtra_0.6-29 yaml_2.2.1
[13] pillar_1.5.1 backports_1.2.1 lattice_0.20-41
[16] glue_1.4.2 uuid_0.1-4 digest_0.6.27
[19] RColorBrewer_1.1-2 checkmate_2.0.0 rvest_1.0.0
[22] colorspace_2.0-0 htmltools_0.5.1.1 Matrix_1.2-18
[25] plyr_1.8.6 pkgconfig_2.0.3 purrr_0.3.4
[28] scales_1.1.1 mycor_0.1.1 webshot_0.5.2
[31] svglite_2.0.0 jpeg_0.1-8.1 distill_1.2
[34] downlit_0.2.1 htmlTable_2.1.0 tibble_3.1.0
[37] mgcv_1.8-33 generics_0.1.0 farver_2.1.0
[40] sjlabelled_1.1.7 ellipsis_0.3.1 withr_2.4.1
[43] ppcor_1.1 nnet_7.3-14 cli_2.3.1
[46] survival_3.2-7 magrittr_2.0.1 crayon_1.4.1
[49] evaluate_0.14 fansi_0.4.2 MASS_7.3-53
[52] nlme_3.1-149 xml2_1.3.2 foreign_0.8-79
[55] tools_4.0.3 data.table_1.14.0 lifecycle_1.0.0
[58] stringr_1.4.0 munsell_0.5.0 cluster_2.1.0
[61] compiler_4.0.3 jquerylib_0.1.3 systemfonts_1.0.1
[64] rlang_0.4.10 grid_4.0.3 rstudioapi_0.13
[67] htmlwidgets_1.5.3 base64enc_0.1-3 labeling_0.4.2
[70] rmarkdown_2.7 gtable_0.3.0 sjmisc_2.8.6
[73] reshape2_1.4.4 R6_2.5.0 gridExtra_2.3
[76] ggiraph_0.7.8 utf8_1.1.4 Hmisc_4.5-0
[79] insight_0.13.1 stringi_1.5.3 Rcpp_1.0.6
[82] vctrs_0.3.6 rpart_4.1-15 png_0.1-7
[85] tidyselect_1.1.0 xfun_0.21
Für Informationen zur Interpretation dieses Outputs schaut auch den Abschnitt Replizierbarkeit von Analysen des Kapitels zu Paketen an.