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 Beschreibungen der kontinuierlichen und kategorialen Merkmale 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 einzelnen Altersgruppen ausgegeben bekommen, kann R für die Spalte mit Geschlecht lediglich die Anzahl an vorhandenen Character Werten zählen. Für weitere Informationen über Faktoren und deren Umgang schaue dir erneut Kapitel 4.3.2 und 6.10 an.

Allerdings fehlt bei dieser Ansicht zum Beispiel die Standardabweichung und man kann mit die Ausgabe nicht vernünftig zum Exportieren formatieren. Im begleitenden remp Package gibt es die Funktion descriptive(), die standardmäßig Anzahl, Minimum, 25% Quartil, Mittelwert, Median, 75% Quartil, die Standardabweichung und den Standardfehler für alle numerischen Spalten ausgibt. Möchte man Spalten nicht in der Ausgabe sehen, sollte man dieser zuvor mit select() entfernen (siehe Kapitel 6.2).

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

Zusätzlich könnte man mit dem .print Argument die Anzahl ausgegebener Spalten erhöhen. Für Grupperiungen der deskriptiven Statistiken verwenden wir die Funktion group_by() aus dem dplyr Package des tidyverse. Dabei können wir der Funktion beliebig viele Spalten ohne Anführungszeichen übergeben. So können wir beispielsweise nach Geschlecht aufteilen.

big5_mod1 |> 
  group_by(Geschlecht) |> 
  descriptive() 
# A tibble: 6 × 11
# Groups:   Geschlecht [2]
  Geschlecht Variable          N   Min    Q1  Mean Median    Q3   Max    SD    SE
  <chr>      <chr>         <int> <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl>
1 f          Alter           118  14   18    25.9    22    29.8  60   11.4   1.05
2 f          Extraversion    118   2.4  2.8   3.05    3     3.2   4.3  0.36  0.03
3 f          Neurotizismus   118   1.4  2.82  3.25    3.3   3.7   4.6  0.63  0.06
4 m          Alter            82  13   20    27.3    23    32    59   11.4   1.25
# ℹ 2 more rows

Bei Interesse nach Unterschieden innerhalb der Geschlechter je nach Altersgruppe, fügen wir die Spalte Gruppe in die Funktion group_by() hinzu.

big5_mod1 |> 
  group_by(Geschlecht, Gruppe) |> 
  descriptive() 
# A tibble: 18 × 12
# Groups:   Geschlecht, Gruppe [6]
  Geschlecht Gruppe Variable          N   Min    Q1  Mean Median    Q3   Max    SD    SE
  <chr>      <fct>  <chr>         <int> <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl>
1 f          Jung   Alter            89  14    17   20.4    20    23    30    4.21  0.45
2 f          Jung   Extraversion     89   2.4   2.8  3.07    3     3.3   4.3  0.37  0.04
3 f          Jung   Neurotizismus    89   1.4   2.9  3.29    3.3   3.6   4.6  0.58  0.06
4 f          Mittel Alter            20  31    34.8 39.0    39    42.2  49    5.16  1.15
# ℹ 14 more rows

Der Nachteil an dieser Funktion ist die fehlende Flexibilität, falls man andere Schätzer inkludieren möchte. Dafür bietet sich die Funktion summarise() aus dem dplyr Package (Teil des tidyverse). Als Argumente übergeben wir auf der linken Seite der Gleichung den gewünschten Namen der neu erstellen Spalte und auf der rechten Seite die Funktion zur Berechnung der Statistik mit dem gewünschten Spaltennamen.

Exemplarisch soll zunächst der Mittelwert mit der Funktion mean() und die Standardabweichung mit sd() berechnet werden. Bei fehlenden Werten müsste man zusätzlich das Argument na.rm zum Entfernen fehlender Werte bei der Berechnung hinzufügen (z.B. mean(Extraversion, na.rm = TRUE)). Wichtig ist allerdings, dass die innerhalb von summarise() verwendeten Funktionen nur einen Wert pro Spalte ausrechnen.

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

Auch hier gruppieren wir mit der zuvor kennengelernten Funktion group_by().

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

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.

Grundsätzlich können wir beliebig viele Berechnungen in einem Funktionsaufruf durchführen. Möchte man die gleichen Statistiken für mehrere Spalten berechnen, greifen wir erneut auf die Hilfsfunktion across() zurück (siehe Kapitel 6.4.2). 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, Median = median, SD = sd))
  )
