6  Deskriptivstatistik

Wir laden das tidyverse-Paket und öffnen den Datensatz des Victim Blaming Experiments.

library(tidyverse)
vb <- readxl::read_excel("data/victim_blaming.xlsx")

6.1 Daten zusammenfassen

Für die deskriptive Statistik sind insbesondere zwei tidyverse-Funktionen nützlich: summarise() und group_by(). summarise() erlaubt es, Variablen zu aggregieren, d.h. die Daten aus mehreren Fällen in einem Wert zusammenzufassen. Wie bei mutate() können wir mehrere Variablen berechnen, indem diese mit , getrennt werden. Es bietet sich an, für die neu enstandenen Aggregatvariablen beschreibende Namen zu wählen.

Der grundlegende Aufbau jedes summarise() Befehls ist summarise(fun_x = fun(x)), wobei jede Funktion verwendbar ist, die aus einem beliebig langen Datenvektor einen einzelnen Wert zurückgibt, z.B. min() und max() für Minimum und Maximum, aber auch mean() und andere Funktionen. Ein wichtiges Prinzip des tidyverse ist, dass jede Funktion wie mutate() oder summarise() nicht nur als Input einen Tibble bekommt, sondern als Output ebenfalls wieder einen solchen Tibble zurückliefert, den wir dann ggf. im nächsten Schritt weiterverarbeiten können.

6.2 Maße der zentralen Tendenz

Zu den Maßen zentraler Tendenz zählen das arithmetische Mittel mean(), der Median median(), und der Modalwert. Wir können summarise() nutzen, um diese Berechnungen anzustellen. Dazu betrachten wir die Altersvariable v_1 im Datensatz.

vb |>
  summarise(
    mean_alter = mean(v_1),
    median_alter = median(v_1),
    min_alter = min(v_1),
    max_alter = max(v_1)
  )
# A tibble: 1 × 4
  mean_alter median_alter min_alter max_alter
       <dbl>        <dbl>     <dbl>     <dbl>
1       19.8           20        16        22

Als Ergebnis der Pipe steht ein Tibble mit einer Zeile und vier Spalten. Wir sehen, dass die Befragten zwischen 16 und 22 Jahre alt sind, mit einem Mittelwert von rund 19.8. Der Medianwert ist 20.

Fehlende Werte oder “Warum bekomme ich immer NA zurück?”

Wenn die Variable, die wir zusammenfassen wollen, fehlende Werte NA enthält, schlagen die meisten statistischen Funktionen fehl und liefern NA zurück.

vb |>
  summarise(min_strafe = min(v_9a)) # klappt nicht wegen NA
# A tibble: 1 × 1
  min_strafe
       <dbl>
1         NA

Wollen wir trotzdem nützliche Ergebnisse bekommen, müssen wir als zusätzlichen Funktionsparameter na.rm = TRUE verwenden. Damit werden vor der Berechnung des Kennwerts die fehlenden Werte entfernt. Das gilt für fast alle Funktionen, die wir in dieser Sitzung verwenden.

vb |>
  summarise(min_strafe = min(v_9a, na.rm = TRUE)) # klappt besser
# A tibble: 1 × 1
  min_strafe
       <dbl>
1          0

Merke: Immer fun(..., na.rm = TRUE) aufrufen, um fehlende Werte zu ignorieren.

6.3 Dispersionsmaße

Dispersionsmaße geben die Streuung der Datenpunkte wieder, sie sollten immer zusätzlich zu den Maßen der zentralen Tendenz angegeben werden. Die zwei wichtigsten Maße sind:

  1. Standardabweichung sd()
  2. Spannweite, also der Abstand zwischen min() und max().

Der bekannte summarise()-Workflow ist auch für die Berechnung dieser Maße anwendbar. Bei der Berechnung der Spannweite nutzen wir die Möglichkeit, auch komplexere Berechnungen statt nur einer einzelnen Funktion durchzuführen, in diesem Fall die Differenz von Maximum und Minimum.

