-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathworkshop-teil2.Rmd
459 lines (351 loc) · 14.7 KB
/
workshop-teil2.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
---
title: "Workshop: Statistische Daten und Wahlergebnisse als Barcodes visualisieren"
output:
html_document:
css: styles.css
toc: true
toc_float: false
theme: cosmo
highlight: tango
---
> _"make a hundred charts and pick one"_
> --- Amanda Cox
### Vorbereitung
1. Workshop-Material herunterladen: [material.zip](https://gka.github.io/nr17-r-workshop/material.zip)
2. ZIP-Archiv an einem beliegen Ort entpacken
3. Anschließend die Datei `workshop-teil2.Rmd` in RStudio öffnen
# Daten einlesen
Zunächst laden wir die benötigten R-Pakete. `readr` stellt Funktionen
zum einlesen von CSV Dateien bereit, `dplyr` liefert praktische
Werkzeuge zum analysieren und transformieren von Tabellen, und
`ggplot2` ist das Visualisierungs-Framework das wir heute benutzen.
Die folgenen Code Blöcke (genannt "chunks") lassen sich in RStudio
durch Klick auf den grünen Pfeil oben rechts im Block ausführen.
```{r setup}
needs(readr, dplyr, ggplot2)
# falls das nicht klappt, muss needs noch installiert werden
# install.packages('needs')
# library(needs)
theme_set(theme_light())
```
Nun laden wir den im [ersten Teil](workshop-teil1.html) vorbereiteten Datensatz
`btw-final.csv` und speichern ihn in der Variable `btw`. Die `mutate`
Anweisung in der zweiten Zeile können wir erstmal ignorieren (sie
legt nur die Reihenfolge fest, in der die Werte in der Spalte
_gruppe_ nachher in den Diagrammen auftauchen).
```{r daten einlesen}
btw <- read_csv('btw-final.csv') %>%
mutate(gruppe=factor(gruppe, c('L_SG', 'C_SG', 'C_RRG', 'L_RRG')))
```
Nach dem Ausführen des Blocks erscheint in RStudio oben links im
"Environment" Fenster ein neuer Eintrag "btw". Durch klick auf den
Eintrag können wir uns den Datensatz genauer ansehen.
# Fingerübungen mit ggplot
Kommen wir zum spannenden Teil: Daten visualisieren mit `ggplot2`. Die
Syntax ist etwas gewöhnungsbedürftig, aber sehr mächtig wenn man
erstmal den Dreh raus hat.
Die Funktion `ggplot()` macht allein erst mal nichts, außer einen
leeren Plot zu erstellen und ihm einen Datensatz zuzuordnen.
```{r leerer plot, fig.height=2}
ggplot(btw)
```
Über den zweiten Parameter `mapping` kann man Spalten aus dem
Datensatz zu Plot-Eigenschaften (den sogenannten "aesthetics")
zuordnen, wie z.B. `x`, `y` oder `color`. ggplot erstellt dann
ggf. schon automatisch Achsen und Gitternetzlinien.
```{r nur achsen}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD))
```
Um nun Datenpunkte auf den Plot zu bekommen müssen wir weitere Ebenen
zum Plot hinzufügen. Dazu "addieren" wir weitere ggplot-Funktionen zu
dem Ergebnis des ersten `ggplot()` Aufrufs. In diesem Fall wollen wir
Punkte, also benutzen wir `geom_point()`:
```{r schwarze punkte}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD)) +
geom_point()
```
## Plot-Eigenschaften anpassen
Jede ggplot Funktion hat selbst eine Reihe von Parametern über die
das Aussehen der Plot-Ebene angepasst werden kann.
Im Falle von `geom_point` lassen sich Form, Farbe, Größe und viele
weitere visuelle Eigenschaften der Symbole ändern (für eine Liste
aller möglichen Parameter können wir die Hilfefunktion benutzen):
```{r rote dreiecke}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD)) +
geom_point(color='red', shape=2)
```
Anstatt die Eigenschaften nur "statisch" für alle Punkte gleichermaßen
zu verändern kann man auch weitere Spalten aus dem Datensatz verwenden
und so neue Dimensionen zu dem Plot hinzufügen.
Im folgenden Beispiel färben wir die Symbole je nach dem Wert der
Spalte _landslide_, und wir machen die Symbolgröße abhängig von dem
Wert in der Spalte _Ohne.Hauptschulabschluss_.
```{r bubble plot}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD)) +
geom_point(aes(color=landslide, size=Ohne.Hauptschulabschluss), alpha=0.5)
```
## Weitere Ebenen hinzufügen
Man kann auch noch weitere Ebenen zum Plot hinzufügen, indem man noch mal `geom_point()` hinzufügt und die `y` aesthetic überschreibt.
Hier fügen wir pro Wahlkreis noch einen weiteren Punkt zum Plot hinzu
aber benutzen das CDU Wahlergebnis für die y-Koordinaten:
```{r roten punkte und schwarze kreuze}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD)) +
geom_point(color='red') +
geom_point(aes(y=CDU), color='black', shape=3)
```
Die Warnung "Removed 45 rows containing missing values" kommt daher,
das in den bayerischen Wahlkreisen nicht CDU sondern CSU gewählt wird.
Wir können das Problem lösen, in dem wir eine neue Spalte _CDUCSU_
anlegen, in der beide Parteien zusammengefasst werden:
```{r}
btw <- btw %>%
mutate(CDUCSU=coalesce(CDU, CSU))
```
Wenn wir den obigen Plot nun noch einmal für die neue _CDUCSU_ Spalte
erzeugen ist die Warnung verschwunden:
```{r cducsu}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD)) +
geom_point(color='red') +
geom_point(aes(y=CDUCSU), color='black', shape=3)
```
## Logarithmische Achse
Da wir feststellen dass sich fast alle Punkte sich an den linken Rand des Plots
drängen, bietet sich vielleicht eine logarithmische x-Achse an. Und
auch das können wir mit einer weiteren "Addition" dem Plot hinzufügen:
```{r plot mit log scale}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD)) +
geom_point(color='red') +
geom_point(aes(y=CDUCSU), color='black', shape=3) +
scale_x_log10()
```
## Trend-Linien hinzufügen
Wie wäre es mit Trend-Linien für unsere beiden Punktmengen? Kein
Problem mit der ggplot-Funktion `geom_smooth()`. Auch hier fügen wir
einen zweiten Aufruf für die CDU Punkte ein und überschreiben die
`y` Eigenschaft.
```{r plot mit trendlinien}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD)) +
geom_point(color='red') +
geom_point(aes(y=CDUCSU), color='black', shape=3) +
geom_smooth(color='red') +
geom_smooth(aes(y=CDUCSU), color='black') +
scale_x_log10()
```
Standardmäßig benutzt `geom_smooth()` eine Loess-Funktion zur
Annäherung der Messwerte und zeigt im Hintergrund einen grauen Bereich
für das Vertrauensinterval an. Mit dem Parameter `method='lm'` legen
wir fest das wir eine gerade (lineare) Trendlinie möchten, und
mit `se=FALSE` schalten das Vertrauensinterval ab.
```{r plot mit geraden trendlinien}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD)) +
geom_point(color='red') +
geom_point(aes(y=CDUCSU), color='black', shape=3) +
geom_smooth(color='red', method='lm', se=FALSE) +
geom_smooth(aes(y=CDUCSU), color='black', method='lm', se=FALSE) +
scale_x_log10()
```
## Vertikale min/max Linien
Oder wie wäre es mit vertikalen Linien die die für jeden Wahlkreis
beiden Punkte verbinden? Dazu können wir z.B. die Funktion
`geom_linerange` verwenden, der wir nur die Parameter `ymin` und
`ymax` mitteilen müssen.
```{r vertikale linien}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD)) +
geom_point(color='red') +
geom_point(aes(y=CDUCSU), color='black') +
geom_linerange(aes(ymin=SPD, ymax=CDUCSU), alpha=0.2) +
scale_x_log10()
```
## Small multiples
Es kommt noch besser: mit `facet_grid` können wir den Plot in mehrere Panels unterteilen, z.b. nach landslide Wahlkreisen und nicht-landslide Wahlkreisen:
```{r split plot mit trendlinien}
ggplot(btw, aes(x=Bevölkerungsdichte, y=SPD)) +
geom_point(color='red') +
geom_point(aes(y=CDUCSU), color='black') +
geom_linerange(aes(ymin=SPD, ymax=CDUCSU), alpha=0.2) +
scale_x_log10() +
facet_grid(. ~ landslide)
```
Wir wir sehen sind die Möglichkeiten fast endlos. Doch nun zurück
zu dem was wir eigentlich machen wollten: Barcodes!
# Barcodes visualisieren!
Barcodes sind nichts anderes als vertikale Linien, also fangen wir an
mit `geom_vline`. geom_vline benötigt die aesthetic `xintercept`, die
wir erstmal auf die Spalte _Bevölkerungsdichte_ mappen.
```{r bunte barcodes, fig.height=2, fig.width=8}
ggplot(btw) +
geom_vline(aes(xintercept=Bevölkerungsdichte, color=gruppe))
```
Zunächst mal möchten wir bessere Farben benutzen, damit wir die
Gruppen besser lesen können. Dazu definieren wir zunächst ein paar
Farben unserer Wahl (schwarz für schwarz-gelb und rot für rot-rot-grün)
und fügen diese dann per `scale_colour_manual` dem Plot hinzu:
```{r schwarz rote barcodes, fig.height=2, fig.width=8}
mycolors <- c(L_SG='#333333', C_SG='#999999', L_RRG='#cc0000', C_RRG='#ee9999')
ggplot(btw) +
geom_vline(aes(xintercept=Bevölkerungsdichte, color=gruppe)) +
scale_colour_manual(values = mycolors)
```
Leider ist immer noch alles etwas zu unübersichtlich im Plot.
Also benutzen wir wieder unseren Freund `facet_grid()`, um die
Barcodes in vier Gruppen zu gliedern.
Mit `guide=FALSE` schalten wir in `scale_colour_manual()` die Farblegende ab, die nun überflüssig geworden ist.
```{r barcodes mit facet grid, fig.asp=0.4}
ggplot(btw) +
geom_vline(aes(xintercept=Bevölkerungsdichte, color=gruppe)) +
scale_colour_manual(values = mycolors, guide=FALSE) +
facet_grid(gruppe ~ .)
```
Natürlich können wir auch hier wieder eine logarithmische
Achse benutzen, in dem wir die `scale_x_log10()` hinzufügen:
```{r barcodes mit log scale, fig.asp=0.4}
ggplot(btw) +
geom_vline(aes(xintercept=Bevölkerungsdichte, color=gruppe)) +
scale_colour_manual(values = mycolors, guide=FALSE) +
facet_grid(gruppe ~ .) +
scale_x_log10()
```
Die grauen Ränder um den Plot können wir entfernen in dem wir das
`theme_minimal()` verwenden:
```{r barcodes mit theme_minimal, fig.asp=0.4}
ggplot(btw) +
geom_vline(aes(xintercept=Bevölkerungsdichte, color=gruppe)) +
scale_colour_manual(values = mycolors, guide=FALSE) +
facet_grid(gruppe ~ .) +
scale_x_log10() +
theme_minimal()
```
Damit wir in Zukunft nicht jedes Mal wieder das Theme hinzufügen müssen,
setzen wir es jetzt mit `theme_set()` als neues Standard-Theme fest:
```{r minimal theme}
theme_set(theme_minimal())
```
## Mittelwerte darüberlegen
Um unseren Plot abzuschließen möchten wir mit weiteren Linien die
Gruppenmittelwerte des dargestellten Wertes zum Diagramm hinzufügen.
Dazu berechnen wir zunächst die Gruppenmittelwerte mit den _dplyr_
Funktionen `group_by()` und `summarize()` und speichern sie in einem
neuen Datensatz _mittelwerte_. Anschließend können wir die neuen
Linien durch addieren eines weiteren `geom_vline()` Aufrufs zum Plot
hinzufügen, wobei wir mit `data=mittelwerte` den Bezug zum neu
erstellten Datensatz erzeugen:
```{r barcodes mit mittelwerten, fig.asp=0.4}
mittelwerte <- btw %>%
group_by(gruppe) %>%
summarize(mittel=mean(Bevölkerungsdichte))
ggplot(btw) +
geom_vline(aes(xintercept=Bevölkerungsdichte, color=gruppe)) +
scale_colour_manual(values = mycolors, guide=FALSE) +
facet_grid(gruppe ~ ., drop=T) +
scale_x_log10() +
geom_vline(aes(xintercept=mittel), data=mittelwerte,
color='blue', size=1.5)
```
Jetzt ist unser Plot soweit erstmal fertig!
## Plot als benutzerdefinierte Funktion wiederverwenden
Damit wir den Plot einfacher wiederverwenden können möchten wir ihn
in einer benutzerdefinierten Funktion ablegen, der wir einfach den
Namen der zu visualisierenden Spalte übergeben können.
Im Prinzip passiert in der Funktion genau das, was wir bis hier hin
alles gemacht haben. Was sich ändert ist, das wir zu Beginn der
Funktion einen neuen Datensatz anlegen, in dem nur die Spalten
_gruppe_ und _wert_ vorhanden sind, wobei letztere den Wert der
übergebenden Spalte enthält.
Mit `ggtitle()` und `xlab()` steuern wir die Beschriftung der
Diagramme und mit dem abschließenden `theme()` Aufruf passen wir die
Textgröße sowie die Abstände rum um den Plot an.
```{r barcode funktion, fig.asp=0.4}
barcode_plot <- function(spalte) {
mycolors <- c(L_SG='#333333', C_SG='#999999',
L_RRG='#cc0000', C_RRG='#ee9999')
data <- btw %>%
filter(landslide) %>%
select_('gruppe', wert=as.name(spalte))
mittelwerte <- data %>%
group_by(gruppe) %>%
summarize(mittel=mean(wert))
data %>%
mutate(wert2=wert+rnorm(nrow(data), 0, 0.02)) %>% # ein bisschen jittering
ggplot() +
geom_vline(aes(xintercept=wert2, color=gruppe)) +
scale_colour_manual(values = mycolors, guide=FALSE) +
facet_grid(gruppe ~ ., drop=T) +
geom_vline(aes(xintercept=mittel), data=mittelwerte,
color='blue', size=1.5) +
ggtitle('', subtitle=paste0(spalte, '\n')) +
xlab('') +
theme(strip.text = element_text(size=7),
plot.margin = margin(0,5,3,5,'mm'))
}
```
Nun können wir die Funktion einfach bequem aufrufen, z.B. für die
Spalte _Arbeitslosenquote_:
```{r funktion aufrufen, fig.asp=0.3}
barcode_plot('Arbeitslosenquote')
```
Oder _Bergbau_:
```{r funktion aufrufen 2, fig.asp=0.3}
barcode_plot('Bergbau')
```
## PDF mit allen Barcode Varianten speichern
Als krönenden Abschluss können wir mit wenigen weiteren Zeilen
Code ein PDF mit Barcodes von allen Spalten in unserem Datensatz
erzeugen.
Zunächst stellen wir sicher, das das Paket `gridExtra` geladen wird:
```{r pdf 1}
needs(gridExtra)
```
Dann legen wir einen Vektor von Spaltennamen an, die sich für die
Visualisierung als Barcode eignen:
```{r pdf spalten}
spalten <- colnames(btw)[18:55]
```
Mit `lapply` führen wir unsere `barcode_plot` Funktion dann mit einem
Rutsch für alle Spalten in unserem Vektor auf. Wir leiten das Ergebnis
direkt in die `marrangeGrob` Funktion weiter, die die einzelnen Plots
auf mehrere Seiten verteilt anordnet (in einem 7-zeiligen und
1-spaltigen Layout):
```{r pdf arrangieren, cache=TRUE}
plots <- lapply(spalten, barcode_plot) %>%
marrangeGrob(nrow=7, ncol=1, padding=unit(10, "mm"))
```
Anschließend benutzen wir `ggsave` um die arrangierten Plots in einer
PDF-Datei im DIN A4 Format zu speichern.
```{r pdf speichern, dependson=plots}
ggsave("plots.pdf", plots, width=210, height=297, units='mm')
```
# Extra: Weitere nützliche ggplot Funktionen
Falls wir noch Zeit am Ende haben, oder zum Weiterüben zu Hause:
**Histogramme** von Variablen verschiedener Gruppen vergleichen:
```{r histogramme}
btw %>%
ggplot(aes(Alter.unter18, fill=gruppe)) +
geom_histogram(bins=40) +
scale_fill_manual(values = mycolors, guide=FALSE) +
facet_grid(gruppe ~ .)
```
Eine Alternative zu Histogrammen sind **Density-Kurven**:
```{r density kurven, fig.asp=0.7}
btw %>%
ggplot(aes(Alter.unter18, fill=gruppe)) +
geom_density(bw=0.2, color=FALSE, alpha=0.8) +
scale_fill_manual(values = mycolors, guide=FALSE) +
facet_grid(gruppe ~ .)
```
**Boxplots** sind auch sehr beliebt:
```{r boxplots, fig.asp=0.5}
btw %>%
ggplot(aes(gruppe, Alter.unter18, fill=gruppe)) +
geom_boxplot() +
scale_fill_manual(values = mycolors, guide=FALSE) +
coord_flip()
```
**Violin Plots** sind eine Mischung aus Boxplot und Density-Kurve:
```{r violin plots, fig.asp=0.5}
btw %>%
ggplot(aes(gruppe, Alter.unter18, fill=gruppe)) +
geom_violin(adjust=0.5, color='black', alpha=0.8,
draw_quantiles=c(0.25, 0.5, 0.75), trim=FALSE) +
scale_fill_manual(values = mycolors, guide=FALSE) +
coord_flip()
```