# A tibble: 1 × 6
  Extraversion_M Extraversion_Median Extraversion_SD Neurotizismus_M Neurotizismus_Median
           <dbl>               <dbl>           <dbl>           <dbl>                <dbl>
1           3.08                   3           0.347            3.13                  3.1
# ℹ 1 more variable: Neurotizismus_SD <dbl>

Wie dir vielleicht bereits aufgefallen ist, sieht die Ausgabe von summarise() für mehrere Spalten anders aus als die von descriptive(). Während erstere die Variablen nebeneinander in neue Spalten auflistet, fügt descriptive() die Ergebnisse zeilenweise hinzu. Wenn wir die gleiche Ausgabe wie in descriptive() erreichen möchten, müssen wir den Datensatz zunächst in ein langes Format bringen (siehe Kapitel 6.6). Nun gruppieren wir nach der neu erstellten Spalte namens Variable. Anschließend können wir wie gewohnt mit summarise() die gewünschten Berechnungen durchführen. 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)
  )
# A tibble: 3 × 4
  Variable         Q1  Mean    Q3
  <chr>         <dbl> <dbl> <dbl>
1 Alter          18.8 26.5   31.2
2 Extraversion    2.8  3.08   3.3
3 Neurotizismus   2.7  3.13   3.6

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.

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

7.2 Häufigkeiten und Kontingenztafeln

Zum Ausführen der Funktionen dieses Kapitels muss das tidyverse und das Package janitor installiert und geladen sein.

library(tidyverse)
library(janitor)

In diesem Kapitel verwenden wir den big5_mod Datensatz aus dem remp Package. Der schnellste Weg zur Berechnung von Häufigkeiten ist die direkt in R integrierte Funktion namens tables(). Dabei übergeben wir die Spalten mithilfe des Dollar-Operators (siehe Kapitel 4.5). Hier wählen wir die Spalte Geschlecht aus.

table(big5_mod$Geschlecht)

  f   m 
118  82 

Durch das Hinzufügen einer weiteren Variable, erhalten wir eine klassische Kontingenztafel, die wir mit Methoden im Rahmen von Kapitel 9.6 inferenzstatistisch betrachten werden. An dieser Stelle möchten wir die absoluten Häufigkeiten der Geschlechter zusätzlich nach den drei Altersgruppen aufteilen. In diesem Fall beinhalten beide Spalten Textbezeichnung. Hätten wir zwei numerische Spalten (z.B. Tod 0/1 und Geschlecht 0/1) solltest zusätzlich das Argument dnn zum Festlegen der Dimensionsnamen verwenden.

table(big5_mod$Geschlecht, big5_mod$Gruppe, dnn = c("Geschlecht", "Altersgruppe"))
          Altersgruppe
Geschlecht Jung Mittel Weise
         f   89     20     9
         m   58     19     5

Ein weiteres nützliches Argument ist useNA, welches wir zum Anzeigen fehlender Werte auf "ifany" setzen können. Darüber hinaus können wir durch das Argument exclude eine Faktorstufe für die Erstellung einer Kontingenztafel zur späteren Auswertung herausnehmen. Wäre bei der Variable Geschlecht beispielsweise eine dritte Stufe namens Divers mit wenigen Beobachtungen vorhanden, könnten wir diese so für die Hypothesentests ausblenden. Die Voraussetzung dafür ist, dass die Variable als Faktor kodiert ist (siehe Kapitel 4.3.2 und 6.10).

Eine dritte Dimension führt zur Ausgabe als sogenannte Liste (siehe Kapitel 11.4). Die erste Kontingenztafel unter dem Wort FALSE beinhaltet alle Personen mit einer Extraversion kleiner oder gleich 3.5 und die Kontingenztafel darunter entsprechend alle Personen mit einer Extraversion größer 3.5, jeweils aufgeteilt nach Geschlecht und Altersgruppe. Das Inkludieren einer logischen Abfrage ist eine der großen Vorteil der table() Funktion.

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

   
    Jung Mittel Weise
  f   81     18     9
  m   54     18     4

, ,  = TRUE

   
    Jung Mittel Weise
  f    8      2     0
  m    4      1     1