vb |>
  summarise(
    sd_alter = sd(v_1, na.rm = TRUE),
    range_alter = max(v_1, na.rm = TRUE) - min(v_1, na.rm = TRUE),
  )
# A tibble: 1 × 2
  sd_alter range_alter
     <dbl>       <dbl>
1     1.94           6

In der Forschungspraxis und nach APA-Guidelines ist es üblich, metrische Variablen anhand von Mittelwert und Standardabweichung zu beschreiben. Das können wir auch für mehrere Variablen in einem Schritt erledigen.

vb |>
  summarise(
    mean_alter = mean(v_1, na.rm = TRUE),
    sd_alter = sd(v_1, na.rm = TRUE),
    mean_empathie = mean(v_13, na.rm = TRUE),
    sd_empathie = sd(v_13, na.rm = TRUE)
  )
# A tibble: 1 × 4
  mean_alter sd_alter mean_empathie sd_empathie
       <dbl>    <dbl>         <dbl>       <dbl>
1       19.8     1.94          3.75       0.980

Wollen wir das aber für mehr als zwei Variablen machen, wird diese Darstellung schnell unübersichtlich, da immer mehr Spalten dazukommen. Eine übersichtlichere Darstellung erhalten wir mit folgendem Code, der sich die map_dfr()-Funktion zunutze macht und die Tabelle so aufbaut, dass in den Spalten die statistischen Maße stehen und für jede neuen Variable eine Zeile hinzukommt:

vb |>
  select(v_1, v_13) |> # Hier wählen wir die Variablen aus, die wir auswerten wollen
  map_dfr(
    ~ tibble
    (
      M = mean(.x, na.rm = TRUE), # Hier reihen wir die statistischen Maße hintereinander, die wir auswerten wollen. Satt des Variablennamen steht ein .x, dort wird dann jeweils nacheinander die Variable eingesetzt, die unter "select" ausgewählt wurden
      SD = sd(.x, na.rm = TRUE)
    ),
    .id = "Variable"
  ) # Hier wird festgelegt, dass die erste Spalte mit den Variablennamen als Überschrift "Variable" haben soll.
# A tibble: 2 × 3
  Variable     M    SD
  <chr>    <dbl> <dbl>
1 v_1      19.8  1.94 
2 v_13      3.75 0.980

6.4 Gruppenvergleiche

Die zweite nützliche Funktion für die Deskriptivstatistik im tidyverse ist group_by(), die wir in der Analyse zwischen den Datensatz und die summarise() Funktion schieben. Die Funktion teilt den Datensatz anhand der angegeben Variable und ermöglicht so ganz leicht Gruppenvergleiche. Achtung: group_by() führt zunächst lediglich dazu, dass die Daten gruppiert werden. Die Effekte von group_by() zeigen sich erst in den nachfolgenden tidyverse-Funktionen, welche dann automatisch die Gruppierung bei der Erstellung neuer Variablen beachten. Dieses Vorgehen erlaubt es, mit summarise() deskriptive Statistiken für Untergruppen zu erstellen. Im folgenden Beispiel berechnen wir zunächst wieder Mittelwert und Standardabweichung der Altersvariable.

vb |>
  summarise(
    mean_alter = mean(v_1, na.rm = TRUE),
    sd_alter = sd(v_1, na.rm = TRUE)
  )
# A tibble: 1 × 2
  mean_alter sd_alter
       <dbl>    <dbl>
1       19.8     1.94

Wenn die Randomisierung gelungen ist, sollten sich diese Werte nicht stark zwischen den Experimentalbedingungen unterscheiden. Das prüfen wir, indem wir den Datensatz vor der Aggregation nach der Variable stimulus_rec gruppieren und dann dieselbe summarise() Funktion nutzen.

