6 Häufigkeiten
In diesem Kapitel betrachten wir ein einzelnes Merkmal \(X\). Man spricht in diesem Fall von univariaten Daten (engl. univariate data). Es handelt sich normalerweise um eine einzelne Spalte eines Dataframes.
Die beobachteten Werte von \(X\) schreiben wir allgemein als \[
x_1, x_2, ..., x_n.
\] Es gibt also Informationen zu insgesamt \(n\) Merkmalsträgern, das können Individuen, Haushalte, Firmen, Länder etc. sein. Man nennt die Daten \(x_1,...,x_n\) auch Urliste. In R handelt es sich bei der Urliste um eine Spalte eines Dataframes, und zwar in Form eines Vektors der Länge \(n\). Wir laden wieder den Beispiel-Dataframe imdb
, der eine Auswahl aus der International Movie Database enthält.
<- read_csv("../data/imdb.csv", col_types = "ccnfnnnnnn")
imdb head(imdb)
# A tibble: 6 × 10
title_id title year genre duration avgvote budget grossinc users_rev
<chr> <chr> <dbl> <fct> <dbl> <dbl> <dbl> <dbl> <dbl>
1 tt0035423 Kate & Leopo… 2001 Come… 118 6.4 48 76.0 341
2 tt0118589 Glitter 2001 Drama 104 2.2 22 5.27 319
3 tt0120467 Vulgar 2000 Crime 87 5.3 0.12 0.0159 119
4 tt0120630 Galline in f… 2000 Anim… 84 7 45 225. 393
5 tt0120667 I Fantastici… 2005 Acti… 106 5.7 100 334. 1029
6 tt0120679 Frida 2002 Biog… 123 7.4 12 56.3 297
# ℹ 1 more variable: critics_rev <dbl>
Es gibt in R mehrere Wege, um ein Merkmal als Vektor aus einem Dataframe heraus zu ziehen. Zwei dieser Wege haben wir im Kapitel 2.3 bereits kennen gelernt. Hier sind sie erneut, ergänzt um eine dritte Möglichkeit:
- Spaltennummer in doppelten Klammern
<- imdb[[4]] x
- Auswahl mit dem Dollarzeichen und dem Spaltennamen
<- imdb$genre x
- Der pull-Befehl in tidyverse
<- pull(imdb, genre) x
oder
<- imdb %>% pull(genre) x
In diesem Beispiel ist x
nun ein Vektor, der die univariaten Daten zum Ursprungsland der Filme im Dataframe imdb
enthält. Wenn man den Vektor (also die Urliste) mit dem Befehl
print(x)
anzeigt, denn erhält man schlicht eine Auflistung aller Werte. Interaktiv kann man auch einfach nur x
eingeben. Da der Vektor mit 5841 Elementen sehr lang ist, wird der Output hier nicht gezeigt.
Um den Umfang (engl. size) \(n\) der Population zu bestimmen, kann man entweder die erste Dimension des Dataframes auswählen,
<- dim(imdb)[1] n
oder man ermittelt die Länge des aus dem Dataframe herausgezogenen Vektors,
<- length(x) n
In beiden Fällen erhält man den Wert
print(n)
[1] 5841
Wenn eine Variable \(X\) nur relativ wenige verschiedene Werte bzw. Ausprägungen annimmt, kann eine Häufigkeitsauszählung interessante Einblicke bringen. Nominale Variablen haben oft nur recht wenige Werte, beispielsweise hat das Geschlecht nur die Ausprägungen “männlich” und “weiblich” (oder einige weitere Ausprägungen wie “divers”). Auch manche metrischen Variablen haben nur wenige Ausprägungen, z.B. die Anzahl der Personen, die in einem Haushalt leben. Für solche metrischen Variablen sind Häufigkeiten gut darstellbar und für das Verständnis der Daten hilfreich. Wenn die Anzahl der möglichen Werte dagegen sehr groß ist, wie z.B. beim Einkommen, sind Häufigkeiten nutzlos.
6.1 Absolute Häufigkeiten
Die absolute Häufigkeit (engl. absolute frequency) gibt für jeden möglichen Wert (Ausprägung) an, wie oft dieser Wert angenommen wird. Wenn wir die möglichen Werte mit \(\xi_1,..., \xi_J\) bezeichnen (diese griechischen Buchstaben werden xi ausgesprochen), dann sind \[ n_j = \#\{x_i = \xi_j\} \] für \(j=1,...J\) die absoluten Häufigkeiten. Das Hashtag-Zeichen steht für “Anzahl”. Die absolute Häufigkeit \(n_j\) gibt also an, bei wie vielen Merkmalsträgern die Variable \(X\) den Wert \(\xi_j\) annimmt. Eine Tabelle der Form \[ \begin{array}{cc}\hline \text{Ausprägung} & \text{abs. Häufigkeit}\\ \hline \xi_1 & n_1 \\ \xi_2 & n_2 \\ \xi_3 & n_3 \\ \vdots & \vdots\\ \xi_J & n_J \\\hline \end{array} \] heißt (absolute) Häufigkeitstabelle. Vorsicht: Nicht jede Tabelle ist eine Häufigkeitstabelle. Auch Urlisten, also \(x_1,\ldots,x_n\), werden oft tabellarisch dargestellt. Dann stehen in der linken Spalte die Indexwerte \(1,\ldots,n\) und in der rechten Spalte die Beobachtungen \(x_1,\ldots,x_n\). Überlegen Sie bei einer Tabelle immer, ob es sich um eine tabellarisch dargestellte Urliste oder eine Häufigkeitstabelle handelt.
Die Summe aller absoluten Häufigkeiten ergibt den gesamten Populationsumfang, \[ \sum_{j=1}^J n_j = n. \]
In R gibt es mehrere Wege, die absoluten Häufigkeiten auszuzählen.
Wenn die Daten in Form eines Vektors vorliegen, lautet der R-Befehl für die absoluten Häufigkeiten table
. Er liefert folgenden Output (für einen Vektor x
):
table(x)
x
Comedy Drama Crime Animation Action Biography Horror Adventure
1460 1273 399 334 1426 342 326 253
Thriller
28
Offenbar ist die Tabelle hier nicht senkrecht, sondern waagerecht angeordnet. Man erkennt, dass in dem Dataframe besonders viele Komödien und Actionfilme zu finden sind, aber nur wenige Thriller.
Die Häufigkeiten lassen sich auch ermitteln, wenn die Variable nicht als Vektor aus dem Dataframe gezogen wurden. Man verwendet für die Auszählung die Funktion group_by
, um den Dataframe nach den Ausprägungen der Variable zu gruppieren, für die man die Häufigkeiten berechnen will. Anschließend kann man in der summarise
-Funktion mit n()
die Häufigkeiten ermitteln:
group_by(imdb, genre) %>%
summarise(n=n())
# A tibble: 9 × 2
genre n
<fct> <int>
1 Comedy 1460
2 Drama 1273
3 Crime 399
4 Animation 334
5 Action 1426
6 Biography 342
7 Horror 326
8 Adventure 253
9 Thriller 28
Ein Vorteil dieses Vorgehens ist, dass man den Output leicht umsortieren kann, z.B. nach der Häufigkeit absteigend:
group_by(imdb, genre) %>%
summarise(n=n()) %>%
arrange(desc(n))
# A tibble: 9 × 2
genre n
<fct> <int>
1 Comedy 1460
2 Action 1426
3 Drama 1273
4 Crime 399
5 Biography 342
6 Animation 334
7 Horror 326
8 Adventure 253
9 Thriller 28
Wenn die Häufigkeiten nicht in Form einer Tabelle, sondern als Grafik dargestellt werden sollen, kann man in ggplot
folgendermaßen vorgehen.
ggplot(imdb, aes(x=genre)) + geom_bar()
Zur Erklärung: Das erste Argument in ggplot
(also imdb
) ist der Dataframe, der die Variable enthält. Mit Hilfe von aes
wird festgelegt, welche Variable als Aesthetic gezeigt werden soll. Hier wird (im Gegensatz zu einem Streudiagramm) nur eine Variable ausgewählt, die Angabe x=
legt fest, dass die Ausprägungen entlang der x-Achse angezeigt werden sollen. Nach dem Pluszeichen folgt die Angabe, durch welche geometrische Form die Darstellung erfolgen soll, hier also durch ein Balkendiagramm (bar plot).
Wenn man als Aesthetic y=
eingibt, werden die Ausprägungen entlang der y-Achse angezeigt, also als horizontale Balken. Das ist manchmal empfehlenswert, weil sich die Beschriftungen der Balken bei längeren Labels dann nicht überlappen.
ggplot(imdb, aes(y=genre)) + geom_bar()
Das Lesen eines Balkendiagramms ist einfacher, wenn die Balken nach ihrer Länge geordnet werden. Das ist in R leider nicht auf elementare Weise möglich, wenn die Variable nominal skaliert und als Datentyp factor
gespeichert ist. Das tidyverse
-Paket forcats
bietet eine Reihe von Funktionen, mit denen man Variablen vom Datentyp factor
bearbeiten kann. Als Beispiel wird hier gezeigt, wie mit der Funktion fct_infreq
die Ausprägungen der factor
-Variable genre
der Häufigkeit nach geordnet werden können.
ggplot(imdb, aes(x=fct_infreq(genre))) + geom_bar()
6.2 Relative Häufigkeiten
Neben den absoluten Häufigkeiten sind oft auch die relativen Häufigkeiten (engl. relative frequencies) von Interesse. Absolute Häufigkeiten sind Anzahlen, relative Häufigkeiten sind Anteile, sie werden durch eine Zahl zwischen 0 und 1 oder in Prozent (zwischen 0 und 100) angegeben. Die relative Häufigkeit der Ausprägung \(\xi_j\) für \(j=1,...,J\) ist \[ f_j=\frac{n_j}{n}, \] wobei \(n\) der Umfang der Population ist. Da die Summe aller absoluten Häufigkeiten dem Populationsumfang entspricht, gilt für die relativen Häufigkeiten \[ \sum_{j=1}^J f_j =1. \] In Tabellenform sieht die relative Häufigkeitsverteilung dann so aus: \[ \begin{array}{cc}\hline \text{Ausprägung} & \text{rel. Häufigkeit}\\ \hline \xi_1 & f_1 \\ \xi_2 & f_2 \\ \xi_3 & f_3 \\ \vdots & \vdots\\ \xi_J & f_J \\\hline \end{array} \] Für die Ermittlung der relativen Häufigkeiten gibt es wieder mehrere Wege in R.
Wenn die Daten als Vektor vorliegen, kann man die Anteile einer Tabelle bestimmen, indem man die Funktion proportions
auf das table
-Objekt wendet.
proportions(table(x))
x
Comedy Drama Crime Animation Action Biography Horror
0.24995720 0.21794213 0.06831022 0.05718199 0.24413628 0.05855162 0.05581236
Adventure Thriller
0.04331450 0.00479370
Der Output ist nicht gut lesbar, weil die Zahl der Nachkommastellen zu groß ist. Mit der Funktion round
runden wir die Ausgabe auf eine vorgegebene Anzahl von Nachkommastellen, z.B. auf vier Stellen:
round(proportions(table(x)), 4)
x
Comedy Drama Crime Animation Action Biography Horror Adventure
0.2500 0.2179 0.0683 0.0572 0.2441 0.0586 0.0558 0.0433
Thriller
0.0048
Wir sehen, dass ein Viertel der Filme Komödien sind. Der Anteil der Actionfilme ist ebenfalls fast ein Viertel. Dagegen sind nur 0.5 Prozent der Filme im Datensatz Thriller.
Mit Hilfe von group_by
und summarise
können wir die relativen Häufigkeiten errechnen, wenn vorher der Populationsumfang n
gespeichert wurde.
<- dim(imdb)[1]
n group_by(imdb, genre) %>%
summarise(f = round(n()/n, 4)) %>%
arrange(desc(f))
# A tibble: 9 × 2
genre f
<fct> <dbl>
1 Comedy 0.25
2 Action 0.244
3 Drama 0.218
4 Crime 0.0683
5 Biography 0.0586
6 Animation 0.0572
7 Horror 0.0558
8 Adventure 0.0433
9 Thriller 0.0048
Die grafische Darstellung von relativen Häufigkeiten mit ggplot
ist leider etwas komplizierter und sieht etwas unsystematisch aus.
ggplot(imdb, aes(x=genre)) +
geom_bar(aes(y=after_stat(count/sum(count))))
Die Angabe in geom_bar
besagt, dass die relativen Häufigkeit als Aesthetic in Richtung der y-Achse interpretiert werden sollen. Die Schreibweise after_stat
steht für eine Variable in ggplot
, die intern aus den Daten der Variable genre
berechnet wird. In diesem Fall wird die absolute Häufigkeit count
durch die Summe der absoluten Häufigkeiten dividiert, es handelt sich also um die relativen Häufigkeiten.
Eine alternative Möglichkeit ist der Umweg über die relative Häufigkeitstabelle. Zunächst wird die Häufigkeitstabelle als eigener Dataframe gespeichert.
<- dim(imdb)[1]
n <- group_by(imdb, genre) %>%
relTable summarise(f = round(n()/n, 4))
Anschließend wird nicht der Inhalt des Dataframes imdb
, sondern die Häufigkeitstabelle relTable
grafisch dargestellt. Dabei muss jedoch beachtet werden, dass die rechte Spalte die Häufigkeiten \(f_j\) enthält, nicht die einzelnen Merkmalswerte \(x_i\). Diese Information übermitteln wir an ggplot
durch die Angabe stat="identity"
als Argument der Funktion geom_bar
:
ggplot(relTable, aes(x=genre, y=f)) +
geom_bar(stat="identity")
6.3 Klassierte Daten
Häufigkeiten sind für das Verständnis der Daten nur dann hilfreich, wenn die Originalinformationen ausreichend stark verdichtet werden. Das Beispiel zu den Genres in der Movie-Database ist eine deutliche Verdichtung, denn aus den 5841 Original-Ausprägungen wurde durch die Auszählung der Häufigkeiten eine übersichtliche Tabelle mit nur noch 9 Zeilen (nämlich eine für jedes Genre).
Wenn wir die Verteilung der Variable duration
darstellen wollen, ist eine Häufigkeitsauszählung nicht sinnvoll, weil diese Variable 129 verschiedene Werte annimmt - viele davon nur ein einziges Mal.
Die Information über die Verteilung von Variablen, die viele verschiedene Werte annehmen, lassen sich gut verdichten und grafisch darstellen, indem man Klassen (Intervalle) bildet und dann auszählt, wie viele Werte in jeder Klasse liegen.
Wenn wir insgesamt \(J\) Klassen bilden, dann bezeichnen wir die Klassengrenzen mit \[ a_0 < a_1 < a_2 < \ldots < a_J \] Die \(i\)-te Klasse ist das Intervall \((a_{i-1}, a_i]\), d.h. die Intervallobergrenze zählen wir zum Intervall dazu, die Untergrenze nicht, sie zählt zu der nächsttieferen Klasse. Aus praktischen Gründen wird oftmals die unterste Klasse als einzige so definiert, dass auch die Untergrenze zum Intervall dazugehört, d.h. das erste Intervall ist \([a_0,a_1]\). Um die Notation im folgenden einfach zu halten, wird dieser Spezialfall nicht jedesmal extra erwähnt. Damit alle Werte \(x_1,\ldots, x_n\) abgedeckt sind, muss gelten \(a_0 \le \min_i x_i\) und \(a_J\ge \max_i x_i\).
Die absoluten Klassenhäufigkeiten sind für \(j=1,\ldots,J\) \[
n_j = \# \{x_i \in (a_{j-1},a_j]\}
\] und die entsprechenden relativen Klassenhäufigkeiten sind \[
f_j = \frac{n_j}{n}.
\] In R ist eine Klassierung sehr einfach möglich mit der Funktion cut
. Wenn die Daten in dem Vektor x
gespeichert sind, kann man mit der Funktion
<- cut(x, breaks=J) x_klass
den Vektor der zugehörigen klassierten Werte erzeugen. Dabei gibt \(J\) an, wie viele Intervalle gebildet werden sollen. Die Intervallgrenzen sind in der Standardeinstellung alle gleich weit voneinander entfernt. Das ist nicht immer sinnvoll, manchmal möchte man einige Intervalle schmaler, andere dagegen breiter haben. Oft ist es auch gewünscht, dass die Klassengrenzen keine “krummen” Werte sind. In diesen Fällen ist es möglich, mit der breaks
-Option alternativ einen Vektor von Klassengrenzen in die Funktion zu übergeben. Angenommen, der Vektor a
enthält die Klassengrenzen, dann lautet der Aufruf
<- cut(x, breaks=a) x_klass
Wenn einige Werte des Vektors x
außerhalb aller Intervalle liegen, kommt es zu einer Fehlermeldung.
Standardmäßig gehört beim cut
-Befehl die Untergrenze nicht zum Intervall dazu (sondern zum nächsttieferen Intervall). Will man für das unterste Intervall eine Ausnahme machen und \(a_0\) zum ersten Intervall gehören lassen, kann man das durch die Option include.lowest
erreichen:
cut(x, breaks=a, include.lowest=TRUE)
Die klassierten Daten x_klass
werden in R als Faktor (also als nominal skaliert) behandelt. Sie können ganz normal mit der Funktion table
ausgezählt werden.
Beispiel:
Wir möchten die Länge (in Minuten) der Filme aus der Internet Movie Database analysieren. Dazu erstellen wir eine Tabelle mit den absoluten Häufigkeiten. Die Daten werden klassiert, weil es deutlich über 100 verschiedene Minutenwerte gibt, die in dem Datensatz imdb
vorkommen.
Zuerst ziehen wir die Variable duration
aus dem Dataframe heraus und speichern sie in einem Vektor mit dem Namen laenge
ab.
<- imdb$duration
laenge head(laenge)
[1] 118 104 87 84 106 123
Der erste Film im Datensatz hat also eine Länge von 118 Minuten, der zweite von 104 Minuten usw.
Nun werden die Beobachtungen klassiert. Als Klassengrenzen verwenden wir den Vektor
<- c(60,80,85,90,95,100,110,130,150,230) a
Dieser Vektor enthält die kleinste Duration, die im Datensatz vorkommt (nämlich 60), die Obergrenze des letzten Intervalls ist jedoch größer als die maximale Filmlänge (nämlich größer als 224). Der Vektor der klassierten Beobachtungen wird als laenge_kl
gespeichert. Damit der kleinste Wert dem ersten Intervall zugewiesen wird, setzen wir die Option include.lowest=TRUE
.
<- cut(laenge, breaks=a, include.lowest=TRUE) laenge_kl
Die absoluten Häufigkeiten können nun durch mit table
ausgezählt werden.
table(laenge_kl)
laenge_kl
[60,80] (80,85] (85,90] (90,95] (95,100] (100,110] (110,130] (130,150]
145 276 661 776 807 1297 1366 389
(150,230]
124
6.4 Histogramme
Die grafische Darstellung der Klassenhäufigkeiten erfolgt in Form von Histogrammen (engl. histograms). Bei einem Histogramm werden die Klassen auf der x-Achse abgetragen. Über jeder Klasse bildet man einen Balken, dessen Fläche proportional zur (absoluten oder relativen) Häufigkeit ist. Die Breite des Balkens \(j\) ist also \(a_j-a_{j-1}\), und die Höhe ist \(f_j/(a_j-a_{j-1})\) (oder \(n_j/(a_j-a_{j-1})\)). Wenn die Klassenbreiten alle gleich sind, sind die Histogrammbalken schlicht proportional zur (absoluten oder relativen) Häufigkeit.
Mit ggplot
ist es sehr einfach Histogramme zu erstellen, z.B.
ggplot(imdb, aes(duration)) + geom_histogram(bins=40)
Zur Erläuterung: Das erste Argument in der Funktion ggplot
ist wieder der Dataframe, der die Variable enthält, für die wir das Histogramm zeichnen wollen. Das zweite Argument gibt das Aesthetic an, also die Variable, die grafisch dargestellt werden soll, hier ist es die Dauer des Films duration
. Nach dem Pluszeichen folgt das Geom, hier ist es das Histogramm. Als Argument ist die Anzahl der Balken (bins) angeben. In der Abbildung ist auf der y-Achse die absolute Zahl von Filmen in jeder Klasse abgetragen.
Man erkennt an der Abbildung, dass es sehr viele Filme gibt, die etwa 90 bis 100 Minuten dauern. Es gibt in dem Datensatz kaum Filme, die kürzer sind als 75 Minuten oder länger als 150 Minuten.
Als Alternative zur Anzahl der Klassen (bins
) ist es auch möglich einen Vektor von Klassengrenzen als Argument zu übergeben. Das folgende Beispiel zeigt, wie man die Klassengrenzen vorgibt. Leider ist das Vorgehen etwas komplizierter, wenn die Klassengrenzen nicht äquidistant sind, weil zusätzlich zu den Klassengrenzen (breaks
) noch sichergestellt werden muss, dass die Flächeninhalte der Balken proportional zur Klassenbreite sind. Letzteres erfolgt durch das zusätzliche Argument aes(y=..density..)
.
<- c(60,75,80,85,90,95,100,110,130,150,200,230)
a ggplot(imdb, aes(duration)) +
geom_histogram(breaks=a, aes(y=after_stat(density)))
In dieser Abbildung ist auf der y-Achse die relative Häufigkeit abgetragen, und zwar so skaliert, dass die Fläche der Balken proportional zur relativen Häufigkeit ist. Daraus folgt, dass die Gesamtfläche unter dem Histogramm den Wert 1 annimmt, denn \[\begin{align*} \sum_{j=1}^J Breite_j \times Höhe_j &= \sum_{j=1}^J (a_j-a_{j-1})\frac{f_j}{a_j-a_{j-1}}\\ &= \sum_{j=1}^J f_j\\ &= 1 \end{align*}\]
(Einen knappen Überblick über die Verwendung des Summenzeichens findet man im Anhang C.)
6.5 Kerndichten
Die genaue Form eines Histogramms hängt davon ab, wie man die Klassengrenzen auswählt. Außerdem ändert sich die Höhe der Balken natürlich plötzlich, wenn man in die nächste Klasse wechselt. Man kann eine geglättete Variante des Histogramms definieren, sie wird Kerndichteschätzung (engl. kernel density estimation) genannt. Auf die genaue Definition gehen wir in diesem Kurs nicht ein.
Die Kerndichte kann in ggplot
durch das Geom geom_density
dargestellt werden.
ggplot(imdb, aes(duration)) +
geom_density()
Dass es sich tatsächlich um ein geglättetes Histogramm handelt, erkennt man gut, wenn beide Darstellungsweise gemeinsam in einer Abbildung zu sehen sind.
ggplot(imdb, aes(duration)) +
geom_histogram(breaks=a, aes(y=after_stat(density)))+
geom_density()