Kapitel 7 Deskriptive Statistik

Die beschreibende Statistik bietet neben Informationen über die Verteilungen und Zusammenhänge der interessierenden Merkmale ebenfalls die Möglichkeit, unplausible Werte zu identifizieren, die mit den im vorherigen Kapitel gelernten Methoden entfernt oder korrigiert werden müssen. Die Angabe der in diesem Kapitel eingeführten Lage- und Streuungsmaße, Häufigkeiten und Zusammenhangsmaße dürfen in keiner wissenschaftlichen Arbeit fehlen.

7.1 Lagemaße und Streuungsmaße

Für dieses Kapitel müssen die Packages des tidyverse und remp installiert und geladen werden.

library(tidyverse)
library(remp)

Wir werden in diesem Kapitel den big5_mod Datensatz ohne die Spalte namens ID verwenden und bezeichnen diesen als big5_mod1.

big5_mod1 <- big5_mod |> select(-ID)
big5_mod1
# A tibble: 200 × 5
  Alter Geschlecht Extraversion Neurotizismus Gruppe
  <dbl> <chr>             <dbl>         <dbl> <fct> 
1    36 m                   3             1.9 Mittel
2    30 f                   3.1           3.4 Jung  
3    23 m                   3.4           2.4 Jung  
4    54 m                   3.3           4.2 Weise 
# ℹ 196 more rows

Einen ersten Überblick über die Daten können wir mit der direkt in R integrierten Funktion summary() erhalten, der wir lediglich den Namen des Datensatzes übergeben müssen.

summary(big5_mod1)
     Alter        Geschlecht         Extraversion   Neurotizismus      Gruppe   
 Min.   :13.00   Length:200         Min.   :2.300   Min.   :1.400   Jung  :147  
 1st Qu.:18.75   Class :character   1st Qu.:2.800   1st Qu.:2.700   Mittel: 39  
 Median :23.00   Mode  :character   Median :3.000   Median :3.100   Weise : 14  
 Mean   :26.48                      Mean   :3.076   Mean   :3.133               
 3rd Qu.:31.25                      3rd Qu.:3.300   3rd Qu.:3.600               
 Max.   :60.00                      Max.   :4.300   Max.   :4.600               

Hier sehen wir einen Vorteil von Faktoren. Während wir die Häufigkeiten der Altersgruppe ausgegeben bekommen, kann R für die Spalte mit Geschlecht lediglich die Anzahl an Character Werten zählen. Für weitere Informationen über Faktoren und deren Umgang schaue dir erneut Kapitel 4.3.2 und 6.10 an.

Falls wir gruppierte Lage- und Streuungsmaße erhalten oder andere Werte wie den Standardfehler ausrechnen möchten, müssen wir auf die summarise() Funktion aus dem Package dplyr (enthalten im tidyverse) zurückgreifen. Auch hier werden wir einzelne Befehle mit der Pipe (|>) aneinanderketten (siehe Kapitel 6.1).

Exemplarisch soll zunächst der Mittelwert mit der Funktion mean() und die Standardabweichung mit sd() berechnet werden. Beide Funktionen müssen wir dabei innerhalb der Funktion summarise() verwenden. Auf der linken Seite des Gleichheitszeichens stehen auch hier wieder die Namen der neu erstellten Spalten.

big5_mod1 |> 
  summarise(
    M = mean(Extraversion),
    SD = sd(Extraversion)
  )
# A tibble: 1 × 2
      M    SD
  <dbl> <dbl>
1  3.08 0.347

Zum Gruppieren der Variablen wird die Funktion group_by() verwendet.

big5_mod1 |> 
  group_by(Geschlecht) |> 
  summarise(
    M = mean(Extraversion),
    SD = sd(Extraversion)
  )
# A tibble: 2 × 3
  Geschlecht     M    SD
  <chr>      <dbl> <dbl>
1 f           3.05 0.358
2 m           3.11 0.328

Dabei können der Funktion beliebig viele Spalten übergeben werden. So könnte man bspw. nicht nur nach Geschlecht, sondern auch nach der Altersgruppe gruppieren.

big5_mod1 |> 
  group_by(Geschlecht, Gruppe) |> 
  summarise(
    M = mean(Extraversion),
    SD = sd(Extraversion)
  )