vb |>
  group_by(stimulus_rec) |> # nur dies haben wir eingefügt
  summarise(
    mean_alter = mean(v_1, na.rm = TRUE),
    sd_alter = sd(v_1, na.rm = TRUE)
  )
# A tibble: 4 × 3
  stimulus_rec            mean_alter sd_alter
  <chr>                        <dbl>    <dbl>
1 Extravertiert mit Info        19.9     1.95
2 Extravertiert ohne Info       19.9     1.74
3 Introvertiert mit Info        19.6     2.01
4 Introvertiert ohne Info       19.6     2.01

Da die Gruppierungsvariable vier Ausprägungen hat, bekommen wir auch einen Tibble mit vier Zeilen (eine pro Gruppe) ausgegeben. Da Gruppenvergleiche für eine Vielzahl von Analyseverfahren notwendig sind, wird uns die group_by() Funktion auch in den nachfolgenden Sitzungen zu T-Tests, ANOVA oder Kreuztabellen wieder begegnen.

Im obigen Beispiel verändern wir den vb-Datensatz nicht und wir erstellen auf Basis der Gruppierung kein neues Objekt. Auch folgt auf die summarise()-Funktion keine weitere Funktion, die ohne Gruppierung ausgeführt werden sollte. Trifft aber eines von beiden zu - wir erstellen ein neues Objekt oder es folgen weitere Funktionen - ist es wichtig, den ungroup()-Befehl zu verwenden. Ansonsten wird auch im Weiteren mit der Gruppierung gearbeitet und die group_by()-Funktion wirkt sich dann also auch auf weitere Auswertungen etc. aus.

ergebnistabelle <- vb |> # Im Unterschied zu oben erstellen wir hier ein neues Objekt `ergebnistabelle`
  group_by(stimulus_rec) |>
  summarise(
    mean_alter = mean(v_1, na.rm = TRUE),
    sd_alter = sd(v_1, na.rm = TRUE)
  ) |>
  ungroup() # Hier wird die Gruppierung aufgehoben

6.5 Häufigkeiten

Eine basale deskriptive Statistik sind Häufigkeiten. Das tidyverse bietet für die Berechnung von Häufigkeiten die Funktion n().

vb |>
  summarise(n_cases = n())
# A tibble: 1 × 1
  n_cases
    <int>
1     586

Der Datensatz hat also 586 Zeilen oder Fälle. Dies ist an sich noch nicht sehr interessant, zumal wir mit der dim() Funktion auch schon die Anzahl Zeilen und Spalten ausgeben lassen können. Wenn wir den Datensatz aber nach einer bestimmten Variable mit group_by() gruppieren, erhalten wir die absoluten Häufigkeiten der Gruppierungsvariable.

vb |>
  group_by(stimulus_rec) |>
  summarise(n_cases = n())
# A tibble: 4 × 2
  stimulus_rec            n_cases
  <chr>                     <int>
1 Extravertiert mit Info      153
2 Extravertiert ohne Info     137
3 Introvertiert mit Info      139
4 Introvertiert ohne Info     157

Da diese Operation sehr häufig genutzt wird, gibt es auch noch eine Kurzform count(), die automatisch eine neue Variable n erzeugt.

vb |>
  count(stimulus_rec)
# A tibble: 4 × 2
  stimulus_rec                n
  <chr>                   <int>
1 Extravertiert mit Info    153
2 Extravertiert ohne Info   137
3 Introvertiert mit Info    139
4 Introvertiert ohne Info   157

Wie bekommen wir nun relative Häufigkeiten und Prozentwerte? Die Antwort kennen wir schon: Wir berechnen neue Variablen mit mutate().

vb |>
  count(stimulus_rec) |>
  mutate(
    rel_freq = n / sum(n), # relative Häufigkeit
    percent = rel_freq * 100 # Prozentwerte
  )
# A tibble: 4 × 4
  stimulus_rec                n rel_freq percent
  <chr>                   <int>    <dbl>   <dbl>