Nachteile der table() Funktion sind hingegen die unbefriedigenden Möglichkeiten zum Exportieren als Word oder PDF Dokument sowie die umständliche Anzeige relativer Häufigkeiten und bedingter Wahrscheinlichkeiten. An dieser Stelle kommt die Funktion tabyl() aus dem janitor Package ins Spiel. Dabei ist das erste Argument der Name des Datensatzes gefolgt von ein bis drei Spaltennamen. Fehlende Werte werden automatisch angezeigt. Falls dies nicht erwünscht ist, könnten wir diese mit dem Argument show_na = FALSE ausblenden.

tabyl(big5_mod, Geschlecht)
 Geschlecht   n percent
          f 118    0.59
          m  82    0.41

Neben der absoluten werden zusätzlich die relativen Häufigkeiten angezeigt. Die Ausgaben der Funktion tabyl() sind außerdem mit der Hilfe von R Markdown direkt nach Word exportierbar (siehe Kapitel 10.3.1). Möchten wir wie zuvor zwei Variablen miteinander in Bezug auf Häufigkeiten analysieren, sollten wir auf einige Formatierungshilfen aus dem janitor Package zurückgreifen. Auch wenn diese zunächst umständlich erscheinen, profitiert man durch die übersichtliche Ausgabe enorm.

Die Funktionen zum Formatieren beginnen alle mit dem Präfix adorn. In unserem ersten Beispiel sind wir an Unterschieden in Bezug auf die Häufigkeit der Altersgruppen je nach Geschlecht interessiert. Ausgeben möchten wir also die Zeilenrandsummen in einer extra Spalte ("col"), die bedingten Wahrscheinlichkeiten für jede Altersgruppe gegeben des jeweiligen Geschlechts ("row") in Prozent, nur eine Nachkommastelle der Prozentwerte, die absoluten Häufigkeiten in Klammern und abschließend die Namen der verwendeten Spalten.

tabyl(big5_mod, Geschlecht, Gruppe) |> 
  adorn_totals("col") |> 
  adorn_percentages("row") |> 
  adorn_pct_formatting(digits = 1) |> 
  adorn_ns() |> 
  adorn_title()
                Gruppe                                 
 Geschlecht       Jung     Mittel    Weise        Total
          f 75.4% (89) 16.9% (20) 7.6% (9) 100.0% (118)
          m 70.7% (58) 23.2% (19) 6.1% (5) 100.0%  (82)

Möchten wir hingegen herausfinden, ob mehr Frauen oder Männer in den jeweiligen Altersgruppen befragt wurden, lassen wir uns stattdessen die Spaltenrandsummen in einer extra Zeile ("row") und die bedingten Wahrscheinlichkeiten mit Konditionierung auf die Altersgruppe ("col") ausgeben.

tabyl(big5_mod, Geschlecht, Gruppe) |> 
  adorn_totals("row") |> 
  adorn_percentages("col") |> 
  adorn_pct_formatting(digits = 1) |> 
  adorn_ns() |> 
  adorn_title()
                  Gruppe                        
 Geschlecht         Jung      Mittel       Weise
          f  60.5%  (89)  51.3% (20)  64.3%  (9)
          m  39.5%  (58)  48.7% (19)  35.7%  (5)
      Total 100.0% (147) 100.0% (39) 100.0% (14)

Während die Funktion table() vor allem für einen ersten Überblick und zum Erstellen von Kontingenztafeln für Hypothesentests geeignet ist, bietet die Funktion tabyl() viele Formatierungsmöglichkeiten mit direkter Kompatibilität mit R Markdown zum Exportieren in ein Word Dokument.

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

7.3 Zusammenhangsmaße

Zur Ausgabe von Korrelationen wird kein zusätzliches Package benötigt, da die dafür zuständigen Funktionen direkt in R integriert sind. Möchte man Korrelationstabellen hingegen optisch ansprechender oder als Graphik ausgeben lassen, sollten die Packages correlation und see installiert und geladen werden.

library(correlation)
library(see)

In diesem Kapitel wird ebenfalls der big5_mod Datensatz aus dem remp Package verwendet. Zwei der wichtigsten Zusammenhangsmaße für metrische Variablen sind die Kovarianz und die darauf basierende Korrelation. Eine Korrelation berechnen wir mit der cor() Funktion, äquivalent dazu die Kovarianz mit cov(). Wir werden uns im weiteren Verlauf auf Korrelationen konzentrieren. Zusammenhangsmaße im Kontext von Kontingenztafeln werden hingegen erst im Rahmen des Kapitels 9.6 besprochen. Ein zentrales Argument in der Funktion cor() ist method, welches standardmäßig Pearsons Produkt-Moment-Korrelation berechnet. Die ersten beiden Argumente stellen die einzelnen Spalten dar, die wir als Wertereihe mithilfe des Dollar-Operators aus dem Datensatz extrahieren (siehe Kapitel 4.5).

