/
043_Typische_Probleme_Datenanalyse.Rmd
603 lines (359 loc) · 23.9 KB
/
043_Typische_Probleme_Datenanalyse.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
```{r include=FALSE, cache=FALSE}
set.seed(1014)
options(digits = 3)
knitr::opts_chunk$set(
comment = "#>",
collapse = TRUE,
message = FALSE,
warning = FALSE,
cache = TRUE,
out.width = "70%",
fig.align = 'center',
fig.width = 6,
fig.asp = 0.618, # 1 / phi
fig.show = "hold",
size = "tiny"
)
script <- TRUE
library(methods) # sometimes not loaded, although built-in, appears like a bug
```
# Praxisprobleme der Datenaufbereitung
```{block2, ziele-typ-probleme, type='rmdcaution', echo = TRUE}
Lernziele:
- Typische Probleme der Datenaufbereitung kennen.
- Typische Probleme der Datenaufbereitung bearbeiten können.
```
Laden wir zuerst die benötigten Pakete; v.a. ist das `dplyr` and friends. Das geht mit dem Paket `tidyverse`.
```{r libs-typische-probleme}
library(corrr) # Korrelationsmatrix
library(car) # Umkodieren
library(prada) # optional: Daten 'extra' und 'stats_test'
library(tidyverse) # Datenjudo
```
Stellen wir einige typische Probleme des Datenjudo (genauer: der Datenaufbereitung) zusammen. Probleme heißt hier nicht, dass es etwas Schlimmes passiert ist, sondern es ist gemeint, wir schauen uns ein paar typische Aufgabenstellungen an, die im Rahmen der Datenaufbereitung häufig anfallen.
## Datenaufbereitung
### Auf fehlende Werte prüfen
Das geht recht einfach mit `summary(mein_dataframe)`. Der Befehl liefert für jede Spalte des Dataframe `mein_dataframe` die Anzahl der fehlenden Werte zurück.
```{r read-stats-data, results = "hide"}
stats_test <- read.csv("data/stats_test.csv")
summary(stats_test)
```
### Fälle mit fehlenden Werte löschen
Weist eine Variable (Spalte) "wenig" fehlende Werte auf, so kann es schlau sein, nichts zu tun. Eine andere Möglichkeit besteht darin, alle entsprechenden Zeilen zu löschen. Man sollte aber schauen, wie viele Zeilen dadurch verloren gehen.
```{r}
# Unsprünglich Anzahl an Fällen (Zeilen)
nrow(stats_test)
# Nach Umwandlung in neuen Dataframe
stats_test %>%
na.omit -> stats_test_na_omit
nrow(stats_test_na_omit)
# Nur die Anzahl der bereinigten Daten
stats_test %>%
na.omit %>%
nrow
```
```{block2, dplyr-no-brackets, type='rmdcaution', echo = TRUE}
Bei mit der Pfeife verketteten Befehlen darf man für Funktionen die runden Klammern weglassen, wenn man keinen Parameter schreibt. Also ist `nrow` (ohne Klammern) erlaubt bei `dplyr`, wo es eigentlich `nrow()` heißen müsste. Sie dürfen die Klammern natürlich schreiben, aber sie müssen nicht.
```
Hier verlieren wir `r nrow(stats_test) - nrow(stats_test_na_omit)` Zeilen, das verschmerzen wir.
Welche Zeilen verlieren wir eigentlich? Lassen wir uns nur die *nicht-*kompletten Fälle anzeigen (und davon nur die ersten paar):
```{r demo-complete-cases, results = "hide"}
stats_test %>%
filter(!complete.cases(.)) %>%
head
```
```{block2, dplyr-punkt, type='rmdcaution', echo = TRUE}
Man beachte, dass der Punkt `.` für den Datensatz steht, wie er vom letzten Schritt weitergegeben wurde. Innerhalb einer dplyr-Befehls-Kette können wir den Datensatz, wie er im letzten Schritt beschaffen war, stets mit `.` ansprechen; ganz praktisch, weil schnell zu tippen. Natürlich könnten wir diesen Datensatz jetzt als neues Objekt speichern und damit weiter arbeiten. Das Ausrufezeichen `!` steht für logisches "Nicht". Mit `head` bekommt man nur die ersten paar Fälle (6 im Standard) angezeigt, was oft reicht für einen Überblick.
```
In Pseudo-Syntax liest es sich so:
```{block2, no-na-filter, type='rmdpseudocode', echo = TRUE}
Nehme den Datensatz `stats_test` UND DANN...
filtere die nicht-kompletten Fälle
```
### Fehlende Werte zählen
Wie viele fehlende Wert weist eine Spalte auf?
```{r}
stats_test %>%
mutate(self_eval_NA = is.na(self_eval)) %>%
summarise(self_eval_NA_sum = sum(self_eval_NA))
```
```{block2, summarise-all-example, type='rmdpseudocode', echo = TRUE}
Nimm die Tablle `stats_test` UND DANN
berechne eine Spalte `self_eval_NA` in der steht, ob bei `self_eval` ein `NA` steht UND DANN
zähle die `NA`s zusammen. FERTIG.
```
### Fehlende Werte ggf. ersetzen
Ist die Anzahl der fehlenden Werte zu groß, als dass wir es verkraften könnten, die Zeilen zu löschen, so können wir die fehlenden Werte ersetzen. Allein, das ist ein weites Feld und übersteigt den Anspruch dieses Kurses^[Das sagen Autoren, wenn sie nicht genau wissen, wie etwas funktioniert.]. Eine einfache, aber nicht die beste Möglichkeit, besteht darin, die fehlenden Werte durch einen repräsentativen Wert, z.B. den Mittelwert der Spalte, zu ersetzen.
```{r}
stats_test %>%
mutate(interest = replace(.$interest,
is.na(.$interest),
mean(.$interest,
na.rm = TRUE))) -> stats_test
sum(is.na(stats_test$interest))
```
`replace`^[aus dem "Standard-R", d.h. Paket "base".] ersetzt Werte aus dem Vektor `.$interest` alle Werte, für die `is.na(.$interest)` wahr ist, bei Zeilen mit fehlenden Werten in dieser Spalte also. Der Punkt `.` steht für den Datensatz (so wie er aus dem letzten Pfeifenschritt hervorkam). Diese Werte werden durch den Mittelwert der Spalte ersetzt^[Hier findet sich eine ausführlichere Darstellung: https://sebastiansauer.github.io/checklist_data_cleansing/index.html]. Der Punkt `.` ersetzt den Daten der Tabelle (wir hätten aber auch den Namen der Tabelle ausschreiben können).
`r if(!(script == TRUE)) knitr::knit_child("children/na_advanced.Rmd")`
<!-- Vertiefung. Nur für Buch, nicht Skript -->
### Nach Fehlern suchen
Leicht schleichen sich Tippfehler oder andere Fehler ein. Man sollte darauf prüfen; so könnte man sich ein Histogramm ausgeben lassen pro Variable, um "ungewöhnliche" Werte gut zu erkennen. Meist geht das besser als durch das reine Betrachten von Zahlen. Gibt es wenig unterschiedliche Werte, so kann man sich auch die unterschiedlichen Werte ausgeben lassen.
```{r}
stats_test %>%
dplyr::count(interest, sort = TRUE) %>% head
```
Da in der Umfrage nur ganze Zahlen von 1 bis 5 abgefragt wurden, ist die `3.21...` auf den ersten Blick suspekt. In diesem Fall ist aber alles ok, da wir diesen Wert selber erzeugt haben.
Findet man 'merkwürdige' (unplausible) Werte, so kann es sinnvoll sein, diese Werte herauszunehmen (im Detail eine schwierige Entscheidung). Besser als die Zeilen zu löschen, ist es oft, diese Werte in `NA` umzuwandeln. Sagen wir, wir möchten alle Fälle mit `score < 21` entfernen bzw. in `NA` umwandeln.
```{r}
stats_test %>%
mutate(score_bereinigt = replace(.$score,
.$score < 21,
NA)) -> stats_test
```
### Ausreißer identifizieren
Ähnlich zu Fehlern, steht man Ausreißer häufig skeptisch gegenüber. Allerdings kann man nicht pauschal sagen, das Extremwerte entfernt werden sollen: Vielleicht war jemand in der Stichprobe wirklich nur 1.20m groß? Hier gilt es, begründet und nachvollziehbar im Einzelfall zu entscheiden. Histogramme und Boxplots sind wieder ein geeignetes Mittel, um Ausreißer zu finden (vgl. Abb. \@ref(fig:fig-ausreisser)).
```{r fig-ausreisser, fig.cap = "Ausreißer identifizieren"}
qplot(x = score, data = stats_test, binwidth = 1)
```
Mit `binwidth = 1` sagen wir, dass jeder Balken (bin) eine Breite (width) von 1 haben soll.
### Hochkorrelierte Variablen finden
Haben zwei Leute die gleiche Meinung, so ist einer von beiden überflüssig - wird behauptet. Ähnlich bei Variablen; sind zwei Variablen sehr hoch korreliert (>.9, als grober (!) Richtwert), so bringt die zweite kaum Informationszuwachs zur ersten. Und kann z.B. ausgeschlossen werden.
Nehmen wir dazu den Datensatz `extra` her und berechnen wir eine Korrelationsmatrix, d.h. wir korrelieren alle Variablen paarweise miteinander:
```{r extra-correlate}
extra <- read.csv("data/extra.csv")
extra %>%
dplyr::select(i01:i10) %>% # Wähle die Variablen von i1 bis i10 aus
corrr::correlate() -> km # Korrelationsmatrix berechnen
```
Betrachten Sie die Korrelationstabelle, was fällt Ihnen auf?[Sie ist quadratisch, auf der Hauptdiagonalen steht `NA`, und gespiegelt an der Hauptdiagonalen, d.h. das obere Dreieck der Matrix ist redundant.]
In diesem Beispiel sind keine Variablen sehr hoch korreliert. Wir leiten keine weiteren Schritte ein, abgesehen von einer Visualisierung (s. Abb. \@ref(fig:fig-corrr)).
```{r fig-corrr, fig.cap = "Ein Korrelationsplot"}
km %>%
shave() %>% # Oberes Dreieck ist redundant, wird "abrasiert"
rplot() # Korrelationsplot
```
Die Funktion `correlate` stammt aus dem Paket `corrr`^[https://github.com/drsimonj/corrr ], welches vorher installiert und geladen sein muss. Hier ist die Korrelation nicht zu groß, so dass wir keine weiteren Schritte unternehmen. Hätten wir eine sehr hohe Korrelation gefunden, so hätten wir eine der beiden beteiligten Variablen aus dem Datensatz löschen können.
### z-Standardisieren
Für eine Reihe von Analysen ist es wichtig, die Skalierung der Variablen zur vereinheitlichen. Die z-Standardisierung ist ein übliches Vorgehen. Dabei wird der Mittelwert auf 0 transformiert und die SD auf 1; man spricht - im Falle von (hinreichend) normalverteilten Variablen - jetzt von der *Standardnormalverteilung*\index{Standardnormalverteilung}. Unterscheiden sich zwei Objekte A und B in einer standardnormalverteilten Variablen, so sagt dies nur etwas zur relativen Position von A zu B innerhalb ihrer Verteilung aus - im Gegensatz zu den Rohwerten.
```{r}
extra %>%
dplyr::select(i01, i02r) %>%
scale() %>% # z-standardisieren
head() # nur die ersten paar Zeilen abdrucken
```
Dieser Befehl liefert z-standardisierte Spalten zurück. Kommoder ist es aber, alle Spalten des Datensatzes zurück zu bekommen, wobei zusätzlich die z-Werte aller numerischen Variablen hinzugekommen sind:
```{r eval = FALSE}
extra %>%
mutate_if(is.numeric, funs("z" = scale)) -> extra
```
Der Befehl `mutate` berechnet eine neue Spalte; `mutate_if` tut dies nur, wenn die Spalte numerisch ist. Die neue Spalte wird berechnet als z-Transformierung der alten Spalte; zum Spaltenname wird ein "_z" hinzugefügt. Natürlich hätten wir auch mit `select` "händisch" die relevanten Spalten auswählen können. Betrachten Sie die ersten paar Zeilen der neu erstellte Tabelle!^[`head(extra)`]
### Quasi-Konstante finden
Hier suchen wir nach Variablen (Spalten), die nur einen Wert oder zumindest nur sehr wenige verschiedene Werte aufweisen. Oder, ähnlich: Wenn 99.9% der Fälle nur von einem Wert bestritten wird. In diesen Fällen kann man die Variable als "Quasi-Konstante" bezeichnen. Quasi-Konstanten sind für die Modellierung von keiner oder nur geringer Bedeutung; sie können in der Regel für weitere Analysen ausgeschlossen werden.
Haben wir z.B. nur Männer im Datensatz, so kann das Geschlecht nicht für Unterschiede im Einkommen verantwortlich sein. Besser ist es, die Variable Geschlecht zu entfernen. Auch hier sind Histogramme oder Boxplots von Nutzen zur Identifikation von (Quasi-)Konstanten. Alternativ kann man sich auch pro die Streuung (numerische Variablen) oder die Anzahl unterschiedlicher Werte (qualitative Variablen) ausgeben lassen:
```{r}
IQR(extra$n_facebook_friends, na.rm = TRUE) # keine Konstante
n_distinct(extra$sex) # es scheint 3 Geschlechter zu geben...
```
### Auf Normalverteilung prüfen
Einige statistische Verfahren gehen von normalverteilten Variablen aus, daher macht es Sinn, Normalverteilung zu prüfen. *Perfekte* Normalverteilung ist genau so häufig wie *perfekte* Kreise in der Natur. Entsprechend werden Signifikanztests, die ja auf perfekte Normalverteilung prüfen, *immer signifikant* sein, sofern die *Stichprobe groß* genug ist. Daher ist meist zweckmäßiger, einen graphischen "Test" durchzuführen: ein Histogramm, ein QQ-Plot oder ein Dichte-Diagramm als "glatt geschmirgelte" Variante des Histogramms bieten sich an (s. Abb. \@ref(fig:fig-norm-check)).
```{r fig-norm-check, echo = FALSE, fig.cap = "Visuelles Prüfen der Normalverteilung"}
extra %>%
dplyr::select(n_facebook_friends) %>%
dplyr::filter(n_facebook_friends <= 500) %>% # nicht größer als Q3
ggplot() +
aes(x = n_facebook_friends) +
geom_density() -> p1
extra %>%
ggplot() +
aes(x = extra_mean) +
geom_density() -> p2
gridExtra::grid.arrange(p1, p2, ncol = 2)
```
Während die der mittlere Extraversionswert recht gut normalverteilt ist, ist die Anzahl der Facebookfreunde ordentlich (rechts-)schief. Bei schiefen Verteilung können Transformationen Abhilfe schaffen; ein Thema, auf das wir hier nicht weiter eingehen.
### Werte umkodieren und partionieren ("binnen")
*Umkodieren*\index{Umkodieren} meint, die Werte zu ändern. Man sieht immer mal wieder, dass die Variable "gender" (Geschlecht) mit `1` und `2` kodiert ist. Verwechslungen sind da vorprogrammiert ("Ich bin mir echt ziemlich sicher, dass ich 1 für Männer kodiert habe, wahrscheinlich..."). Besser wäre es, die Ausprägungen `male` und `female` ("Mann", "Frau") o.ä. zu verwenden (vgl. Abb. \@ref(fig:umkodieren)).
```{r umkodieren, echo = FALSE, fig.cap = "Sinnbild für Umkodieren"}
knitr::include_graphics("images/typ_prob/umkodieren_crop.png")
```
Partitionieren\index{Partitionieren) oder *"Binnen"*\index{Binnen} meint, eine kontinuierliche Variablen in einige Bereiche (mindestens 2) zu zerschneiden. Damit macht man aus einer kontinuierlichen Variablen eine diskrete. Ein Bild erläutert das am einfachsten (vgl. Abb. \@ref(fig:cut-schere)).
```{r cut-schere, echo = FALSE, fig.cap = "Sinnbild zum 'Binnen'"}
knitr::include_graphics("images/typ_prob/cut_schere_crop.png")
```
#### Umkodieren und partionieren mit `car::recode`
Manchmal möchte man z.B. negativ gepolte Items umdrehen oder bei kategoriellen Variablen kryptische Bezeichnungen in sprechendere umwandeln. Hier gibt es eine Reihe praktischer Befehle, z.B. `recode` aus dem Paket `car`. Schauen wir uns ein paar Beispiele zum Umkodieren an.
```{r stats_test_recode, results = "hide"}
stats_test %>%
mutate(study_binned = car::recode(.$study_time,
"5 = 'sehr viel'; 2:4 = 'mittel'; 1 = 'wenig'",
as.factor.result = TRUE)) -> stats_test
stats_test %>%
mutate(study_binned = car::recode(.$study_time,
"5 = 'sehr viel'; 2:4 = 'mittel'; 1 = 'wenig'",
as.factor.result = FALSE)) -> stats_test
stats_test %>%
mutate(score_binned = car::recode(.$score,
"40:38 = 'sehr gut'; 37:35 = 'gut'; else = 'Weiterlernen'",
as.factor.result = TRUE)) -> stats_test
stats_test %>% select(score_binned, study_binned) %>% head
```
Der Befehle `recode` ist praktisch; mit `:` kann man "von bis" ansprechen (das ginge mit `c()` übrigens auch); `else` für "ansonsten" ist möglich und mit `as.factor.result` kann man entweder einen Faktor oder eine Text-Variable zurückgeliefert bekommen. Der ganze "Wechselterm" steht in Anführungsstrichen (`"`). Einzelne Teile des Wechselterms sind mit einem Strichpunkt (`;`) voneinander getrennt.
Das klassische Umkodieren von Items aus Fragebögen kann man so anstellen; sagen wir `interest` soll umkodiert werden:
```{r}
stats_test %>%
mutate(no_interest = car::recode(.$interest,
"1 = 6; 2 = 5; 3 = 4; 4 = 3; 5 = 2; 6 = 1; else = NA")) -> stats_test
glimpse(stats_test$no_interest)
```
Bei dem Wechselterm muss man aufpassen, nichts zu verwechseln; die Zahlen sehen alle ähnlich aus...
Testen kann man den Erfolg des Umpolens mit
```{r eval = FALSE}
dplyr::count(stats_test, interest)
dplyr::count(stats_test, no_interest)
```
Scheint zu passen. Noch praktischer ist, dass man so auch numerische Variablen in Bereiche aufteilen kann ("binnen"). Hier das Binnen von Prof. Schnaggeldi:
```{r}
stats_test %>%
mutate(bestanden =
car::recode(.$score,
"1:38 = 'durchgefallen';
else = 'bestanden'")) -> stats_test
```
Natürlich gibt es auch eine Pfeifen kompatible Version, um Variablen umzukodieren bzw. zu binnen: `dplyr::recode`^[https://blog.rstudio.org/2016/06/27/dplyr-0-5-0/]. Die Syntax ist allerdings etwas weniger komfortabel (da strenger), so dass wir an dieser Stelle bei `car::recode` bleiben.
#### Einfaches Umkodieren mit einer Logik-Prüfung
Nehmen wir an, wir möchten die Anzahl der Punkte in einer Statistikklausur (`score`) umkodieren in eine Variable "bestanden" mit den zwei Ausprägungen "ja" und "nein"; der griesgrämige Professor beschließt, dass die Klausur ab 25 Punkten (von 40) bestanden sei. Die Umkodierung ist also von der Art "viele Ausprägungen in zwei Ausprägungen umkodieren". Das kann man z.B. so erledigen:
```{r}
stats_test %>%
mutate(bestanden = score > 24) -> stats_test
head(stats_test$bestanden)
```
```{block2, wann-punkt, type='rmdcaution', echo = TRUE}
Wann braucht man einen Punkt `.` in einer dpylr-Pfeifenkette? Man braucht ihn nur dann, wenn man innerhalb der Pfeifenkette Nicht-dplyr-Befehle verwendet wie `mean` oder `recode`. Diese Befehle wissen nicht, dass sie gerade in einer Pfeifenkette stehen, daher müssen wir ihnen den Namen der Tabelle explizit erzählen. Genau genommen steht der Punkt für die Tabelle, so wie sie aus dem letzten Pfeifenschritt herausgekommen ist (z.B. schon mit einigen Zeilen weggefiltert).
```
Genauso könnte man sich die "Grenzfälle" - die Bemitleidenswerten mit 24 Punkten - anschauen (knapp daneben ist auch vorbei, so der griesgrämige Professor weiter):
```{r}
stats_test$Grenzfall <- stats_test$score == 24
dplyr::count(stats_test, Grenzfall)
```
Natürlich könnte man auch hier "Durchpfeifen":
```{r}
stats_test <-
stats_test %>%
mutate(Grenzfall = score == 24)
dplyr::count(stats_test, Grenzfall)
```
#### Binnen mit `cut`
Numerische Werte in Klassen zu gruppieren ("to bin", denglisch: "binnen") kann mit dem Befehl `cut` (and friends) besorgt werden.
Es lassen sich drei typische Anwendungsformen unterscheiden:
Eine numerische Variable ...
1. in *k* gleich große Klassen gruppieren (gleichgroße Intervalle)
2. so in Klassen gruppieren, dass in jeder Klasse *n* Beobachtungen sind (gleiche Gruppengrößen)
3. in beliebige Klassen gruppieren
##### Gleichgroße Intervalle
Nehmen wir an, wir möchten die numerische Variable "Körpergröße" in drei Gruppen einteilen: "klein", "mittel" und "groß". Der Range von Körpergröße soll gleichmäßig auf die drei Gruppen aufgeteilt werden, d.h. der Range (Intervall) der drei Gruppen soll gleich groß sein. Dazu kann man `cut_interval` aus `ggplot2` nehmen^[d.h. `ggplot2` muss geladen sein; wenn man `tidyverse` lädt, wird `ggplot2` automatisch auch geladen].
```{r}
temp <- cut_interval(x = stats_test$score, n = 3)
levels(temp)
```
`cut_interval` liefert eine Variable vom Typ `factor` zurück. Hier haben wir das Punktespektrum in drei gleich große Bereiche unterteilt (d.h. mit jeweils gleichem Punkte-Range).
##### Gleiche Gruppengrößen
```{r}
temp <- cut_number(stats_test$score, n = 2)
str(temp)
median(stats_test$score)
```
Mit `cut_number` (aus ggplot2) kann man einen Vektor in `n` Gruppen mit (etwa) gleich viel Observationen einteilen. Hier haben wir `score` am Median geteilt.
> Teilt man einen Vektor in zwei gleich große Gruppen, so entspricht das einer Aufteilung am Median (Median-Split).
##### In beliebige Klassen gruppieren
```{r}
stats_test %>%
mutate(punkte_gruppe = cut(stats_test$score,
breaks = c(-Inf, 25, 29, 33, 37, 40),
labels = c("5", "4", "3", "2", "1"))) -> stats_test
dplyr::count(stats_test, punkte_gruppe)
```
`cut` ist im Standard-R (Paket "base") enthalten. Mit `breaks` gibt man die Intervallgrenzen an. Zu beachten ist, dass man eine Unter- bzw. Obergrenze angeben muss. D.h. der kleinste Wert in der Stichprobe wird nicht automatisch als unterste Intervallgrenze herangezogen. Anschaulich gesprochen ist `cut` ein Messer, das ein Seil (die kontinuierliche Variable) mit einem oder mehreren Schnitten zerschneidet (vgl. Abb. \@ref(fig:cut-schere)). Wenn wir 6 Schnitte (`breaks`) tun, haben wir 5 Teile, wie Abb. \@ref(fig:cut-schere) zeigt. Darum müssen wir auch nur 5 (6-1) `labels` für die Teile vergeben.
## Deskriptive Statistiken berechnen
### Mittelwerte pro Zeile berechnen
#### `rowMeans`
Um Umfragedaten auszuwerten, will man häufig einen Mittelwert *pro Zeile* berechnen. Normalerweise fasst man eine *Spalte* zu einer Zahl zusammen; aber jetzt, fassen wir eine *Zeile* zu einer Zahl zusammen. Der häufigste Fall ist, wie gesagt, einen Mittelwert zu bilden für jede Person. Nehmen wir an, wir haben eine Befragung zur Extraversion durchgeführt und möchten jetzt den mittleren Extraversionswert pro Person (d.h. pro Zeile) berechnen.
```{r}
extra_items <- extra %>%
dplyr::select(i01:i10)
extra %>%
mutate(extra_mean = rowMeans(extra_items)) -> extra
```
Da der Datensatz über 28 Spalten verfügt, wir aber nur 10 Spalten heranziehen möchten, um Zeilen auf eine Zahl zusammenzufassen, bilden wir als Zwischenschritt einen "schmäleren" Datensatz, `extra_items`. Im Anschluss berechnen wir mit `rowMeans` die Mittelwerte pro Zeile (engl. "row").
### Mittelwerte pro Spalte berechnen
Eine Möglichkeit ist der Befehl `summary` aus `dplyr`.
```{r}
stats_test %>%
na.omit %>%
summarise(mean(score),
sd(score),
median(score),
IQR(score))
```
Die Logik von `dplyr` lässt auch einfach Subgruppenanalysen zu. Z.B. können wir eine Teilmenge des Datensatzes mit `filter` erstellen und dann mit `group_by` Gruppen vergleichen:
```{r}
stats_test %>%
filter(study_time > 1) %>%
group_by(interest) %>%
summarise(median(score, na.rm = TRUE))
```
Wir können auch Gruppierungskriterien unterwegs erstellen:
```{r}
stats_test %>%
na.omit %>%
filter(study_time > 1) %>%
group_by(intessiert = interest > 3) %>%
summarise(md_gruppe = median(score))
```
Die beiden Gruppen von `interessiert` sind "ja, interessiert" (`interest > 3` ist `TRUE`) und "nein, nicht interessiert" (`interest > 3` ist `FALSE`). Außerdem haben wir der Spalte, die die Mediane zurückliefert einen ansprechenderen Namen gegeben (`md_gruppe`).
Etwas expliziter wäre es, `mutate` zu verwenden, um die Variable `interessiert` zu erstellen:
```{r}
stats_test %>%
na.omit %>%
filter(study_time > 1) %>%
mutate(interessiert = interest > 3) %>%
group_by(interessiert) %>%
summarise(md_gruppe = median(score),
mw_gruppe = mean(score))
```
Dieses Mal haben wir nicht nur eine Spalte mit den Medianwerten, sondern zusätzlich noch mit Mittelwerten berechnet.
```{block2, robust-only, type='rmdcaution', echo = TRUE}
Statistiken, die auf dem Mittelwert (arithmetisches Mittel) beruhen, sind nicht robust gegenüber Ausreißer: Schon wenige Extremwerte können diese Statistiken so verzerren, dass sie erheblich an Aussagekraft verlieren.
Daher: besser robuste Statistiken verwenden. Der Median, der Modus und der IQR bieten sich an.
```
### Korrelationstabellen berechnen
Korrelationen bzw. Korrelationstabellen lassen sich mit dem R-Standardbefehl `cor` berechnen:
```{r cor-demo1}
stats_test %>%
select(study_time,interest,score) %>%
cor()
```
Oh! Lauter NAs! Besser wir löschen Zeilen mit fehlenden Werten bevor wir die Korrelation ausrechnen:
```{r cor-demo2}
stats_test %>%
select(study_time:score) %>%
na.omit %>%
cor()
```
Alternativ zu `cor` kann man auch `corrr:correlate` verwenden:
```{r correlate-demo}
stats_test %>%
select(study_time:score) %>%
correlate
```
`correlate` hat den Vorteil, dass es bei fehlenden Werten einen Wert ausgibt; die Korrelation wird paarweise mit den verfügbaren (nicht-fehlenden) Werten berechnet. Außerdem wird eine Dataframe (genauer: tibble) zurückgeliefert, was häufig praktischer ist zur Weiterverarbeitung. Wir könnten jetzt die resultierende Korrelationstabelle plotten, vorher "rasieren" wir noch das redundanten obere Dreieck ab (da Korrelationstabellen ja symmetrisch sind):
```{r rplot-demo}
stats_test %>%
select(study_time:score) %>%
correlate %>%
shave %>%
rplot
```
## Befehlsübersicht
Tabelle \@ref(tab:befehle-praxisprobleme) stellt die Befehle dieses Kapitels dar.
```{r befehle-praxisprobleme, echo = FALSE}
df <- readr::read_csv("includes/Befehle_Praxisprobleme.csv")
knitr::kable(df, caption = "Befehle des Kapitels 'Praxisprobleme'")
```