# A tibble: 6 × 4
# Groups:   Geschlecht [2]
  Geschlecht Gruppe     M    SD
  <chr>      <fct>  <dbl> <dbl>
1 f          Jung    3.07 0.373
2 f          Mittel  3.07 0.299
3 f          Weise   2.83 0.269
4 m          Jung    3.12 0.324
# ℹ 2 more rows

Alternativ können wir zum Gruppieren anstelle von group_by() auch das Argument .by hinzufügen. Während der Datensatz nach group_by() gruppiert ist, was bei weiterer Verwendung mit ungroup() aufgelöst werden müsste, gruppiert das .by Argument nur einmalig. Das Argument kann auch mit der in Kapitel 6.4 kennengelernten Funktion mutate() verwendet werden. Da die Ausgabe von summarise() in der Regel bereits ein Endergebnis darstellt, ist die Wahl zwischen group_by() oder .by größtenteils abhängig von persönlicher Präferenz.

big5_mod1 |> 
  summarise(
    M = mean(Extraversion),
    SD = sd(Extraversion),
    .by = c(Geschlecht, Gruppe)
  )
# A tibble: 6 × 4
  Geschlecht Gruppe     M    SD
  <chr>      <fct>  <dbl> <dbl>
1 m          Mittel  3.13 0.281
2 f          Jung    3.07 0.373
3 m          Jung    3.12 0.324
4 m          Weise   2.96 0.546
# ℹ 2 more rows

Neben der Funktionen für den Mittelwert und der Standardabweichung gibt es noch diverse weitere: n() für die Anzahl an Beobachtungen, min() und max() für Minimum und Maximum, var() für die Varianz, sqrt() für die Quadratwurzel, median() für den Median und quantile() zur Berechnung der jeweiligen Quantile.

Für die Berechnung des Standardfehlers teilen wir direkt innerhalb des Funktionsaufrufes die Standardabweichung durch die Wurzel aus der Anzahl der Personen.

big5_mod1 |> 
  summarise(
    N = n(), 
    Min = min(Alter),
    Mean = mean(Alter),
    Median = median(Alter),
    Max = max(Alter),
    SD = sd(Alter),
    SE = SD / sqrt(N)
  )

Grundsätzlich kann jede Funktion summarise() übergeben werden, die einen einzelnen Wert berechnet. Somit unterscheidet sich die Anwendung maßgeblich vom bereits kennengelernten mutate(). Dort musste die Ausgabe immer eine Reihe von Werten umfassen, die der Anzahl der Zeilen im Datensatz entspricht.

Auch hier können wir mehrere Spalten gleichzeitig mithilfe von across() auswerten (siehe Kapitel 6.4.2). Die Syntax ändert sich in dem Fall im Vergleich zu vorher. Hier müssen wir die verschiedenen Funktionen mit entsprechendem Namen innerhalb einer Liste übergeben. Listen als solche werden erst später eingeführt und müssen uns an dieser Stelle nicht weiter interessieren (siehe Kapitel 11.4).

big5_mod1 |> 
  summarise(across(
    .cols = Extraversion:Neurotizismus, 
    .fns = list(M = mean, SD = sd))
  )
# A tibble: 1 × 4
  Extraversion_M Extraversion_SD Neurotizismus_M Neurotizismus_SD
           <dbl>           <dbl>           <dbl>            <dbl>
1           3.08           0.347            3.13            0.682

Bei vielen Lagemaßen kann es schnell umständlich werden, jede einzeln aufzurufen. Wenn man keine Lust hat, dies jedes Mal manuell abzutippen, kann man auch direkt vereinfachte Funktionen zur deskriptiven Statistik verwenden. Das remp Package bietet die Funktion descriptive() an. Dieser muss man kein weiteres Argument übergeben. Es wird die Anzahl, das Minimum, das erste Quartil, der Mittelwert, der Median, das zweite Quartil, die Standardabweichung und der Standardfehler für sämtliche numerische Spalten zurückgegeben. Alle anderen Datentypen werden von dieser Funktion ignoriert.

big5_mod1 |> 
  descriptive()
# A tibble: 3 × 10
  Variable          N   Min    Q1  Mean Median    Q3   Max    SD    SE
  <chr>         <int> <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl>
