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()
library(nycflights13)
Bei einigen Funktionen muss explizit angegeben werden, dass
NAs
entfernt werden sollen, damit beispielsweise der
Mittelwert berechnet werden kann:
%>%
flights group_by(year, month, day) %>%
summarise(mean = mean(dep_delay))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
Falls na.rm = TRUE
nicht gesetzt wird, ist das Ergebnis
immer NA
, sofern fehlende Werte in den Daten vorhanden
sind.
Daher haben die Funktionen zur Aggregation immer ein
na.rm
Parameter:
%>%
flights group_by(year, month, day) %>%
summarise(mean = mean(dep_delay, na.rm = TRUE))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
Anstatt bei jeder Funktion na.rm
zu setzen, können wir
den Datensatz zuvor auch vorbereiten, indem die NAs
in
einem ersten Schritt entfernt werden. Je nach Fragestellung kann das
sinnvoll sein, jedoch gehen hierdurch auch Daten verloren.
<- flights %>%
nicht_annulliert filter(!is.na(dep_delay), !is.na(arr_delay))
%>%
nicht_annulliert group_by(year, month, day) %>%
summarise(mean = mean(dep_delay))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
Kleiner Hinweis: die Gestaltung, wie Leerzeichen und Zeilenumbrüche gesetzt werden, das obliegt Ihnen.
Der Übersichtlichkeit wegen wird meistens nach %>%
ein Zeilenumbruch eingeleitet.
Um gleichzeitig zu den Zusammenfassungen einzusehen, wie sich die
Anzahl der Gruppen verhält, kann die Zählfunktion n()
oder
der Befehl sum(!is.na(x))
verwendet werden. So können Sie
beispielsweise direkt einsehen, ob eine Gruppe mit starken Unterschieden
gegebenfalls sehr klein ist.
Betrachten wir für einen Moment die durchschnittliche Verspätung für
jedes Flugzeug (identifiziert durch tailnum
):
<- nicht_annulliert %>%
delays group_by(tailnum) %>%
summarise(avg_delay = mean(arr_delay))
delays
# Verteilung der Häufigkeiten der Delays:
ggplot(data = delays, mapping = aes(x = avg_delay)) +
geom_freqpoly(binwidth = 10)
Wir sehen, dass der Großteil der Flugzeuge sich um Verspätungen im Bereich von 0 Minuten bewegen. Die Kurve ist lecht nach rechts verschoben, denn Verspätungen sind häufiger als frühzeitige Ankünfte. Es ist aber auch einzusehen, dass es Flugzeuge mit einer durchschnittlichen Verspätung von über 5 Stunden gibt.
Wenn wir nun einen Blick auf die Flugzeuge und ihre Flughäufigkeit werfen, sehen wir Folgendes:
<- nicht_annulliert %>%
delays group_by(tailnum) %>%
summarise(avg_delay = mean(arr_delay, na.rm = TRUE),
anzahl = n())
delays
ggplot(data = delays, mapping = aes(x = anzahl, y = avg_delay)) +
geom_point(alpha = 1/10)
Sie sehen, dass die Variation (Schwankungen) bei Flügen mit einer geringen Frequenz größer ist. Die Variation (z. B. der Mittelwerte) nimmt mit der Stichprobengröße ab.
Das ist eine typische Beobachtungen und hebt die Wichtigkeit der Stichprobengröße hervor, um aussagekräftige Ergebnisse abzuleiten.
Dementsprechen kann es sinnvoll sein, Gruppen mit einer geringen Häufigkeit zu filtern, sodass “Ausreißer”/seltene Beobachtungen, die eine große Variation verursachen, aus der Verteilung entnommen werden.
Wir könnten nun also zusätzlich nach der Anzahl in unserem Dataframe filtern:
%>%
delays filter(anzahl > 25) %>%
ggplot(mapping = aes(x = anzahl, y = avg_delay)) +
geom_point(alpha = 1/10)
Achten Sie auf die Achsen! Die durchschnittliche Verspätung überschreitet 60 Minuten nicht mehr.
Es ist hierbei lediglich etwas umständlich, von der Pipe
%>%
zu dem Operator +
für ggplot Grafiken
zu wechseln.
summarise()
An dieser Stelle eine Liste von weiteren nützlichen Funktionen mit Hilfe dessen Daten zusammengefasst werden können:
median(x)
, welcher den Mittelpunkt der Daten
beschreibt (50% der Daten liegen ober/unter diesem Wert). Häufig bietet
der Median eine solide Alternative zu dem Mittelwert
mean(x)
an, denn er ignoriert Ausreißer:%>%
nicht_annulliert group_by(year, month, day) %>%
summarise(median_delay = median(arr_delay),
avg_delay = mean(arr_delay))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
var(x)
,
Standardabweichung sd(x)
, Interquartilsbereich
IQR(x)
, Median der absoluten Abweichungen
mad(x)
. Hierbei sind IQR(x)
und
mad(x)
robustere Alternativen zu sd(x)
:# Die Distanz zu manchen Flughäfen ist variabler (größere SD) als zu anderen:
%>%
nicht_annulliert group_by(dest) %>%
summarise(distanz_sd = sd(distance),
distanz_iqr = IQR(distance),
distanz_mad = mad(distance)) %>%
arrange(desc(distanz_sd))
min(x)
, max(x)
,
quantile(x, 0.25)
. Das Quantil hier beschreibt die Grenze,
bei der x
größer als 25% und kleiner als 75% der Daten
ist:# Erster, letzter Flug und der Moment, wo 90% der Flüge am Tag vorbei sind:
%>%
nicht_annulliert group_by(year, month, day) %>%
summarise(erster_flug = min(dep_time),
letzter_flug = max(dep_time),
fast_alle_fluege_vorbei = quantile(dep_time, 0.9)
)
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
first(x)
,
nth(x, 10)
, last(x)
geben den ersten, den
zehnten und den letzten Wert des Dataframes aus:%>%
nicht_annulliert group_by(year, month, day) %>%
summarise(erster_flug = first(dep_time),
letzter_flug = last(dep_time),
zehnter_flug = nth(dep_time, 10))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
Hinweis: diese Funktionen lassen sich ergänzend zu Sortierungen mit
min_rank()
verwenden, wobei hier die gesamten Einträge
erhalten bleiben:
%>%
nicht_annulliert group_by(year, month, day) %>%
mutate(abflug_rang = min_rank(desc(dep_time))) %>%
filter(abflug_rang %in% range(abflug_rang))
n()
lässt sich die Anzahl
der nicht-fehlende Werte durch sum(!is.na(x))
bestimmen und
die Anzahl an einzigartigen Werten mit n_distinct(x):
%>%
flights group_by(dest) %>%
summarise(einzigartige_flugzeuge = n_distinct(carrier),
alle_fluege = n(),
annullierte_fluege = sum(is.na(dep_time))) %>%
arrange(desc(einzigartige_flugzeuge))
Um das zu vereinfachen gibt es zusätzlich die count(x)
Hilfsfunktion:
%>%
nicht_annulliert count(dest)
Falls nicht nur die Häufigkeit der Gruppe gezählt werden soll,
sondern die Summe einer Variable für jede Gruppe (z. B. die gesamte
Flugzeit aller Flugzeuge zu einem Flughafen), so kann das Argument
wt
verwendet werden:
%>%
nicht_annulliert count(dest, wt = air_time)
Für das Zählen bzw. Summen von Variablen für Gruppen kann also mit
group_by()
gruppiert werden und dann die gewünschte
Operation verwendet werden oder direkt count()
.
sum(x > 10)
aus, wie viele Werte x
größer als 10 sind und mean(x == 0)
, wie viel Prozent der
Werte von x
einen Wert von 0 haben:# Anzahl der Flüge, die vor 5 Uhr stattgefunden haben:
%>%
nicht_annulliert group_by(year, month, day) %>%
summarise(fruehe_fluge = sum(dep_time < 500))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# Verhältnis der Flüge mit einer Verspätung von über 60 Minuten:
%>%
nicht_annulliert group_by(year, month, day) %>%
summarise(verspaetung_verhaeltnis = mean(arr_delay > 60))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
Sie sehen an dieser Stelle aber auch, dass einige Operationen redundant sind. Sie können auf verschiedenen Wegen dieselben Ergebnisse erhalten.
Wenn mehrere Variablen zur Gruppierung verwendet werden, wird mit
jedem summarise()
Befehl eine Gruppe verworfen:
<- group_by(flights, year, month, day)
taeglich <- summarise(taeglich, anzahl_fluege = n())) (pro_tag
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
<- summarise(pro_tag, anzahl_fluege = sum(anzahl_fluege))) (pro_monat
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.
<- summarise(pro_monat, anzahl_fluege = sum(anzahl_fluege))) (pro_jahr
Hierbei muss genau bedacht werden, was in jedem Schritt gemacht wird. Ansonsten entstehen Probleme beispielsweise, wenn hier schrittweise der Median berechnet werden würde.
Mit Hilfe von ungroup()
wird die Gruppierung rückgängig
gemacht:
%>%
taeglich ungroup() %>%
summarise(anzahl_fluege = n())
Neben summarise()
lassen sich auch mutate()
und filter()
mit Gruppierungen verwenden:
%>%
flights group_by(year, month, day) %>%
filter(rank(desc(arr_delay)) < 5)
<- flights %>%
beliebte_ziele group_by(dest) %>%
filter(n() > 365)
beliebte_ziele
%>%
beliebte_ziele filter(arr_delay > 0) %>%
mutate(proportion_delay = arr_delay / sum(arr_delay)) %>%
select(year:day, dest, arr_delay, proportion_delay)
Abhängig von der Anwendung können gruppierte Filterung unerwartete Nebeneffekte haben, daher ist hierbei Vorsicht geboten.
In diesem Abschnitt soll erneut der diamonds
Datensatz
betrachtet werden:
library(tidyverse)
diamonds
Überlegen Sie zunächst, nach welchen Variablen sich der Datensatz
diamonds
gruppieren lässt. Denken Sie hierbei besonders an
die unterschiedlichen Datentypen.
Welche Farbe der Diamanten hat den geringsten Wert?
Wie erklären Sie sich das Ergebnis?
Erstellen und betrachten Sie hierzu auch ein Streudiagramm mit
dem Zusammenhang von carat
und price
bezüglich
der Farben.
Gruppieren Sie die Diamanten nach der Variable cut
und berechnen Sie die Mittelwerte für carat
,
price
und depth
.
Erkennen Sie hierbei Zusammenhänge?
Stellen Sie die durchschnittlichen Preise durch Balkendiagramme
für alle Kategorien von cut
dar.
Erstellen Sie einen Boxplot mit den Preisen für jede Kategorie
cut
. Vergleichen Sie das Ergebnis mit dem
Balkendiagramm.
Gruppieren Sie die Diamanten nach carat
und
berechnen Sie Minimum, Maximum und Mittelwert von
price
.
Erstellen Sie auf Grundlage der Mittelwerte und der Variable
carat
ein Streudiagramm.
Erstellen Sie ein Streudiagramm für alle Einträge mit
carat
und price
. Fügen Sie zusätzlich die
bestimmten Mittelwerte in die Grafik mit geom_line()
ein.
Setzen Sie hierzu stat = "smooth"
.
Entfernen Sie zuletzt alle Einträge, die seltener als 20 mal auftreten.
Finden Sie für jede Kategorie von color
die Tiefen
(depth
), bei denen mehr als 25%, 50% und 75% der Diamanten
einen geringen Wert aufweisen.
Finden Sie für jeden cut
den teuersten und den
günstigsten Diamanten. Entspricht das Ergebnis Ihren Erwartungen? Woran
kann dieser Zusammenhang liegen?
Betrachten Sie neben dem cut
auch die Klarheit
clarity
und geben Sie den Anteil und die Anzahl von
Diamanten mit einem Preis von über 10000 aus.
Wählen Sie im Anschluss die ersten fünf aller Ergebnisse aus. Stoßen Sie hierbei auf ein Problem?
Erstellen Sie eine Grafik mit Histogrammen der Preise für alle
Kategorien von cut
und clarity
(durch
facet_grid()
).
Markieren Sie in der Grafik die Bereiche rot, bei denen der Preis über 10000 liegt. Vergleichen Sie die Grafik mit den Anteilen, die Sie zuvor berechnet haben.