1 Extravertiert mit Info    153    0.261    26.1
2 Extravertiert ohne Info   137    0.234    23.4
3 Introvertiert mit Info    139    0.237    23.7
4 Introvertiert ohne Info   157    0.268    26.8

Dichotome Variablen, die 0/1 codiert sind, haben viele Vorteile in der Datenanalyse. Einer davon ist, dass der Mittelwert einer solchen Variable der relativen Häufigkeit der Ausprägung 1 entspricht. Damit können wir mit einem einzigen summarize() z.B. Mehrfachantworten oder andere Variablengruppen auszählen. Hier die relativen Häufigkeiten einiger vorgeschlagenen Strafen:

vb |>
  summarise(
    ermahnung = mean(v_9a, na.rm = TRUE),
    entschuldigung = mean(v_9e, na.rm = TRUE),
    nachsitzen = mean(v_9f, na.rm = TRUE)
  )
# A tibble: 1 × 3
  ermahnung entschuldigung nachsitzen
      <dbl>          <dbl>      <dbl>
1     0.479          0.815      0.147

Fast die Hälfte der Befragten findet eine Ermahnung richtig, über 80% fordern eine Entschuldigung, aber weniger als 15% Nachsitzen.

Zum Schluss können wir die Häufigkeitstabelle auch noch nach Häufigkeiten (statt nach den Experimentalbedingungen) sortieren, auch diese Funktion haben wir schon kennengelernt: arrange(). Wir speichern das Ergebnis der gesamten Datentransformation in einem neuen Objekt, das wir später wiederverwenden wollen.

freq_stimulus <- vb |>
  count(stimulus_rec) |>
  mutate(percent = n / sum(n) * 100) |> # Prozentwerte
  arrange(desc(n)) # absteigende Häufigkeiten

freq_stimulus
# A tibble: 4 × 3
  stimulus_rec                n percent
  <chr>                   <int>   <dbl>
1 Introvertiert ohne Info   157    26.8
2 Extravertiert mit Info    153    26.1
3 Introvertiert mit Info    139    23.7
4 Extravertiert ohne Info   137    23.4

Wir werden group_by() und count() in der Sitzung zu Kreuztabellen wiedersehen, da die Funktionen auch mehrere Gruppierungsvariablen berücksichtigen können.

6.6 Glossar

Funktion Definition
arrange() Sortieren eines Dataframes/Tibbles
arrange() Sortieren einer Variable (default: aufsteigend, für absteigend: desc())
count() Zählen der Häufigkeit, mit der jede Ausprägung einer Variable vorkommt
group_by() Einteilung des Dataframes/Tibbles in Gruppen anhand der angegebenen Variable(n)
max() Anzeige des größten Wertes in einer Variable
mean() Berechnung des arithmetischen Mittels
median() Berechnung des Medians
min() Anzeige des kleinsten Wertes in einer Variable
mutate() Verändern oder Erstellen einer oder mehrerer Variablen in einem Dataframe/Tibble
sd() Berechnung der Standardabweichung
summarise() Aggregieren einer oder mehrerer Variablen eines Dataframes/Tibbles zu einer oder mehreren neuen Variablen

6.7 Hausaufgabe

Für die Hausaufgabe analysieren wir den Datensatz gewohnheiten.xlsx.

  1. Was ist das Durchschnittsalter in der Stichprobe (arithmetisches Mittel, Median)? Und welche Altersrange (Spannweite) wurde befragt?
  2. Wieviele Männer und Frauen wurden befragt?
  3. Unterscheiden sich Männer und Frauen im Durchschnitt darin, wie sehr sie den Fernseher vermissen würden? (mit Antwortsatz)

Für die Aufgaben gilt:

  • Geben Sie als Kommentar (mit # beginnend) an, welche Frage Sie bearbeiten, darunter folgt der zugehörige Code.
  • Die Antwortsätze folgen darunter, sofern gefordert, ebenfalls als Kommentar (mit # beginnend).