1 Alter           200  13    18.8 26.5    23    31.2  60   11.4   0.8 
2 Extraversion    200   2.3   2.8  3.08    3     3.3   4.3  0.35  0.02
3 Neurotizismus   200   1.4   2.7  3.13    3.1   3.6   4.6  0.68  0.05

Auch hier können wir die Berechnungen auf dieselbe Art und Weise gruppieren.

big5_mod1 |> 
  group_by(Geschlecht) |> 
  descriptive() 

Alternativen für einen schnellen Überblick bieten beispielsweise auch das skimr Package mit der Funktion skim() oder describe() aus dem psych Package. Beide können auch nicht-numerische Spalten auswerten und erstere gibt zu jeder Spalte sogar ein kleines Histogramm aus.

Wie dir vielleicht bereits aufgefallen ist, sieht die Ausgabe von descriptive() anders aus als die von summarise(). Während erstere die Variablen untereinander in unterschiedliche Zeilen übersichtlich auflistet, fügt summarise() die Ergebnisse spaltenweise hinzu. Wenn wir denselben Output wie in descriptive() erreichen möchten, müssen wir zuerst den Datensatz in ein langes Format bringen (siehe Kapitel 6.6). Nun gruppieren wir nach der neuen Spalte namens Variable. Anschließend können wir wie gewohnt mit summarise() die deskriptiven Statistiken berechnen. Nichts anderes macht die Funktion descriptive() hinter den Kulissen.

big5_mod1 |> 
  pivot_longer(
    cols = c(Alter, Extraversion, Neurotizismus),
    names_to = "Variable",
    values_to = "Wert"
  ) |> 
  group_by(Variable) |> 
  summarise(
    Q1 = quantile(Wert, 0.25),
    Mean = mean(Wert),
    Q3 = quantile(Wert, 0.75),
    Schiefe = skewness(Wert),
    Kurtosis = kurtosis(Wert)
  )
# A tibble: 3 × 6
  Variable         Q1  Mean    Q3 Schiefe Kurtosis
  <chr>         <dbl> <dbl> <dbl>   <dbl>    <dbl>
1 Alter          18.8 26.5   31.2   1.34      3.97
2 Extraversion    2.8  3.08   3.3   0.761     3.95
3 Neurotizismus   2.7  3.13   3.6  -0.132     2.56

Beachte an dieser Stelle, dass für die Funktionen skewness() und kurtosis() das moments Package installiert und geladen sein muss.

Übung 7.1. (Noch nicht enthalten) Starte die Übung mit uebung_starten(7.1).

7.2 Häufigkeiten und Kontingenztafeln

Auch in diesem Unterkapitel verwenden wir unter anderem die Packages aus dem tidyverse.

library(tidyverse)

Eine Möglichkeit Häufigkeiten zu zählen, haben wir bereits mit n() innerhalb von summarise() kennengelernt. Eine Alternative stellt die count() Funktion aus selbigem Package dar. Hier können wir uns group_by() sparen und stattdessen die gruppierenden Spalten direkt in count() schreiben.

big5_mod |> 
  count(Geschlecht)
# A tibble: 2 × 2
  Geschlecht     n
  <chr>      <int>
1 f            118
2 m             82

Dabei können beliebig viele Spalten übergeben werden. Um die höchste Anzahl zu Beginn auszugeben, würden wir zusätzlich das sort Argument auf TRUE setzen.

big5_mod |> 
  count(Geschlecht, Gruppe, sort = TRUE)
# A tibble: 6 × 3
  Geschlecht Gruppe     n
  <chr>      <fct>  <int>
1 f          Jung      89
2 m          Jung      58
3 f          Mittel    20
4 m          Mittel    19
# ℹ 2 more rows

Zum Darstellen der Anteile der jeweiligen Gruppen, müssen wir innerhalb von mutate() die einzelnen Häufigkeiten durch Anzahl in der jeweiligen Gruppe teilen (siehe Kapitel 6.4).

big5_mod |>
  count(Geschlecht, Gruppe) |>
  mutate(Prop = n / sum(n))
# A tibble: 6 × 4
  Geschlecht Gruppe     n  Prop
  <chr>      <fct>  <int> <dbl>
1 f          Jung      89 0.445
2 f          Mittel    20 0.1  
3 f          Weise      9 0.045
4 m          Jung      58 0.29 
5 m          Mittel    19 0.095
6 m          Weise      5 0.025

