In diesem Teil wollen wir uns auf die Interpretation und Kommunikation von Daten anhand von Grafiken fokussieren.
Als erster Schritt einer Datenanalyse steht stets das Einlesen von Daten, was wir an dieser Stelle aufgreifen wollen.
library(tidyverse)Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.2 ──✔ ggplot2 3.3.6 ✔ purrr 0.3.4
✔ tibble 3.1.8 ✔ dplyr 1.0.10
✔ tidyr 1.2.1 ✔ stringr 1.4.1
✔ readr 2.1.3 ✔ forcats 0.5.2 ── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
Die Bibliothek readr aus tidyverse bietet
die folgenden Hilfsfunktionen zum Einlesen von Dateien:
read_csv(): Komma separierte Dateien (CSV)
read_tsv(): Tabulator separierte Dateien
(TSV)
read_delim(): generell separierte Dateien (inklusive
CSV und TSV)
read_fwf(): Dateien mit fixierter Breite
read_table(): Leerzeichen separierte
Dateien
read_log(): Log-Dateien
Die Funktionen sind hierbei selbstbeschreibend und Argumente
einheitlich. Nach erfolgreichem Einlesen liegt eine Tabelle als
tibble vor.
Zu den Lesefunktionen von Tabellen gibt es auch eine zugehörige
Schreibfunktion: write_csv(), write_tsv() oder
write_delim().
Zusätzlich können CSV-Tabellen mit write_excel_csv() in
ein Excel-Format überführt werden.
Zum Einlesen von Excel-Dateien wird die Bibliothek
readxl verwendet. Hierzu später mehr. Auch andere Formate
können durch externe Bibliotheken eingelesen werden wie z. B. SPSS-,
Stata- und SAS-Dateien durch die Bibliothek haven.
col.names: ob die Datei eine Kopfzeile
(Spaltennamen) hat (TRUE oder FALSE)
skip = x: überspringe x Reihen
(nützlich wenn Reihen Kommentare enthalten)
für alle weiteren Argument: ?read_delim.
Beim Einlesen treten folgende Fehler typischerweise auf:
Das Argument col.names wurde falsch eingegeben. Wenn
es keine Spaltennamen gibt, muss explizit col.names = FALSE
eingegeben werden.
Das Trennzeichen wurde falsch definiert oder die falsche Funktion
zum Einlesen verwendet. Falls das Trennzeichen Ihrer Datei nicht
explizit als read_* Funktion vorliegt, müssen Sie
read_delim() verwenden und das Argument delim
auf das Trennzeichen setzen.
Der Pfad zur Datei wurde falsch angegeben.
Die Datei hat eine untypische Struktur bzw. stellt keine einheitliche Tabelle dar.
Um diese Probleme zu erkennen, gibt es einige Möglichkeiten:
Falls beim Einlesen ein Fehler auftritt: den Fehler lesen und verstehen oder Fehler + R in eine Suchmaschine eingeben und nach einer Lösung suchen.
Falls die Datei eingelesen wurde, aber ihr Format nicht stimmt:
lassen Sie sich den Dataframe ausgeben und prüfen Sie, ob die Kopfzeile
richtig ist und ob die einzelnen Spalten tatsächlich getrennt sind.
Falls nein, dann muss col.names oder delim
angepasst werden.
Falls die Datei nicht gefunden wurde, wurde der Pfad zur Datei falsch angegeben. Dateien müssen immer relativ aus dem aktuellen Pfad angegeben werden. Schauen Sie unter RStudio, in welchem Ordner Sie sich befinden und verschieben Sie die Datei in diesen Ordner.
Es ist immer ratsam, die Datei vorher anzuschauen, um Trennzeichen und Kopfzeile zu bestimmen.
In dem folgenden Beispiel liegt die Datei als typische
CSV vor, d. h. es muss lediglich der Name angepasst
werden:
huehner_gewicht <- read_csv("../resources/ChickWeight.csv")Rows: 578 Columns: 4── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (4): weight, Time, Chick, Diet
ℹ 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.
huehner_gewichtSie sehen, dass die Datei direkt als tibble
vorliegt.
Als Alternative hierzu sehen wir, dass read.table()
mehrere Parameter benötigt:
huehner_gewicht2 <- read.table("../resources/ChickWeight.csv")
huehner_gewicht2Wir sehen hier, dass kein richtiges Trennzeichen gesetzt wurde und
somit alle Spalten zusammengesetzt wurden. Zusätzlich sehen wir, dass
statt den Kopfzeilen lediglich V1 steht.
Um das zu beheben, muss sep und header
gesetzt werden:
huehner_gewicht2 <- read.table("../resources/ChickWeight.csv", sep = ",", header = TRUE)
huehner_gewicht2Nun liegt die Tabelle als vernünftiger Dataframe vor.
Die read_* Funktionen von tidyverse sind
hiermit sehr einfach zu verwenden und lesen die Dateien direkt als
tibble und nicht als data.frame ein.
Um Excel-Dateien einzulesen, bedarf es der externen Bibliothek
readxl.
Zum Installieren können wir wie gewohnt
install.packages() verwenden:
install.packages("readxl")Bei dem Start jeder R-Session muss dann wie gewohnt die Bibliothek geladen werden:
library(readxl)Zum Einlesen der Excel-Datei kann nun read_excel()
verwendet werden:
huehner_gewicht <- read_excel("../resources/ChickWeight.xlsx")
huehner_gewichtAuch hier liegt nach dem Einlesen ein tibble vor.
Der sich wiederholende Zyklus für die explorative Datenanalyse besteht grob aus drei Schritten:
Fragen über Daten erstellen
Antworten durch Visualisierung, Transformation (und Modellierung) suchen
Neue Erkenntnisse verwenden, um Fragen zu präzisieren oder neue Fragen zu generieren
Das Verständnis der Daten steht hierbei im Vordergrund und wird durch die Fragestellungen geleitet.
Typische wiederkehrende Fragen sind beispielsweise:
Welche Variation liegt innerhalb der Variablen vor?
Was für Zusammenhänge/Kovariation (Korrelation) gibt es zwischen den Variablen?
Variation ist die Tendenz, dass sich eine Variable von Beobachtung zu Beobachtung ändert:
Bei kontinuierlichen Variablen ergeben zwei Messungen meist (leicht) unterschiedliche Messwerte
In jeder Messung stecken (kleine) Messfehler, die sich von Messung zu Messung unterscheiden
Auch kategorische Variablen können sich unterscheiden (z. B. bei Messung unterschiedlicher Subjekte, an unterschiedlichen Zeiten, usw.)
Visualisierung der Verteilung ist hierbei essentiell
Kategorische Variablen sind beschränkt auf eine kleine Anzahl an Werten (Kategorien) und werden in R durch Vektoren von Faktoren oder Charaktere (Zeichenketten) beschrieben. Für die Darstellung der Verteilung von kategorischen Variablen verwenden wir Balkendiagramme (oder Tortendiagramme):
ggplot(data = diamonds) +
geom_bar(mapping = aes(x = cut))Die Höhe der Balken lässt sich wie zuvor mit count()
berechnen:
diamonds %>%
count(cut)Bei kontinuierlichen Variablen sind wir nicht auf endliche Kategorien beschränkt sondern auf Zahlen (oder Daten). Die Verteilung wird hierbei mit Hilfe von Histogrammen dargestellt:
ggplot(data = diamonds) +
geom_histogram(mapping = aes(x = carat), binwidth = 0.5)Die Höhen der Klassen lassen sich ebenfalls mit count()
und cut_width() berechnen, wobei in
cut_width() die Klassenbreite angegeben werden muss:
diamonds %>%
count(cut_width(carat, 0.5))Im Gegensatz zum Balkendiagramm werden hierbei die Daten in Klassen
(bins) eingeordnet und für diese Klassen die Häufigkeiten
berechnet:
bindwidth gibt hierbei die Breite der Klassen
(relativ zu der x-Achse) an
Es lohnt sich, unterschiedliche Klassenbreiten zu betrachten:
kleine_diamanten <- diamonds %>%
filter(carat < 3)
ggplot(data = kleine_diamanten, mapping = aes(x = carat)) +
geom_histogram(binwidth = 0.1)Um mehrere Histogramme (z. B. für unterschiedliche Gruppen) in einer
Grafik anzuzeigen, lohnt es sich anstelle von
geom_histogram() die Funktion geom_freqpoly()
zu verwenden, welches Linien statt Blöcke verwendet:
ggplot(data = kleine_diamanten, mapping = aes(x = carat, colour = cut)) +
geom_freqpoly(binwidth = 0.1)Sie sehen vor allem, dass viele Diamanten mit einem idealen Schliff
(cut) einen geringen Karatwert aufweisen.
Es zeigt sich, dass hohe Balken die typischen Werte einer Variable darstellen, kleine Werte weniger typische Werte. Fehlende Balken zeigen, dass diese Werte in den erhobenen Daten nicht vorliegen.
Folgende Fragen können interessante Erkenntnisse liefern:
Welche Werte sind am häufigsten? Warum?
Welche Werte sind selten? Wieso? Liegt das innerhalb der Erwartungen?
Sehen Sie untypische Muster? Woher stammen diese?
Folgendes Histogramm soll als Beispiel gelten:
Warum gibt es mehr Diamanten mit ganzenzahligen Karatwerten und typischen Brüchen?
Warum sind rechts von einem Höchstwert mehr Diamanten als links von diesem?
Warum gibt es keine Diamanten mit mehr als 3 Karat?
ggplot(data = kleine_diamanten, mapping = aes(x = carat)) +
geom_histogram(binwidth = 0.01)Ausreißer sind untypische Beobachtungen, die stark von dem Muster der Daten abweichen (besonders große oder kleine Werte).
In manchen Fällen ist es schwierig Ausreißer zu finden:
ggplot(diamonds) +
geom_histogram(mapping = aes(x = y), binwidth = 0.5)Die Grafiken ist hierbei untypisch weit, obwohl der Großteil der Daten zwischen 5 und 10 liegt. Das ist ein Indiz, dass ein Ausreißer vorliegt. Die Klassenhöhe der seltenen Beobachtungen (mit großen oder kleinen Werten) sind hierbei kaum zu erkennen.
Um diese Werte zu sehen, muss herangezoomt werden durch die
Veränderung der y-Achse (mit coord_carstesian()):
ggplot(diamonds) +
geom_histogram(mapping = aes(x = y), binwidth = 0.5) +
coord_cartesian(ylim = c(0, 50))Mit Hilfe von filter() lassen sich diese Werte
anzeigen:
Ob die Ausreißer Sinn ergeben, ist von der Variable abhängig. Der
y-Wert beschreibt eine der Dimensionen (in mm) der
Diamanten. Ein Wert von 0mm erscheint hierbei nicht möglich. Es ist auch
fragwürdig, ob sehr große Werte korrekt sind oder Messfehler.
Typischerweise werden Analysen mit und ohne Ausreißer durchgeführt, um zu testen, ob diese einen Effekt haben.
Falls diese keinen Effekt haben, können sie entfernt werden. Ansonsten sollten diese nicht ohne Begründung entfernt oder ersetzt werden. In dem Fall müsste der Grund der Ausreißer gefunden werden.
Bei Ausreißern gibt es mehrere Möglichkeiten, diese zu “beheben”:
diamonds2 <- diamonds %>%
filter(between(y, 3, 20))Das Problem hierbei ist, dass neben dem Ausreißer für eine Variable andere, valide Variablen der Beobachtungen ebenfalls verloren gehen.
NA ersetzen. Hierbei helfen die
mutate() und ifelse() Funktionen:diamonds2 <- diamonds %>%
mutate(y = ifelse(y < 3 | y > 20, NA, y))ifelse() hat drei Argumente:
eine Bedingung als logischer Vektor (für welche Reihen soll etwas verändert werden)
falls die Bedingung TRUE ist, dann wird der erste
Teil zurückgegeben
falls die Bedingung FALSE, dann wird der zweite Teil
zurückgegeben
In diesem Fall werden also Einträge, bei denen y kleiner
als 3 oder größer als 20 sind, durch NA ersetzt. Ansonsten
bleibt der y-Wert gleich.
Variation beschreibt das Verhalten innerhalb einer Variable während Kovariation das Verhalten zwischen Variablen beschreibt.
Kovariation beschreibt also die Tendenz, dass zwei sich oder mehr Variablen in einem gemeinsamen Muster verändern
Verteilungen von kontinuierlichen Variablen, die nach Kategorien gruppiert wurden, hatten wir bereits mehrfach betrachtet.
In manchen Fällen sind die Muster der Verteilungen jedoch nicht gut einsehbar, wenn manche Gruppen weniger häufig auftreten:
ggplot(data = diamonds, mapping = aes(x = price)) +
geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)ggplot(diamonds) +
geom_bar(mapping = aes(x = cut))In diesem Fall bietet es sich an, die Dichte anstelle der Häufigkeiten darzustellen:
ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) +
geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)Eine weitere Darstellungsmöglichkeit von kontiniuerlichen Daten sind die Boxplots, welche ebenfalls das Vergleichen von Verteilungen ermöglichen:
ggplot(data = diamonds, mapping = aes(x = cut, y = price)) +
geom_boxplot()Auch hier sehen wir, dass durchschnittlich die Diamanten mit gutem Schliff weniger Wert sind.
Falls die Reihenfolge der Darstellung der Kategorien verändert werden
soll, wird die Funktion reorder() verwendet.
In diesem Fall ist die Darstellung der Gruppen sinnvoll, da die
Kategorien nach dem cut geordnet sind (vom schlechtesten
zum besten).
Einige kategorische Variablen wie cut sind also bereits
geordnet. Die meisten jedoch sind nicht geordnet:
ggplot(data = mpg, mapping = aes(x = class, y = hwy)) +
geom_boxplot()Der Autotyp zum Beispiel lässt sich nicht wirklich ordnen.
Um dennoch einen Trend zu sehen, können die Kategorien beispielsweise
aufsteigend mit reorder() angeordnet werden:
ggplot(data = mpg) +
geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy))Die kategorische Variable class wird somit durch
reorder() nach dem Median der Variable hwy
angeordnet.
Bei langen Namen mag sich auch die Änderung der Achsen anbieten
(durch coord_flip()).
Um die Beziehung zweier kategorischer Variablen zu betrachte, müssen
die geteilten Häufigkeiten betrachtet werden. Hierzu wird
geom_count() verwendet:
ggplot(data = diamonds) +
geom_count(mapping = aes(x = cut, y = color))Die Häufigkeit der Farben und dem Schliff werden also durch die Größe der Punkte hervorgehoben.
Um diese Häufigkeiten zu berechnen, kann wieder count()
verwendet werden:
diamonds %>%
count(color, cut)Eine schönere Ansicht (heatmap) ist ebenfalls mit
geom_title() möglich:
diamonds %>%
count(color, cut) %>%
ggplot(mapping = aes(x = color, y = cut)) +
geom_tile(mapping = aes(fill = n))Es gibt jedoch noch viele weitere externe Bibliotheken, mit denen heatmaps angenehmer erstellt werden können.
Streudiagramme sind bei der Darstellung des Zusammenhangs von zwei kontinuierlichen Variablen am besten geeignet.
Beispielsweise hatten wir bereits den exponentiellen Zusammenhang von Karat und Preis gesehen:
ggplot(data = diamonds) +
geom_point(mapping = aes(x = carat, y = price))Leider werden bei großen Datensätzen Streudiagramme weniger
übersichtlich. Eine Möglichkeit wäre die Anpassung der Transparenz mit
dem alpha Wert:
ggplot(data = diamonds) +
geom_point(mapping = aes(x = carat, y = price), alpha = 1/100)Eine weitere Möglichkeit wäre die Darstellung von Klassen und deren Häufigkeiten, ähnlich wie eine Heatmap:
ggplot(data = kleine_diamanten) +
geom_bin2d(mapping = aes(x = carat, y = price))# install.packages("hexbin")
ggplot(data = kleine_diamanten) +
geom_hex(mapping = aes(x = carat, y = price))Sie sehen, dass die Grafiken sich genauso einfach erstellen lassen wie ein Streudiagramm, auch wenn die Komplexität wesentlich höher ist.
Eine weitere Möglichkeit ist die Einordnung einer Variablen in Klassen und diese dann als kategorische Variable zu benutzen:
ggplot(data = kleine_diamanten, mapping = aes(x = carat, y = price)) +
geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))Die Klassen werden hierbei durch eine Breite von 0.1
(wird in cut_width(x, breite festgelegt) abgetrennt.
Um die Proportionen der Breite anzupassen, kann statt
cut_width() auch cut_number() verwendet
werden:
ggplot(data = kleine_diamanten, mapping = aes(x = carat, y = price)) +
geom_boxplot(mapping = aes(group = cut_number(carat, 20)))Muster (pattern) in den Daten zeigen Beziehungen auf. Es sollten sich stets die folgenden Fragen gestellt werden:
Ist das Muster durch Zufall entstanden?
Wie kann diese Beziehung beschrieben werden?
Wie stark ist diese Beziehung
Welche anderen Variablen könnten Einfluss auf diese Beziehung haben?
Verändert sich die Beziehung, wenn besondere Untergruppen betrachtet werden?
In diesem Abschnitt soll ein neuer Datensatz betrachtet werden. Der
Datensatz msleep behandelt das Schlafverhalten von
Säugetieren und liegt uns als Excel-Datei vor. Für mehr Information
tippen Sie wie gewohnt ?msleep.
library(tidyverse)msleep.xlsx aus Moodle als
tibble msleep in R ein. Schaffen Sie sich
einen Überblick über den Datensatz.sleep_total und vore. Betrachten Sie auch die
Gruppierung von sleep_total nach vore. Was
fällt Ihnen auf?awake im
Bezug auf vore. Entspricht die Verteilung Ihrer Erwartung
im Vergleich zu dem sleep_total?awake und
sleep_total grafisch dar. Was stellen Sie fest?geom_violin() mit
geom_histogram(), welches Facetten benutzt, um Gruppen
darzustellen, und geom_freqpoly(), welches für jede Gruppe
Farben einfügt. Verwenden Sie hierfür erneut die Variablen
awake und vore.vore und
conservation gemeinsam dar.
Verwenden Sie hierfür zwei verschiedene Darstellungsarten.
Welche Kombination tritt am häufigste/seltensten auf?
Ändern Sie die Farben des Gradienten der vorherigen Grafik mit
Hilfe von
scale_fill_gradient(low = farbe1, high = farbe2).
vore,
brainwt und bodywt dar.