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:
<- read_csv("../resources/ChickWeight.csv") huehner_gewicht
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_gewicht
Sie sehen, dass die Datei direkt als tibble
vorliegt.
Als Alternative hierzu sehen wir, dass read.table()
mehrere Parameter benötigt:
<- read.table("../resources/ChickWeight.csv")
huehner_gewicht2 huehner_gewicht2
Wir 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:
<- read.table("../resources/ChickWeight.csv", sep = ",", header = TRUE)
huehner_gewicht2 huehner_gewicht2
Nun 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:
<- read_excel("../resources/ChickWeight.xlsx")
huehner_gewicht huehner_gewicht
Auch 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:
<- diamonds %>%
kleine_diamanten 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”:
<- diamonds %>%
diamonds2 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:<- diamonds %>%
diamonds2 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.