Hier werden die Verhältnisse über alle Häufigkeiten berechnet, da count() keinen gruppierten Datensatz zurückgibt. Sollen die Häufigkeiten innerhalb einer Gruppe berechnet werden, muss zusätzlich mithilfe von group_by() explizit neu gruppiert werden.

big5_mod |>
  count(Geschlecht, Gruppe) |>
  group_by(Geschlecht) |> 
  mutate(Prop = n / sum(n)) |> 
  ungroup()
# A tibble: 6 × 4
  Geschlecht Gruppe     n   Prop
  <chr>      <fct>  <int>  <dbl>
1 f          Jung      89 0.754 
2 f          Mittel    20 0.169 
3 f          Weise      9 0.0763
4 m          Jung      58 0.707 
5 m          Mittel    19 0.232 
6 m          Weise      5 0.0610

Für die Erstellung von Kontingenztafeln, die wir mit statistischen Tests auswerten können, benötigen wir allerdings eine andere Funktion namens table(). Damit können wir ähnlich wie mit count() die Häufigkeiten kategorialer Merkmale abbilden. Die Werte müssen dabei als Wertereihe (bzw. Vektor) mit dem Dollar-Operator aus dem Datensatz extrahiert werden (siehe Kapitel 4.5).

table(big5_mod$Geschlecht)

  f   m 
118  82 

Zur Erstellung einer klassischen Vierfeldertafel wird eine zusätzliche Variable in die Funktion geschrieben. Wenn wir also untersuchen möchten, ob es Unterschiede in der Häufigkeit extrovertierter Menschen zwischen den Geschlechtern gibt, wählen wir erst die Spalte Extraversion des Datensatzes big5_mod und anschließend die Spalte Geschlecht. Beachte die logische Abfrage bei der Extraversion: wenn Extraversion größer als 3 ist, wird eine 1 und ansonsten eine 0 zurückgegeben. Mit dem optionalen Argument dnn können für eine bessere Übersichtlichtkeit die Dimensionsnamen festgelegt werden (hier z.B. Extrovertiert und Geschlecht).

tb <- table(big5_mod$Extraversion > 3, big5_mod$Geschlecht, 
            dnn = c("Extrovertiert", "Geschlecht"))
tb
             Geschlecht
Extrovertiert  f  m
        FALSE 66 43
        TRUE  52 39

Häufig fehlen zumindest ein paar Werte in den jeweiligen Spalten. Um diese ebenfalls ausgegeben zu bekommen, setzen wir das Argument useNA innerhalb der Funktion table() auf "ifany". Dadurch werden fehlende Werte nur dann angezeigt, falls diese tatsächlich vorkommen. Ein weiteres nützliches Argument ist exclude zum Ausschluss einer Faktorstufe für die Erstellung einer Kontingenztafel zur späteren Auswertung. Wäre bei der Variable Geschlecht eine dritte Stufe namens divers mit wenigen Beobachtungen vorhanden, könnten wir diese so ausblenden. Voraussetzung ist der Datentyp Faktor für die Variable (siehe Kapitel 4.3.2).

Es können beliebig viele Häufigkeiten von Merkmalen miteinander in Beziehung gesetzt werden. Allerdings sind mehr als drei Dimensionen nur selten sinnvoll zu interpretieren.

table(big5_mod$Extraversion > 3, 
      big5_mod$Geschlecht, 
      big5_mod$Neurotizismus > 3.5)
, ,  = FALSE

       
         f  m
  FALSE 47 39
  TRUE  33 23

, ,  = TRUE

       
         f  m
  FALSE 19  4
  TRUE  19 16

Nach Erstellen der Kontingenztafeln mithilfe von table() können die Verhältnisse der Merkmale mit der Funktion proportions() ausgegeben werden. Dies stellt eine Alternative zur bereits kennengelernten Kombination aus count() und mutate() dar.

proportions(tb)
             Geschlecht
Extrovertiert     f     m
        FALSE 0.330 0.215
        TRUE  0.260 0.195

Der Vorteil ist hier, dass wir zusätzlich bestimmen können, ob wir auf die Zeilen oder Spalten bedingen möchten. Zum Konditionieren auf die Zeilen, muss nur eine 1 hinzugefügt werden.