cor(big5_mod$Extraversion, big5_mod$Neurotizismus, method = "pearson")
[1] 0.06972529

Beim Vorliegen ordinaler Daten bevorzugen wir hingegen Rangkorrelationen nach Spearman ("spearman") oder Kendall ("kendall"), wofür das Argument method entsprechend angepasst werden muss.

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

Um auf einen Schlag die Zusammenhänge von mehr als zwei Merkmalen zu explorieren, können wir der Funktion cor() den Datensatz mit den interessierenden Variablen auch direkt übergeben. Wichtig ist dabei, dass alle Spalten numerisch sind. Für unser Beispiel wählen wir zunächst die Spalten Alter, Extraversion und Neurotizismus aus und bezeichnen das Zwischenergebnis als big5_sub.

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

Anschließend berechnen wir die Korrelationen mit der Funktion cor().

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

Die Korrelationen sind allerdings nicht gerundet und auch sonst können wir diese Ausgabe nicht gut zum Exportieren in Word oder als PDF verwenden. Alternativ bietet sich die Funktion correlation() aus dem gleichnamigen Package an. Das Ergebnis dieser übergeben wir zusätzlich der Funktion summary(). Die Sterne (Asterisk) symbolisieren statistische Signifikanz.

corr_table2 <- big5_sub |> 
  correlation() |> 
  summary()
corr_table2
# Correlation Matrix (pearson-method)

Parameter    | Neurotizismus | Extraversion
-------------------------------------------
Alter        |       -0.23** |        -0.12
Extraversion |          0.07 |             

p-value adjustment method: Holm (1979)

Zur graphischen Ausgabe der Korrelationsmatrix verwenden wir schließlich die Funktion plot() aus dem see Package. In Abbildung 7.1 sehen wir die Korrelationen gefärbt in Abhängigkeit der Höhe und Richtung. Positive Korrelationen werden dabei in Blautönen und negative Korrelationen in Rottönen illustriert. Je geringer die Ausprägung der jeweiligen Korrelation ist, desto blasser erscheinen deren Farben.

plot(corr_table2)
Darstellung der Korrelationen in Abhängigkeit der Stärke und Richtung.

Abbildung 7.1: Darstellung der Korrelationen in Abhängigkeit der Stärke und Richtung.

Für Korrelationen zwischen lediglich drei Variablen erscheint diese Art der Visualisierung übertrieben. Bei größeren Korrelationstabellen erhalten wir hingegen einen schnellen Überblick über Zusammenhänge, die wir genauer betrachten sollten. In einem weiteren Beispiel wählen wir daher mit allen Fragen zur Extraversion dieses Mal zehn Spalten aus. Die verwendeten Funktionen verändern sich nicht. In Abbildung 7.2 wird deutlich ersichtlich, welche Fragen zur Extraversion invers mit einer gegensätzlichen Formulierung gestellt wurden. Jede zweite Frage involviert eine Verneinung, weswegen die Korrelation erwartungsgemäß negativ ist. Zur inferenzstatistischen Auswertung müssten wir daher diese Fragen vorher entsprechend umkodieren (siehe Kapitel 6.4.3).

big5_comp |> 
  select(E1:E10) |> 
  correlation() |> 
  summary() |> 
  plot()
Darstellung der Korrelationen zwischen Fragen zur Extraversion.

Abbildung 7.2: Darstellung der Korrelationen zwischen Fragen zur Extraversion.

In dem nächsten Kapitel werden wir die einzelnen Bestandteile von Abbildungen wie dieser kennenlernen. Zudem werden typische Anpassungsmöglichkeiten vorgestellt. So können wir beispielsweise mit theme_minimal() den grauen Hintergrund verändern oder mit scale_x_discrete(guide = guide_axis(n.dodge = 3, check.overlap = TRUE)) dafür sorgen, dass sich die Namen der Variablen auf der x-Achse auf 3 Zeilen verteilen, falls sich diese ansonsten überlappen (siehe Kapitel 8.10.2).

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