proportions(tb, 1)
             Geschlecht
Extrovertiert         f         m
        FALSE 0.6055046 0.3944954
        TRUE  0.5714286 0.4285714

Äquivalent dazu wird mit einer zusätzlichen 2 auf die Spalten bedingt.

proportions(tb, 2) 
             Geschlecht
Extrovertiert         f         m
        FALSE 0.5593220 0.5243902
        TRUE  0.4406780 0.4756098

Möchte man die Kontingenztafel für jede Spalte ausgeben lassen, kann table() ohne Klammern in der Funktion map() genutzt werden. Diese wendet table() auf jeden Spalte der Reihe nach an und speichert diese in Form einer Liste ab.

big5_mod |>
  map(table)

Zur besseren Übersicht schalten wir an dieser Stelle noch ein select() davor, um nur das Geschlecht und die Altersgruppe auszuwählen. Wir könnten aber auf dieselbe Art und Weise sämtlich Spalten in einer Kontingenztafel ausgeben lassen.

big5_mod |>
  select(Geschlecht, Gruppe) |>  
  map(table)
$Geschlecht

  f   m 
118  82 

$Gruppe

  Jung Mittel  Weise 
   147     39     14 

So können auch alle Verhältnisse ausgegeben werden.

big5_mod |>
  select(Geschlecht, Gruppe) |>  
  map(table) |> 
  map(proportions)
$Geschlecht

   f    m 
0.59 0.41 

$Gruppe

  Jung Mittel  Weise 
 0.735  0.195  0.070 

Diese Art des wiederholten Berechnens einer Funktion wird in Kapitel 12 genauer erläutert. Vom Prinzip her ist die Anwendung von map() in diesem Fall wie das Nutzen von across() innerhalb von mutate() (siehe Kapitel 6.4.2). Allerdings muss die Ausgabe in mutate() oder auch in summarise() das gleiche Format für alle Spalten haben. Wenn wir Kontingenztafeln mit unterschiedlich vielen Kategorien in den jeweiligen Spalten erstellen, ist diese Bedingung jedoch nicht erfüllt. Daher müssen wir in diesem Fall auf map() zurückgreifen.

Übung 7.2. (Noch nicht enthalten) Starte die Übung mit uebung_starten(7.2).

7.3 Zusammenhangsmaße

Zwei der wichtigsten Zusammenhangsmaße sind die Kovarianz und die darauf basierende Korrelation. Eine Korrelation berechnen wir mit der cor() Funktion, welche standardmäßig die Produkt-Moment-Korrelation berechnet.

cor(big5_mod$Extraversion, big5_mod$Neurotizismus)
[1] 0.06972529

Für die Rangkorrelation nach Spearman oder die Kendall-Tau Korrelation muss das Argument method auf "spearman" oder kendall gesetzt werden.

cor(big5_mod$Extraversion, big5_mod$Neurotizismus, method = "spearman")
[1] 0.04874732

Um mehrere Korrelationen auf einmal zu berechnen, übergeben wir der Funktion cor() mehr als zwei Spalten. Dabei können wir der Funktion grundsätzlich unbegrenzt viele numerische Spalten übergeben. Erst wählen wir die gewünschten Spalten aus und bezeichnen das Zwischenergebnis als big5_sub.

big5_sub <- big5_mod |> 
  select(Alter, Extraversion, Neurotizismus)

Diesen Datensatz mit ausschließlich numerischen Spalten übergeben wir dann der Funktion cor().

cor(big5_sub)
                   Alter Extraversion Neurotizismus
Alter          1.0000000  -0.12250136   -0.23019948
Extraversion  -0.1225014   1.00000000    0.06972529
Neurotizismus -0.2301995   0.06972529    1.00000000

Auf dieselbe Art und Weise kann die Kovarianz mithilfe der Funktion cov() berechnet werden.

cov(big5_sub)
                    Alter Extraversion Neurotizismus
Alter         128.8837940  -0.48201005   -1.78158291
Extraversion   -0.4820101   0.12012462    0.01647437
Neurotizismus  -1.7815829   0.01647437    0.46473467

Zusammenhangsmaße für Kontingenztafeln werden in Kapitel 9.6 besprochen.

Übung 7.3. (Noch nicht enthalten) Starte die Übung mit uebung_starten(7.3).