diff --git a/slides/dplyr-grammar.Rmd b/slides/dplyr-grammar.Rmd index e9b97996145440999ba9248b81f6ebdc3e997bf3..4370ba04d0ad533a67e20a30700b1eb816166408 100644 --- a/slides/dplyr-grammar.Rmd +++ b/slides/dplyr-grammar.Rmd @@ -1,15 +1,12 @@ --- -title: "Einführung in dplyr-Grammatik" -subtitle: "Daten bändigen & visualisieren" -author: "B. Philipp Kleer" -date: "11. Oktober 2021" -institute: "Methodentage 2021" output: xaringan::moon_reader: - css: [metropolis, metropolis-fonts, "./styles/scrollable.css"] + css: ["./styles/slides.css"] nature: + seal: false highlightStyle: github highlightLines: true + highlightLanguage: ["r"] countIncrementalSlides: false ratio: '16:9' slideNumberFormat: | @@ -19,7 +16,6 @@ output: </div>` navigation: scroll: false - --- ```{r setup, include=FALSE} @@ -27,22 +23,20 @@ library("knitr") library("rmarkdown") library("tidyverse") library("DT") +library("icons") -uni <- readRDS("./datasets/uni.rds") +uni <- readRDS("../datasets/uni.rds") -opts_chunk$set(fig.path = "pics/s6-", # path for calculated figures +opts_chunk$set(fig.path = "pics/dplyr-", # path for calculated figures fig.align = "center", # alignment of figure (also possible right, left, default) fig.show = "hold", # how to show figures: hold -> direct at the end of code chunk; animate: all plots in an animation - fig.width = 3, # figure width - fig.height = 4, # figure height + out.width = "60%", echo = TRUE, # Code is printed eval = FALSE, # Code is NOT evaluated warning = FALSE, # warnings are NOT displayed message = FALSE, # messages are NOT displayed - size = "tiny", # latex-size of code chunks background = "#E7E7E7", # background color of code chunks comment = "", # no hashtags before output - options(width = 80), results = "markdown", rows.print = 15 ) @@ -65,7 +59,25 @@ htmltools::tagList( ``` -# Starten wir! +class: center, title-slide, middle + +# Einführung in dplyr-Grammatik + +## Daten bändigen & visualisieren + +### B. Philipp Kleer + +### Methodentage 2021 + +### 11. Oktober 2021 + +.social[ +   [`r icons::icon_style(fontawesome("orcid"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://orcid.org/0000-0003-1935-387X)   [`r icons::icon_style(fontawesome("gitlab"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://gitlab.com/bpkleer)   [`r icons::icon_style(fontawesome("university"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://www.uni-giessen.de/faculties/f03/departments/dps/staff/researchers/kleer?set_language=en)   [`r icons::icon_style(fontawesome("researchgate"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://www.researchgate.net/profile/Benedikt_Kleer) +] + +--- + +# Starten wir jetzt! Nun tauchen wir in die Welt von **dplyr** ein. Das Paket nutzt man oft, um Datenstrukturen zu erkunden oder Transformationen vorzunehmen. Dabei gibt es einen Grundstock an Vokabeln, die über **piping** miteinander verbunden werden. @@ -154,16 +166,16 @@ Mit **select()** wählen wir Spalten aus, die uns angezeigt werden ``` {r select, eval=FALSE} -select(uni, # Datenquelle - c(mot, # ausgewählte Spalten - term +select(uni, + c(mot, #<< + term #<< ) ) ``` ``` {r select-unseen, eval=TRUE, echo=FALSE} -select(uni, # Datenquelle - c(mot, # ausgewählte Spalten +select(uni, + c(mot, term ) ) %>% @@ -174,11 +186,11 @@ select(uni, # Datenquelle --- # slice() -Demgegenüber können wir mit **slice()** Zeilen auswählen, also Fälle: +Demgegenüber können wir mit **slice()** Zeilen auswählen, also Fälle: ``` {r slice, eval=TRUE} -slice(uni, # Datenquelle - 50:55 # ausgewählte Zahlen +slice(uni, + 50:55 #<< ) ``` @@ -203,22 +215,22 @@ Mit `filter()` können wir spezifische Fälle des Datensatzes auswählen. Zur Er # filter() Zur Anwendung: -.pull-left[ +.pull-left-40[ ``` {r filter, eval = FALSE} -filter(uni, # Datenquelle - city == "Giessen" # Filtervariable #<< +filter(uni, + city == "Giessen" #<< ) ``` ] -- -.pull-right[ +.pull-right-60[ .scrollable[ ```{r filter2, eval=TRUE, echo=FALSE, size=8} filter(uni, - city == "Frankfurt" & study == "Political Science" + city == "Giessen" ) ``` ] @@ -227,24 +239,24 @@ filter(uni, --- # arrange() -.pull-left[ +.pull-left-40[ Mit **arrange()** können wir den Datensatz sortieren. ``` {r arrange, eval=FALSE} arrange(uni, - abi + abi #<< ) ``` ] -- -.pull-right[ +.pull-right-60[ .scrollable[ -``` {r arrange-true, eval=TRUE, echo=FALSE} +``` {r arrange-out, eval=TRUE, echo=FALSE} arrange(uni, - abi + abi ) ``` ] @@ -253,7 +265,7 @@ arrange(uni, --- # arrange() -.pull-left[ +.pull-left-40[ Die Sortierung ist dabei immer aufsteigend. Dies kann man über die Funktion ```desc()``` ändert (**desc**ending): ``` {r arrange2, eval=FALSE} arrange(uni, @@ -264,9 +276,9 @@ arrange(uni, -- -.pull-right[ +.pull-right-60[ .scrollable[ -``` {r arrange2-true, eval=TRUE, echo=FALSE} +``` {r arrange2-out, eval=TRUE, echo=FALSE} arrange(uni, desc(abi) ) @@ -277,7 +289,7 @@ arrange(uni, --- # arrange() -.pull-left[ +.pull-left-40[ Alternativ kann man auch einfach ein Minuszeichen vor die Variable, nach der sortiert werden soll, setzen: ``` {r arrange3, eval=FALSE} @@ -289,9 +301,9 @@ arrange(uni, -- -.pull-right[ +.pull-right-60[ .scrollable[ -``` {r arrange3-true, eval=TRUE, echo=FALSE} +``` {r arrange3-out, eval=TRUE, echo=FALSE} arrange(uni, -abi ) @@ -302,7 +314,7 @@ arrange(uni, --- # mutate() -.pull-left-narrow[ +.pull-left-40[ Mit **mutate()** werden neue Variablen geschaffen. Zum Beispiel könnten wir eine Variable schaffen, die den Abstand zum Mittelwert in der Variable `abi` misst. @@ -312,24 +324,25 @@ Zum Beispiel könnten wir eine Variable schaffen, die den Abstand zum Mittelwert -- -.pull-right-wide[ +.pull-right-60[ ``` {r mutate, eval=FALSE} mutate(uni, abiDist = abi - mean(abi, #<< na.rm = TRUE #<< - ). #<< + ) #<< ) ``` -] .scrollable[ -``` {r mutate-true, eval=TRUE, echo=FALSE} +``` {r mutate-out, eval=TRUE, echo=FALSE} mutate(uni, - abiDist = abi - mean(abi, - na.rm = TRUE - ) - ) + abiDist = abi - mean(abi, + na.rm = TRUE + ) + ) %>% + select(ID, abi, abiDist, mot, study, city, distance, term) ``` + ] ] @@ -355,30 +368,34 @@ case_when(Fallauswahl ~ neuer Codewert) Im Beispiel: ``` {r mutate2, eval=FALSE} mutate(uni, - dum.pum = case_when(city == "Marburg" ~ 1, # "Fallauswahl" ~ "neuer Codewert" #<< - city == "Gießen" ~ 0, #<< - city == "Frankfurt" ~ 0 #<< - ) + dumPum = case_when(city == "Marburg" ~ 1, # "Fallauswahl" ~ "neuer Codewert" #<< + city == "Gießen" ~ 0, #<< + city == "Frankfurt" ~ 0 #<< + ) ) ``` --- # mutate() & case_when() + +.center[ .scrollable[ -``` {r mutate2-true, eval=TRUE, echo=FALSE} +``` {r mutate2-out, eval=TRUE, echo=FALSE} mutate(uni, - dum.pum = case_when(city == "Marburg" ~ 1, # "Fallauswahl" ~ "neuer Codewert" - city == "Gießen" ~ 0, - city == "Frankfurt" ~ 0 - ) + dumPum = case_when(city == "Marburg" ~ 1, # "Fallauswahl" ~ "neuer Codewert" + city == "Gießen" ~ 0, + city == "Frankfurt" ~ 0 + ) ) ``` ] +] --- # mutate() & case_when() + Auch hier könnten mehrere Bedingungen verknüpft werden: So möchten wir einen Dummy schaffen, der anzeigt, ob eine Person in Marburg Erziehungswissenschaften studiert. -- @@ -386,8 +403,8 @@ Auch hier könnten mehrere Bedingungen verknüpft werden: So möchten wir einen Wir würden wie folgt beginnen: ``` {r mutate3} mutate(uni, - dum.pum.es = case_when(city == "Marburg" & study == "Educational Science" ~ 1 #<< - ) + dumPumEs = case_when(city == "Marburg" & study == "Educational Science" ~ 1 #<< + ) ) ``` @@ -399,24 +416,27 @@ Wenn man nicht alle verschiedenen Kombinationen eingeben möchte und zum Beispie ``` {r mutate4, eval=FALSE} mutate(uni, - dum.pum.es = case_when(city == "Marburg" & study == "Educational Science" ~ 1, - TRUE ~ 0 #<< - ) + dumPumEs = case_when(city == "Marburg" & study == "Educational Science" ~ 1, + TRUE ~ 0 #<< + ) ) ``` --- # mutate() & case_when() + +.center[ .scrollable[ -``` {r mutate4-true, eval=TRUE, echo=FALSE} +``` {r mutate4-out, eval=TRUE, echo=FALSE} mutate(uni, - dum.pum.es = case_when(city == "Marburg" & study == "Educational Science" ~ 1, - TRUE ~ 0 - ) + dumPumEs = case_when(city == "Marburg" & study == "Educational Science" ~ 1, + TRUE ~ 0 + ) ) ``` ] +] --- @@ -439,12 +459,23 @@ In **summarise()** können verschiedene Funktionen genutzt werden, die auf die V **Wichtig:** Das Ausgabe-Format ist immer ein *tibble*. -```{r summarize2, eval=TRUE} +.pull-left[ +```{r summarize2} +summarise(uni, + mean(term), + mean(mot) + ) +``` +] + +.pull-right[ +```{r summarize2-out, eval=TRUE, echo=FALSE} summarise(uni, mean(term), mean(mot) ) ``` +] --- @@ -453,7 +484,8 @@ Die Unterfunktion **summarise_if()** bietet dazu die Möglichkeit leicht auf ein -- -``` {r summarize3, eval=TRUE} +.pull-left-40[ +``` {r summarize3} summarise_if(uni, is.numeric, #<< list(mean = mean, @@ -461,6 +493,22 @@ summarise_if(uni, ) ) ``` +] + +-- + +.pull-right-60[ +.center[ +``` {r summarize3-out, eval=TRUE, echo=FALSE} +summarise_if(uni, + is.numeric, + list(mean = mean, + sd = sd + ) + ) +``` +] +] -- @@ -473,7 +521,25 @@ Die Unterfunktion **summarise_at()** bietet die Möglichkeit nur bei bestimmten -- -``` {r summarize4, eval=TRUE} +.pull-left-30[ +``` {r summarize4} +summarise_at(uni, + vars(mot, #<< + abi, #<< + term #<< + ), + list(mean = mean, + sd = sd + ) + ) +``` +] + +-- + +.pull-right-60[ +.center[ +``` {r summarize4-out, eval=TRUE, echo=FALSE} summarise_at(uni, vars(mot, #<< abi, #<< @@ -484,25 +550,25 @@ summarise_at(uni, ) ) ``` +] +] --- # group_by() -.pull-left[ +.pull-left-40[ Mit **group_by()** kann der Datensatz gruppiert werden, also zum Beispiel nach eine kategoriellen Variable. In `uni`-Datensatz zum Beispiel nach `study`: ``` {r groupby, eval=FALSE} group_by(uni, - study + `study` ) ``` - -Was sehen wir? ] -.pull-right[ +.pull-right-60[ .scrollable[ -``` {r groupby-true, eval=TRUE, echo=FALSE} +``` {r groupby-out, eval=TRUE, echo=FALSE} group_by(uni, study ) @@ -510,6 +576,10 @@ group_by(uni, ] ] +-- + +Was sehen wir? + ??? Antwort: **Nichts!** @@ -520,13 +590,29 @@ Antwort: **Nichts!** -- -```{r groupby2, eval=TRUE} +.pull-left-40[ +```{r groupby2} summarize(group_by(uni, study ), mean(term) #<< ) ``` +] + +-- + +.pull-right-60[ +.center[ +```{r groupby2-out, eval=TRUE, echo=FALSE} +summarize(group_by(uni, + study + ), + mean(term) + ) +``` +] +] -- @@ -536,11 +622,11 @@ Jetzt haben wir für jeden Studienort einen Mittelwert für das Fachsemester (`t **Wichtig**: Wenn Daten gespeichert oder übergeben werden, sollte am Ende die Befehlskette immer mit `ungroup()` enden, um die Datenteilung nicht zu übergeben! ---- +--- -class: inverse, mline, center, middle +class: inverse2, mline, center, middle -# Piping `%>%` +# Piping `%>%` --- @@ -553,7 +639,7 @@ Den *pipe*-Operator in `tidyverse` ist `%>%` und kann einfach per Tastenkürzel -- -Seit R Version 4.0 gibt es den Pipe-Operator auch in RBase, daher diese beiden nicht verwechseln. +Seit R Version 4.0 gibt es den Pipe-Operator auch in RBase (`\>`), daher diese beiden nicht verwechseln. --- @@ -563,42 +649,54 @@ Hier mal ein Beispiel: Das Ziel ist es eine Variable zu erstellen, die den Abitu --- # Beispiel Pipes + Die Schritte, die wir hierbei machen, sind folgende: -- +.pull-left[ 1. Wir geben den Datensatz `uni` weiter. --- - 2. Wir gruppieren den Datensatz nach `city`. --- - 3. Wir berechnen eine neue Variable `abiMean`. --- - 4. Wir heben die Gruppierung wieder auf. +5. (bzw. 0.) Wir überspeichern den alten Datensatz. +] + -- -5./0. Wir überspeichern den alten Datensatz. +.pull-right[ +``` {r pipe} +uni <- uni %>% + group_by(city) %>% + mutate(abiMean = mean(abi, + na.rm = TRUE + ) + ) %>% + ungroup() #<< --- +table(uni$city, + uni$abiMean) +``` -``` {r pipe, eval=TRUE} +.center[ +``` {r pipe-out, eval=TRUE, echo=FALSE} uni <- uni %>% group_by(city) %>% mutate(abiMean = mean(abi, na.rm = TRUE ) ) %>% - ungroup() + ungroup() #<< table(uni$city, uni$abiMean) ``` +] +] --- @@ -607,7 +705,9 @@ Alternativ könnten wir uns dies auch erstmal nur ausgeben lassen. -- -```{r pipe2, eval=TRUE} +.pull-left[ + +```{r pipe2} uni %>% group_by(city) %>% summarize(mean = mean(abi, @@ -615,7 +715,22 @@ uni %>% ) ) ``` +] + +-- +.pull-right[ +.center[ +```{r pipe2-out, eval=TRUE, echo=FALSE} +uni %>% + group_by(city) %>% + summarize(mean = mean(abi, + na.rm = TRUE + ) + ) +``` +] +] --- # Pipes @@ -633,7 +748,23 @@ unterscheiden. -- -```{r pipe3, eval=TRUE} +.pull-left-60[ +```{r pipe3} +uni <- uni %>% + mutate(termg = case_when(term <= 2 ~ "Anfänger:in", + term > 2 & term <= 6 ~ "Erfahrene", + term > 6 ~ "Langzeit" + ) + ) +table(uni$termg) +str(uni$termg) +``` +] + +-- + +.pull-right-40[ +```{r pipe3-out, eval=TRUE, echo=FALSE} uni <- uni %>% mutate(termg = case_when(term <= 2 ~ "Anfänger:in", term > 2 & term <= 6 ~ "Erfahrene", @@ -643,6 +774,7 @@ uni <- uni %>% table(uni$termg) str(uni$termg) ``` +] --- @@ -651,7 +783,8 @@ Etwas komplexer wäre folgende Aufgabe: Wir möchten nicht die Abweichung zum Mi -- -```{r pipe4, eval=TRUE} +.pull-left-40[ +```{r pipe4} uni <- uni %>% group_by(city) %>% mutate(abigm = mean(abi)) %>% @@ -666,15 +799,53 @@ uni[, c("ID", ) ] ``` +] + +-- +.pull-right-60[ +.center[ +```{r pipe4-out, eval=TRUE, echo=FALSE} +uni <- uni %>% + group_by(city) %>% + mutate(abigm = mean(abi)) %>% + mutate(abid = abi - abigm) %>% + ungroup() + +uni[, c("ID", + "abi", + "city", + "abigm", + "abid" + ) + ] +``` +] +] --- # Pipes + +.pull-left-40[ Alternativ könnten wir die Daten auch hierarchisch nach Standort und Studienfach gruppieren und uns dann einfach die unterschiedlichen Mittelwerte mit **summarise()** ausgeben lassen: -- -```{r pipe5, eval=TRUE} +```{r pipe5, eval=FALSE} +mCityStudy <- uni %>% + group_by(city, + study + ) %>% + summarise(mean(abi)) + +mCityStudy +``` +] + + +.pull-right-60[ +.center[ +```{r pipe5-out, eval=TRUE, echo=FALSE} mCityStudy <- uni %>% group_by(city, study @@ -683,6 +854,8 @@ mCityStudy <- uni %>% mCityStudy ``` +] +] --- @@ -720,6 +893,6 @@ Nutzt dazu den Datensatz `pss` (Panem Social Survey). --- -class: inverse, mline, center, middle +class: inverse2, mline, center, middle # Das wars! \ No newline at end of file diff --git a/slides/ggplot-grammar.Rmd b/slides/ggplot-grammar.Rmd index c80d1efd6e9b7028970c1daad9b180f244dc78ec..ab24727f0970163d1cdeb233b12cb85be7fefbeb 100644 --- a/slides/ggplot-grammar.Rmd +++ b/slides/ggplot-grammar.Rmd @@ -1,42 +1,40 @@ --- -title: "Einführung in ggplot2-Grammatik" -subtitle: "Daten bändigen & visualisieren" -author: "B. Philipp Kleer" -date: "11. Oktober 2021" -output: - slidy_presentation: - footer: "CC BY-SA 4.0, B. Philipp Kleer" - widescreen: true - highlight: pygments - theme: readable - css: ./styles/style-slides.css - df_print: paged - mathjax: default - self_contained: false - incremental: false #True dann jedes Bullet einzeln - collapse: true # means the text output will be merged into the R source code block +output: + xaringan::moon_reader: + css: ["./styles/slides.css"] + nature: + seal: false + highlightStyle: github + highlightLines: true + highlightLanguage: ["r"] + countIncrementalSlides: false + ratio: '16:9' + slideNumberFormat: | + <div class="progress-bar-container"> + <div class="progress-bar" style="width: calc(%current% / %total% * 100%);"> + </div> + </div>` + navigation: + scroll: false --- ```{r setup, include=FALSE} library("knitr") library("rmarkdown") library("tidyverse") +library("icons") pss <- readRDS("../datasets/pss.rds") opts_chunk$set(#fig.path = 'pics/s6-', # path for calculated figures fig.align = 'center', # alignment of figure (also possible right, left, default) fig.show = 'hold', # how to show figures: hold -> direct at the end of code chunk; animate: all plots in an animation - fig.width = 6, # figure width - fig.height = 6, # figure height + out.width = "60%", echo = TRUE, # Code is printed eval = FALSE, # Code is NOT evaluated warning = FALSE, # warnings are NOT displayed message = FALSE, # messages are NOT displayed - size = "tiny", # latex-size of code chunks - background = "#E7E7E7", # background color of code chunks comment = "", # no hashtags before output - options(width = 80), results = "markdown", rows.print = 15 ) @@ -52,9 +50,29 @@ htmltools::tagList( ``` -## Starten wir! +class: center, title-slide, middle + +# Einführung in ggplot-Grammatik + +## Daten bändigen & visualisieren + +### B. Philipp Kleer + +### Methodentage 2021 + +### 11. Oktober 2021 + +.social[ +   [`r icons::icon_style(fontawesome("orcid"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://orcid.org/0000-0003-1935-387X)   [`r icons::icon_style(fontawesome("gitlab"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://gitlab.com/bpkleer)   [`r icons::icon_style(fontawesome("university"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://www.uni-giessen.de/faculties/f03/departments/dps/staff/researchers/kleer?set_language=en)   [`r icons::icon_style(fontawesome("researchgate"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://www.researchgate.net/profile/Benedikt_Kleer) +] + +--- + +# Starten wir! Nun tauchen wir in die Welt von **ggplot2** ein. Das Paket ist **das** Grafik-Paket in R. Viele weitere Grafikpakete beruhen auf derselben Grammatik wie **ggplot2**, so dass Kenntnisse dieses Pakets jedem helfen. +-- + Auch hier laden wir zuerst **tidyverse** bzw. installieren es, wenn es noch nicht installiert ist: ``` {r install-tidy} @@ -66,51 +84,137 @@ library("tidyverse") # library("ggplot2") ``` -Anschließend laden wir den Datensatz ```pss``` ins *environment*. +-- + +Anschließend laden wir den Datensatz `pss` ins *environment*. ``` {r uni-load} pss <- readRDS("../datasets/pss.rds") #oder eigener Pfad, wenn nicht in Cloud ``` +--- + +# ggplot + Wir machen **gplot2** ist sehr umfangreich, wir machen heute einen Einstieg in die Grammatik. Da die Grafiken aber als Layer aufgebaut sind, können mithilfe des Verständnisses der Grafikgrammatik auch aufwendigere Grafiken erstellt werden. Einen Überblick, was wir heute machen: +-- + 1. Balkendiagramme und Grundaufbau von ggplot +-- + 2. Histogramme +-- + 3. Scatterplots +-- + 4. Gruppierungen -## Balkendiagramme -Ein Balkendiagramm ist ein Plot einer einzelnen kategorialen Variable. Es gibt zwei Wege eine Grafik mit **ggplot2** zu erstellen: einmal als direkte Ausgabe und einmal als Objekt zu speichern. Vorteil von letzterem ist, dass man dann am gespeicherten Plot Anpassungen vornehmen kann ohne den ganzen vorherigen Code nochmals ausführen lassen zu müssen. +--- + +class: inverse2, mline, center, middle + +# Balkendiagramme + +--- + +# Direkte Ausgabe + +Ein Balkendiagramm ist ein Plot einer einzelnen kategorialen Variable. Mit der Funktion `ggplot()` kann man direkt einen Plot ausgeben. + +.pull-left-40[ +``` {r ggplot1} +# direkter Output +ggplot(data = pss, + mapping = aes(x = edu) + ) +``` +] + +-- -``` {r ggplot1, eval=TRUE} +.pull-right-60[ +``` {r ggplot1-out, eval=TRUE, echo=FALSE} # direkter Output ggplot(data = pss, mapping = aes(x = edu) ) ``` +] +--- + +# Objekte speichern +Alternativ (und meist besser) ist es Grafiken in Objekte zu speichern: + +-- + +.pull-left-40[ +``` {r ggplot2} +# oder speichern als Objekt +mfPlot <- ggplot(data = pss, + mapping = aes(x = edu) + ) +mfPlot +``` +] -``` {r ggplot2, eval=TRUE} +-- + +.pull-right-60[ +``` {r ggplot2-out, eval=TRUE, echo=FALSE} # oder speichern als Objekt mfPlot <- ggplot(data = pss, mapping = aes(x = edu) ) mfPlot ``` +] + +-- Aber warum sind beide Plots leer? -## ggplot() verstehen -Wir haben nur das Grundgerüst mit der Funktion **ggplot()** übergeben. Diese Funktion beinhaltet immer die Daten (in ```data```) und die Struktur des Plots (```mapping```). Ein Balkendiagramm ist eine univariate Darstellung und deshalb übergeben wir nur eine Variable (hier ```edu```). +??? + +nur die Struktur wurde angegeben, nicht was geplottet werden soll. + +--- + +# ggplot() verstehen -Um nun ein Balkendiagramm aus dem Plot zu machen, benötigen wir einen weiteren Layer, der eben ein Balkendiagramm ausgibt. Dies ist der Zusatz ```geom_bar()```. +Wir haben nur das Grundgerüst mit der Funktion `ggplot()` übergeben. Diese Funktion beinhaltet immer die Daten (in `data`) und die Struktur des Plots (`mapping`). Ein Balkendiagramm ist eine univariate Darstellung und deshalb übergeben wir nur eine Variable (hier `edu`). -``` {r bar, eval=TRUE} +-- + +Um nun ein Balkendiagramm aus dem Plot zu machen, benötigen wir einen weiteren Layer, der eben ein Balkendiagramm ausgibt. Dies ist der Zusatz `geom_bar()`. + +-- + +.pull-left-40[ +``` {r bar} +ggplot(data = pss, + mapping = aes(x = edu) + ) + + geom_bar() #<< + +# oder: +# mfPlot + +# geom_bar() +``` +] + +-- + +.pull-right-60[ +.center[ +``` {r bar-out, eval=TRUE, echo=FALSE, out.width="50%"} ggplot(data = pss, mapping = aes(x = edu) ) + @@ -120,9 +224,31 @@ ggplot(data = pss, # mfPlot + # geom_bar() ``` +] +] + +--- + +# Prozente statt Häufigkeiten + +Gerade haben wir uns Häufigkeiten ausgeben lassen. Manchmal möchte man lieber Prozente: +.pull-left-40[ +``` {r bar2} +ggplot(data = pss, + mapping = aes(x = edu, + y = ..prop.., #<< + group = 1 #<< + ) + ) + + geom_bar() +``` +] + +-- -Jetzt haben wir ein Balkendiagramm mit Häufigkeiten. Manchmal möchte man lieber Prozente: -``` {r bar2, eval=TRUE} +.pull-right-60[ +.center[ +``` {r bar2-out, eval=TRUE, echo=FALSE} ggplot(data = pss, mapping = aes(x = edu, y = ..prop.., #Einstellung Prozente @@ -131,18 +257,69 @@ ggplot(data = pss, ) + geom_bar() ``` +] +] + +??? + +Hinweise zum Code: + +1. y: Einstellung Prozente + +2. group: Einstellung nur eine Gruppe, sonst wäre jeder Balken 100 % + +--- + +# Nun wirklich Balken Alternativ können wir das Diagramm auch zu einem tatsächlichen Balkendiagramm machen und die Säulen loswerden: -``` {r bar3, eval=TRUE} + +-- + +.pull-left-40[ +``` {r bar3} +mfPlot + + geom_bar() + + coord_flip() #<< +``` +] + +-- + +.pull-right-60[ +.center[ +``` {r bar3-out, eval=TRUE, echo=FALSE} mfPlot + geom_bar() + coord_flip() ``` +] +] + +--- + +# Bringen wir Farbe ins Spiel! +In der Regel wollen wir Grafiken ansprechend gestalten, dafür gibt es verschiedene Argumente in `ggplot()`. Die zwei wichtigsten sind `color` und `fill`. Probieren wir es einfach mal der Reihe nach aus. Wir wollen, dass die Balken jetzt jeweils eine andere Farbe haben. + +-- + +.pull-left-40[ +``` {r bar4, eval=FALSE} +eduPlot <- ggplot(pss, + aes(edu, + color = edu #<< + ) + ) + + geom_bar() +eduPlot +``` +] -## Bringen wir Farbe ins Spiel! -In der Regel wollen wir Grafiken ansprechend gestalten, dafür gibt es verschiedene Argumente in **ggplot()**. Die zwei wichtigsten sind ```color``` und ```fill```. Probieren wir es einfach mal der Reihe nach aus. Wir wollen, dass die Balken jetzt jeweils eine andere Farbe haben. +-- -``` {r bar4, eval=TRUE} +.pull-right-60[ +.center[ +``` {r bar4-out, eval=TRUE, echo=FALSE} eduPlot <- ggplot(pss, aes(edu, color = edu @@ -151,10 +328,31 @@ eduPlot <- ggplot(pss, geom_bar() eduPlot ``` +] +] + +--- + +# Coloring +`color` macht also hier nur die Randlinie, nicht aber die Fläche der Balken farbig. Mit `fill` können wir das beheben. + +-- + +.pull-left-40[ +```{r bar5} +eduPlot <- ggplot(pss, + aes(edu, + fill = edu #<< + ) + ) + + geom_bar() +eduPlot -```color``` macht also hier nur die Randlinie, nicht aber die Fläche der Balken farbig. Mit ```fill``` können wir das beheben. +``` +] -```{r bar5, eval=TRUE} +.pull-right-60[ +```{r bar5-out, eval=TRUE, echo=FALSE} eduPlot <- ggplot(pss, aes(edu, fill = edu @@ -164,9 +362,40 @@ eduPlot <- ggplot(pss, eduPlot ``` +] + +--- + +# Eigene Farbpaletten + +Wenn man eigene Farben benennen will, kann man dies in der Unterfunktion `geom_bar()`. Es empfiehlt sich vorab einen Farbvektor zu definieren: + +-- + +.pull-left-40[ +``` {r bar6} +cntryCol = c("steelblue", + "seagreen", + "red4", + "orange", + "pink", + "lightgray" + ) + +ggplot(pss, + aes(x = edu, + fill = edu + ) + ) + + geom_bar(fill = cntryCol) #<< +``` +] -Wenn man eigene Farben benennen will, kann man dies in der Unterfunktion ```geom_bar()```. Es empfiehlt sich vorab einen Farbvektor zu definieren: -``` {r bar6, eval=TRUE} +-- + +.pull-right-60[ +.center[ +``` {r bar6-out, eval=TRUE, echo=FALSE} cntryCol = c("steelblue", "seagreen", "red4", @@ -182,10 +411,47 @@ ggplot(pss, ) + geom_bar(fill = cntryCol) ``` +] +] + +--- + +# Eigene Farbpaletten + +Neben dieser Möglichkeit können auch Farbpaletten genutzt werden, die eine beliebige Anzahl an Farben inkludieren. **Wichtig:** Sind in der Farbpalette weniger Farben definiert, gibt es einen Fehler. Es müssen mindestens so viele Farben vorhanden sein, wie die Variable Kategorien hat. Hierzu fügt man einen weiteren Layer `scale_fill_manual()` bzw. `scale_fill_color()` hinzu. + +-- + +.pull-left-40[ +``` {r bar7} +# a colourblind-friendly palettes +cbp1 <- c("#999999", + "#E69F00", + "#56B4E9", + "#009E73", + "#F0E442", + "#0072B2", + "#D55E00", + "#CC79A7" + ) + +eduPlotCb<- ggplot(pss, + aes(edu, + fill = edu + ) + ) + + geom_bar() + + scale_fill_manual(values = cbp1) #<< + +eduPlotCb +``` +] -Neben dieser Möglichkeit können auch Farbpaletten genutzt werden, die eine beliebige Anzahl an Farben inkludieren. **Wichtig:** Sind in der Farbpalette weniger Farben definiert, gibt es einen Fehler. Es müssen mindestens so viele Farben vorhanden sein, wie die Variable Kategorien hat. Hierzu fügt man einen weiteren Layer ```scale_fill_manual()``` bzw. ```scale_fill_color()``` hinzu. +-- -``` {r bar7, eval=TRUE} +.pull-right-60[ +.center[ +``` {r bar7-out, eval=TRUE, echo=FALSE} # a colourblind-friendly palettes cbp1 <- c("#999999", "#E69F00", @@ -207,9 +473,34 @@ eduPlotCb<- ggplot(pss, eduPlotCb ``` +] +] + +--- + +# Geld sparen + +Für einen kostensparenden Druck kann man auch einfach direkt `scale_fill_gray()` nutzen: + +-- + +.pull-left-40[ +``` {r bar8} +ggplot(pss, + aes(district, + fill = district + ) + ) + + geom_bar() + + scale_fill_grey() +``` +] + +-- -Für einen kostensparenden Druck kann man auch einfach direkt ```scale_fill_gray()``` nutzen: -``` {r bar8, eval=TRUE} +.pull-right-60[ +.center[ +``` {r bar8-out, eval=TRUE, echo=FALSE} ggplot(pss, aes(district, fill = district @@ -218,47 +509,135 @@ ggplot(pss, geom_bar() + scale_fill_grey() ``` +] +] + +-- + Alternativ kann man auch verschiedene vorgefertigte Farbpaletten nutzen. Dazu muss man oftmals das entsprechenden Paket laden und dann gibt es eine dazugehörige Funktion, die als zusätzlicher Layer festgelegt wird. -Ein solche Paket ist zum Beispiel ```RColorBrewer```: -``` {r brewer, eval=TRUE} +--- + +# RColorBrewer + +Ein solche Paket ist zum Beispiel `RColorBrewer`: + +-- + +``` {r brewer} # if(!require("RColorBrewer")) install.packages("RColorBrewer") library("RColorBrewer") display.brewer.all() ``` +-- + Die einzelnen Farbpaletten können, dann wie folgt hinzugefügt werden: -``` {r brewer2, eval=TRUE} +``` {r brewer2} +eduPlot + + scale_fill_brewer(palette = "Dark2") +``` + +--- + +# RColorBrewer + +.center[ +``` {r brewer2-out, eval=TRUE, echo=FALSE, out.width="50%"} +library("RColorBrewer") + eduPlot + scale_fill_brewer(palette = "Dark2") ``` +] + +--- +# Layout: Achsen anpassen -## Achsen anpassen Als nächsten Schritt passen wir die Achsen an, da die Standardeinstellungen dafür meistens nicht schön sind. Hierfür gibt es folgende Funktionen: -- ```coord_cartesian()``` (Achsengrenzen festlegen) -- ```scale_x_continuous()```/```scale_y_continuous()``` (für numerische Vektoren) -- ```scale_x_discrete()```/```scale_y_discrete()``` (für Faktoren oder Character-Variablen) +-- + +- `coord_cartesian()` (Achsengrenzen festlegen) + +-- + +- `scale_x_continuous()`/`scale_y_continuous()` (für numerische Vektoren) + +-- + +- `scale_x_discrete()`/`scale_y_discrete()` (für Faktoren oder Character-Variablen) + +-- Was ist mit unseren Achsen? -``` {r axes, eval=TRUE} + +--- + + # Layout: y-Achse anpassen + +.pull-left[ +``` {r axes} +eduPlot2 <- eduPlot + + coord_cartesian(ylim = c(0, + 1500 + ) + ) + + scale_y_continuous(breaks = seq(0, + 1500, + 100 + ) + ) +eduPlot2 +``` +] + +-- + +.pull-right[ +``` {r axes-out, eval=TRUE, echo=FALSE, out.width="75%" } eduPlot2 <- eduPlot + coord_cartesian(ylim = c(0, 1500 ) ) + - scale_y_continuous(breaks = seq(0, - 1500, - 100 - ) + scale_y_continuous(breaks = seq(0, + 1500, + 100 + ) ) eduPlot2 ``` +] + +--- + +# Layout: x-Achse anpassen + +Die x-Achse ist kategoriell und daher macht eine metrische Aufteilung keinen Sinn. Mit `scale_x_discrete()` können wir aber die limits festsetzen: + +-- + +.pull-left[ +``` {r axes2} +eduPlot3 <- eduPlot2 + + scale_x_discrete(limits = c("ES-ISCED I", + "ES-ISCED II", + "ES-ISCED III", + "ES-ISCED IV", + "ES-ISCED V" + ) + ) + +eduPlot3 +``` +] -Die x-Achse ist kategoriell und daher macht eine metrische Aufteilung keinen Sinn. Mit ```scale_x_discrete()``` können wir aber die limits festsetzen: -``` {r axes2, eval=TRUE} +.pull-right[ +.center[ +``` {r axes2-out, eval=TRUE, echo=FALSE, out.width="75%"} eduPlot3 <- eduPlot2 + scale_x_discrete(limits = c("ES-ISCED I", "ES-ISCED II", @@ -270,18 +649,45 @@ eduPlot3 <- eduPlot2 + eduPlot3 ``` +] +] + +-- Was haben wir ausgelassen? -## Titel, Caption, Legende und weitere Infos -Wir können eine Reihe an Achsenbeschriftungen hinzufügen. Dies geschieht am einfachsten über die Funktion ```labs()```. Darin gibt es folgende Unterargumente: +--- + +# Layout: Titel, Caption, Legende und weitere Infos + +Wir können eine Reihe an Achsenbeschriftungen hinzufügen. Dies geschieht am einfachsten über die Funktion `labs()`. Darin gibt es folgende Unterargumente: + +-- + +- `xlab`: x-Achsentitel +- `ylab`: y-Achsentitel +- `title`: Titel +- `caption`: Fußnote/Caption + +-- + +``` {r text1} +eduPlot4 <- eduPlot3 + + labs(x = "Education level", + y = "Frequency", + title = "my first shot at ggplot2", + caption = "Data: Panem Social Survey." + ) + +eduPlot4 +``` + +--- -- ```xlab```: x-Achsentitel -- ```ylab```: y-Achsentitel -- ```title```: Titel -- ```caption```: Fußnote/Caption +# Layout: Titel, Caption, Legende und weitere Infos -``` {r text1, eval=TRUE} +.center[ +``` {r text1-out, eval=TRUE, echo=FALSE, out.width="50%"} eduPlot4 <- eduPlot3 + labs(x = "Education level", y = "Frequency", @@ -291,10 +697,39 @@ eduPlot4 <- eduPlot3 + eduPlot4 ``` +] + +Das schaut schon soweit gut aus, jetzt wollen wir nur noch die Legende anpassen, so dass auch dort ein angepasster Titel steht. + +--- + +# Layout: und wieder coloring + +Dies machen wir in der Funktion `scale_fill_manual()`, die wir zuvor schon genutzt haben. Wir überschreiben sozusagen die Angaben: + +-- +.pull-left[ +``` {r legend1} +eduPlot5 <- eduPlot4 + + scale_fill_manual(values = cbp1, + name = "Education category", + labels = c("sehr niedrig", + "niedrig", + "mittel", + "hoch", + "sehr hoch" + ) + ) + +eduPlot5 +``` +] -Das schaut schon soweit gut aus, jetzt wollen wir nur noch die Legende anpassen, so dass auch dort ein angepasster Titel steht. Dies machen wir in der Funktion ```scale_fill_manual()```, die wir zuvor schon genutzt haben. Wir überschreiben sozusagen die Angaben: +-- -``` {r legend1, eval=TRUE} +.pull-right[ +.center[ +``` {r legend1-out, eval=TRUE, echo=FALSE} eduPlot5 <- eduPlot4 + scale_fill_manual(values = cbp1, name = "Education category", @@ -308,24 +743,62 @@ eduPlot5 <- eduPlot4 + eduPlot5 ``` +] +] -Über die Funktion ```theme()``` können viele Feineinstellungen vorgenommen werden. Diese können wir nicht im einzelnen hier besprechen, aber es kann wirklich jedes Detail eingestellt werden. Mehr dazu machen die Personen, die am Nachmittag nochmal vertieft mit **ggplot()** arbeiten. +-- +Über die Funktion `theme()` können viele Feineinstellungen vorgenommen werden. Diese können wir nicht im einzelnen hier besprechen, aber es kann wirklich jedes Detail eingestellt werden. Mehr dazu machen die Personen, die am Nachmittag nochmal vertieft mit `ggplot` arbeiten. -## Histogramme -Gehen wir nun über zu Histogrammen. Diese nutzen wir für metrische Variablen. Hierfür nutzen wir den Layer ```geom_histogram()```. +--- -``` {r hist1, eval=TRUE} -agePlot <- ggplot(pss, - aes(agea) - ) + - geom_histogram() +class: inverse2, mline, center, middle + +# Histogramme + +--- + +# Zum nächsten ... + +Gehen wir nun über zu Histogrammen. Diese nutzen wir für metrische Variablen. Hierfür nutzen wir den Layer `geom_histogram()`. + +-- + +.pull-left-40[ +``` {r hist1} +agePlot <- ggplot(pss, + aes(agea) + ) + + geom_histogram() + +agePlot +``` +] + +-- + +.pull-right-60[ +.center[ +``` {r hist1-out, eval=TRUE, echo=FALSE} +agePlot <- ggplot(pss, + aes(agea) + ) + + geom_histogram() agePlot ``` +] +] + +-- + +Auch hier können wir ganz einfach Anpassungen von oben übernehmen und den Plot schöner gestalten. -Auch hier können wir ganz einfach Anpassungen von oben übernehmen und den Plot schöner gestalten: -``` {r hist2, eval=TRUE} +--- + +# Fine-Tuning Histogram +.pull-left-40[ +``` {r hist2} agePlot2 <- agePlot + geom_histogram(color = "lightgray", fill = "gray" @@ -337,9 +810,61 @@ agePlot2 <- agePlot + agePlot2 ``` +] + +-- + +.pull-right-60[ +.center[ +``` {r hist2-out, eval=TRUE, echo=FALSE, out.wdith="75%"} +agePlot2 <- agePlot + + geom_histogram(color = "lightgray", + fill = "gray" + ) + + labs(x = "Age in years", + y = "Frequency", + title = "Histogram of Age (PSS)" + ) -Oftmals fügen wir auch die Dichte hinzu, um einfacher beurteilen zu können, ob annähernd eine Normalverteilung vorliegt. Hierzu bestimmen wir, dass die y-Achse die Dichte anzeig (```y = ..density..```) und fügen ```geom_density()``` hinzu. Mit ```alpha``` stellen wir Durchsichtigkeit ein und mit ```fill``` die Farbe der Fläche. -``` {r hist3, eval=TRUE} +agePlot2 +``` +] +] + +--- + +# Dichte + +Oftmals fügen wir auch die Dichte hinzu, um einfacher beurteilen zu können, ob annähernd eine Normalverteilung vorliegt. Hierzu bestimmen wir, dass die y-Achse die Dichte anzeig (`y = ..density..`) und fügen `geom_density()` hinzu. Mit `alpha` stellen wir Durchsichtigkeit ein und mit `fill` die Farbe der Fläche. + +-- + +.pull-left-40[ +``` {r hist3} +ageDensPlot <- ggplot(pss, + aes(agea) + ) + + geom_histogram(aes(y = ..density..), #<< + color = "lightgray", + fill = "gray" + ) + + geom_density(alpha = 0.2, #<< + fill = "lightblue" #<< + ) + #<< + labs(x = "Age in years", + y = "Density", + title = "Histogram of Age (PSS)" + ) + +ageDensPlot +``` +] + +-- + +.pull-right-60[ +.center[ +``` {r hist3-out, eval=TRUE, echo=FALSE} ageDensPlot <- ggplot(pss, aes(agea) ) + @@ -357,9 +882,43 @@ ageDensPlot <- ggplot(pss, ageDensPlot ``` +] +] + +--- + +# Anpassungen: Histogramme + +`geom_histogram()` wählt automatisch die Breite der `bin` (Säulen), was irreführend sein kann. Hier könnten wir zum Beispiel so einstellen, dass wir so viele `bins` erhalten, wie es auch tatsächliche Alterskategorien gibt. Man kann einfach die Breite der `bins` mit `binwidth()` festlegen. Hier wählen wir den realen Abstand zwischen zwei Einträgen in dem Vektor/Variable (hier 1). + +-- + +.pull-left[ +```{r hist5} +ggplot(pss, + aes(agea) + ) + + geom_histogram(aes(y = ..density..), + color = "lightgray", + fill = "gray", + binwidth = 1 #<< + ) + + geom_density(alpha = 0.2, + fill = "lightblue" + ) + + labs(x = "Age in years", + y = "Density", + title = "Histogram of Age (PSS)" + ) + +``` +] -```geom_histogram()``` wählt automatisch die Breite der ```bin``` (Säulen), was irreführend sein kann. Hier könnten wir zum Beispiel so einstellen, dass wir so viele ```bins``` erhalten, wie es auch tatsächliche Alterskategorien gibt. Man kann einfach die Breite der ```bins``` mit ```binwidth()``` festlegen. Hier wählen wir den realen Abstand zwischen zwei Einträgen in dem Vektor/Variable (hier 1). -```{r hist5, eval=TRUE} +-- + +.pull-right[ +.center[ +```{r hist5-out, eval=TRUE, echo=FALSE, out.width="75%"} ggplot(pss, aes(agea) ) + @@ -377,29 +936,93 @@ ggplot(pss, ) ``` +] +] + +--- + +class: inverse2, mline, center, middle + +# Scatterplots + +--- + +# Scatterplots + +Oftmals wollen wir zwei Variablen darstellen und ihren Zusammenhang. So könnten wir uns zum Beispiel für den Zusammenhang zwischen der Zufriedenheit mit dem demkoratischen System (`stfdem`) und der Zufriedenheit mit der ökonomischen Entwicklung (`stfeco`) anschauen. Dies sind beides Variablen auf pseudometrischen Niveau gemessen, in R aber als numerische Variablen hinterlegt. + +-- + +Um Scatterplots darzustellen nutzen wir `geom_point()`. Wir geben jetzt zwei Variablen in `aes()` an. Einmal die x-Achsenvariable und dann die y-Achsenvariable. + +```{r scatter1} +scatter1 <- ggplot(pss, + aes(stfeco, + stfdem + ) + ) + + geom_point() #<< + +scatter1 +``` -## Scatterplots -Oftmals wollen wir zwei Variablen darstellen und ihren Zusammenhang. So könnten wir uns zum Beispiel für den Zusammenhang zwischen der Zufriedenheit mit dem demkoratischen System (```stfdem```) und der Zufriedenheit mit der ökonomischen Entwicklung (```stfeco```) anschauen. Dies sind beides Variablen auf pseudometrischen Niveau gemessen, in R aber als numerische Variablen hinterlegt. +--- -Um Scatterplots darzustellen nutzen wir ```geom_point()```. Wir geben jetzt zwei Variablen in ```aes()``` an. Einmal die x-Achsenvariable und dann die y-Achsenvariable. +# Scatterplots -```{r scatter1, eval=TRUE} +.center[ +```{r scatter1-out, eval=TRUE, echo=FALSE, out.width="40%"} scatter1 <- ggplot(pss, aes(stfeco, stfdem ) ) + - geom_point() + geom_point() scatter1 ``` +] + +-- + +Warum sehen wir nun so wenige Punkte, obwohl der Datensatz **5000** Fälle hat? -Warum sehen wir nun so wenige Punkte, obwohl der Datensatz $5000$ Fälle hat? +??? -## Scatterplot pseudometrische Variablen -Um pseudo-metrische Variablen korrekt darzustellen, benötigen wir ```geom_jitter()```, da die Datenpunkte sonst übereinanderlappen. **Wichtig:** Das jittern sollte angegeben werden, damit kein falscher Dateneindruck entsteht! +Überlappende Datenpunkte $\rightarrow$ Jittern! -``` {r scatter2, eval=TRUE} +--- + +# Scatterplot: pseudometrische Variablen + +Um pseudo-metrische Variablen korrekt darzustellen, benötigen wir `geom_jitter()`, da die Datenpunkte sonst übereinanderlappen. **Wichtig:** Das jittern sollte angegeben werden, damit kein falscher Dateneindruck entsteht! + +-- + +.pull-left[ +``` {r scatter2} +scatter2 <- ggplot(pss, + aes(stfeco, + stfdem + ) + ) + + geom_jitter(width = 0.3, #<< + height = 0.3 #<< + ) + #<< + labs(x = "Satisfaction with Economy", + y = "Satisfaction with Democracy", + caption = "Data jittered." + ) + +scatter2 +``` +] + +-- + +.pull-right[ +.center[ +``` {r scatter2-out, eval=TRUE, echo=FALSE, out.width="60%"} scatter2 <- ggplot(pss, aes(stfeco, stfdem @@ -415,10 +1038,35 @@ scatter2 <- ggplot(pss, scatter2 ``` +] +] + +--- + +# Anpassungen: Scatteprlots + +Weitergehend können wir mit `alpha` in `geom_jitter()` (oder auch in `geom_point()`) einen besseren Eindruck verstärken. Datenkombinationen die weniger oft vorkommen erscheinen nciht so kräftig: + +-- + +.pull-left-40[ +``` {r scatter3} +ggplot(pss, + aes(stfeco, + stfdem + ) + ) + + geom_jitter(alpha = .2, #<< + col = "blue" + ) +``` +] -Weitergehend können wir mit ```alpha``` in ```geom_jitter()``` (oder auch in ```geom_point()```) einen besseren Eindruck verstärken. Datenkombinationen die weniger oft vorkommen erscheinen nciht so kräftig: +-- -``` {r scatter3, eval=TRUE} +.pull-right-60[ +.center[ +``` {r scatter3-out, eval=TRUE, echo=FALSE} ggplot(pss, aes(stfeco, stfdem @@ -428,20 +1076,53 @@ ggplot(pss, col = "blue" ) ``` +] +] -Als weitere Argumente können sowohl in ```geom_jitter()``` als auch in ```geom_point()``` mit ```shape``` das Erscheinungsbild geändert werden. Das Cheat Sheet findet man [hier](https://github.com/rstudio/cheatsheets/raw/master/data-visualization.pdf). +--- + +# Anpassungen: Scatterplots +Als weitere Argumente können sowohl in `geom_jitter()` als auch in `geom_point()` mit `shape` das Erscheinungsbild geändert werden. Das Cheat Sheet findet man [hier](https://github.com/rstudio/cheatsheets/raw/master/data-visualization.pdf). + +--- + +# Gruppierungen +Oftmals möchte man eine Variable oder den Zusammenhang zweier Variablen getrennt nach einer kategoriellen Variable einblicken. Dazu gibt es in `ggplot` verschiedene Möglichkeiten. Ein paar davon werde ich kurz vorstellen: -## Gruppierungen -Oftmals möchte man eine Variable oder den Zusammenhang zweier Variablen getrennt nach einer kategoriellen Variable einblicken. Dazu gibt es in **ggplot2** verschiedene Möglichkeiten. Ein paar davon werde ich kurz vorstellen: +-- -- Balkendiagramm: ```fill```, ```color``` und ```facets``` +- Balkendiagramm: `fill` / `color` und `facets` + +-- + +- Scatterplots: `shape` und `facets` + +--- + +# Gruppierungen: Balkendiagramme + +Wir möchten gerne wissen, wie viele Befragte in den fünf ausgewählten Distrikten weiblich bzw. männlich sind. Dies können wir einfach erhalten, in dem wir in `ggplot()` mit `fill` eine Aufteilung nach Geschlecht hinzufügen. Das funktioniert auch analog mit `color` (nur ist es etwas unübersichtlich bei Balkendiagrammen). + +-- + +.pull-left-40[ +```{r bargroup} +barg <- ggplot(pss, + aes(district, + fill = gndr #<< + ) + ) + + geom_bar() -- Scatterplots: ```shape``` und ```facets``` +barg +``` +] -### Balkendiagramme -Wir möchten gerne wissen, wie viele Befragte in den fünf ausgewählten Distrikten weiblich bzw. männlich sind. Dies können wir einfach erhalten, in dem wir in ```ggplot()``` mit ```fill``` eine Aufteilung nach Geschlecht hinzufügen. Das funktioniert auch analog mit ```color``` (nur ist es etwas unübersichtlich bei Balkendiagrammen). +-- -```{r bargroup, eval=TRUE} +.pull-right-60[ +.center[ +```{r bargroup-out, eval=TRUE, echo=FALSE} barg <- ggplot(pss, aes(district, fill = gndr @@ -451,9 +1132,35 @@ barg <- ggplot(pss, barg ``` +] +] + +--- -Will man dies nun etwas übersichtlicher nebeneinander haben, geschieht dies einfach mit dem Argument ```position``` in ```geom_bar()```: -```{r barg2, eval=TRUE} +# position_dodge() + +Will man dies nun etwas übersichtlicher nebeneinander haben, geschieht dies einfach mit dem Argument `position` in `geom_bar()`: + +-- + +.pull-left-40[ +```{r barg2} +barg2 <- ggplot(pss, + aes(district, + fill = gndr + ) + ) + + geom_bar(position = position_dodge()) #<< + +barg2 +``` +] + +-- + +.pull-right-60[ +.center[ +```{r barg2-out, eval=TRUE, echo=FALSE} barg2 <- ggplot(pss, aes(district, fill = gndr @@ -463,10 +1170,35 @@ barg2 <- ggplot(pss, barg2 ``` +] +] + + +--- + +# Alternativ: facets -Alternativ kann man ```facets``` innerhalb eines **ggplots** erstellen. Dies geht entweder mit ```facet_wrap()``` oder mit ```facet_grid```: +Alternativ kann man `facets` innerhalb eines **ggplots** erstellen. Dies geht entweder mit `facet_wrap()` oder mit `facet_grid`: -``` {r facet1, eval=TRUE} +.pull-left-40[ +``` {r facet1} +barg2 <- ggplot(pss, + aes(district, + fill = gndr + ) + ) + + geom_bar() + + facet_wrap(vars(gndr)) #<< + +barg2 +``` +] + +-- + +.pull-right-60[ +.center[ +``` {r facet1-out, eval=TRUE, echo=FALSE} barg2 <- ggplot(pss, aes(district, fill = gndr @@ -477,13 +1209,38 @@ barg2 <- ggplot(pss, barg2 ``` +] +] -### Scatterplots -Bei Scatteprlots kann man ebenfalls mit ```shape``` arbeiten oder auch direkt ```facet_wrap```/```facet_grid``` benutzen. +??? -Wir möchten nun das Scatterplot von vorher nach Distrikten ausgeben lassen. Dazu können wir zuerst den Distrikten verschiedene Formen zuordnen. Dazu geben wir innerhalb von ```aes()``` einfach an, dass die Form (```shape```) in Abhängigkeit der Variable ```district``` ausgegeben werden soll. +facet_grid() kommt später noch -``` {r scatter-shapes, eval=TRUE} +--- + +# Gruppierungen: Scatterplots + +Bei Scatteprlots kann man auch direkt `facet_wrap`/`facet_grid` benutzen. Wir möchten nun das Scatterplot von vorher nach Distrikten ausgeben lassen. Dazu können wir zuerst den Distrikten verschiedene Formen zuordnen. Dazu geben wir innerhalb von `aes()` einfach an, dass die Form (`shape`) in Abhängigkeit der Variable `district` ausgegeben werden soll. + +.pull-left-40[ +``` {r scatter-shapes} +ggplot(pss, + aes(stfeco, + stfdem, + shape = district #<< + ) + ) + + geom_jitter(alpha = .2, + col = "blue" + ) +``` +] + +-- + +.pull-right-60[ +.center[ +``` {r scatter-shapes-out, eval=TRUE, echo=FALSE, out.width="55%"} ggplot(pss, aes(stfeco, stfdem, @@ -494,10 +1251,23 @@ ggplot(pss, col = "blue" ) ``` +] +] -Das ist jetzt noch nicht ganz so übersichtlich. Jetzt könnte man noch zusätzlich die Farbe in Abhängigkeit des Distrikts ändern. Dazu nutzt man das ```color```-Argument in ```aes()``` und entfernt es aus ```geom_jitter()```. Lässt man das ```color```-Argument in ```geom_jitter()``` bestehen, wird dies als zuletzt definierte Einstellung verwendet und alles bleibt blau. +-- -``` {r scatter-color, eval=TRUE} +Das ist jetzt noch nicht ganz so übersichtlich. + +--- + +# Anpassungen bei Gruppierungen + +Jetzt könnte man noch zusätzlich die Farbe in Abhängigkeit des Distrikts ändern. Dazu nutzt man das `color`-Argument in `aes()` und entfernt es aus `geom_jitter()`. Lässt man das `color`-Argument in `geom_jitter()` bestehen, wird dies als zuletzt definierte Einstellung verwendet und alles bleibt blau. + +-- + +.pull-left-40[ +``` {r scatter-color} ggplot(pss, aes(stfeco, stfdem, @@ -507,12 +1277,61 @@ ggplot(pss, ) + geom_jitter(alpha = .2) ``` +] + +-- + +.pull-right-60[ +.center[ +``` {r scatter-color-out, eval=TRUE, echo=FALSE} +ggplot(pss, + aes(stfeco, + stfdem, + shape = district, + color = district + ) + ) + + geom_jitter(alpha = .2) +``` +] +] + +--- -Die Abbildung ist zwar jetzt schön bunt, aber so richtig einen guten Überblick gibt auch diese Darstellung noch nicht. Um das zu erreichen, nutzt man ```facets```. Der Unterschied zwischen den zwei möglichen ```facets``` ist folgender: Bei ```facet_wrap()``` wird aus einer eigentlich eindimensionalen Darstellung eine zweidimensionalen Darstellung gemacht und dementsprechend mehrere Abbildungen erstellt. Wenn man zum Beispiel eine kategoriale Variable hat, nach der man die Darstellung splitten möchte, ist ```facet_wrap()``` die richtige Wahl. Mit den Argumenten ```nrow``` und ```ncol``` kann man dazu die Anzahl der Zeilen und Spalten festlegen. -```facet_grid()``` dagegen ist zweidimensional. Hier wird ein Grafikpanel anhand von zwei Variablen erstellt, dass alle Kombinationen (auch die ohne Fälle) anzeigt. Dazu wird die Reihenfolge der Variablen auch wie in einer Formel angegeben, erst pro Zeile und dann pro Spalte. +# Bisheriges Ergebnis + +Die Abbildung ist zwar jetzt schön bunt, aber so richtig einen guten Überblick gibt auch diese Darstellung noch nicht. Um das zu erreichen, nutzt man `facets`. Der Unterschied zwischen den zwei möglichen `facets` ist folgender: + +-- + +- Bei `facet_wrap()` wird aus einer eigentlich eindimensionalen Darstellung eine zweidimensionalen Darstellung gemacht und dementsprechend mehrere Abbildungen erstellt. Wenn man zum Beispiel eine kategoriale Variable hat, nach der man die Darstellung splitten möchte, ist `facet_wrap()` die richtige Wahl. Mit den Argumenten `nrow` und `ncol` kann man dazu die Anzahl der Zeilen und Spalten festlegen. + +-- + +- `facet_grid()` dagegen ist zweidimensional. Hier wird ein Grafikpanel anhand von zwei Variablen erstellt, dass alle Kombinationen (auch die ohne Fälle) anzeigt. Dazu wird die Reihenfolge der Variablen auch wie in einer Formel angegeben, erst pro Zeile und dann pro Spalte. + +--- +# facet_wrap() + +.pull-left-40[ +``` {r scatter-facetwrap} +ggplot(pss, + aes(stfeco, + stfdem, + shape = district, + color = district + ) + ) + + geom_jitter(alpha = .2) + + facet_wrap(vars(district)) +``` +] +-- -``` {r scatter-facetwrap, eval=TRUE} +.pull-right-60[ +.center[ +``` {r scatter-facetwrap-out, eval=TRUE, echo=FALSE} ggplot(pss, aes(stfeco, stfdem, @@ -523,9 +1342,19 @@ ggplot(pss, geom_jitter(alpha = .2) + facet_wrap(vars(district)) ``` +] +] + +--- + +# facet_grid() -Wie oben beschrieben wird in ```facet_grid()``` ein tatsächlicher zweidimensionaler Plot erstellt. Dazu geben wir in der Funktion selbst an, über welche (kategorialen) Variablen der Plot geteilt werden soll. Die erste Variable ist die Trennung über Zeilen und die zweite Variable die Trennung über Spalten. Im Beispiel trennen wir in Zeilen nach Distrikt und in Spalten nach Geschlecht: -``` {r scatter-facetgrid, eval=TRUE} +Wie oben beschrieben wird in `facet_grid()` ein tatsächlicher zweidimensionaler Plot erstellt. Dazu geben wir in der Funktion selbst an, über welche (kategorialen) Variablen der Plot geteilt werden soll. Die erste Variable ist die Trennung über Zeilen und die zweite Variable die Trennung über Spalten. Im Beispiel trennen wir in Zeilen nach Distrikt und in Spalten nach Geschlecht: + +-- + +.pull-left-40[ +``` {r scatter-facetgrid} ggplot(pss, aes(stfeco, stfdem, @@ -536,20 +1365,61 @@ ggplot(pss, geom_jitter(alpha = .2) + facet_grid(district ~ gndr) ``` +] -## Was es noch zu entdecken gibt in ```ggplot2```? +-- + +.pull-right-60[ +.center[ +``` {r scatter-facetgrid-out, eval=TRUE, echo=FALSE} +ggplot(pss, + aes(stfeco, + stfdem, + shape = district, + color = district + ) + ) + + geom_jitter(alpha = .2) + + facet_grid(district ~ gndr) +``` +] +] + +--- + +# Was es noch zu entdecken gibt in ggplot2? + +-- - Schriftarten bearbeiten bzw. Darstellung des Plots +-- + - *missing values* darstellen +-- + - Marginal Plots +-- + - bivariate Normalverteilungen +-- + - Karten bearbeiten +-- + - Zusammenfügen von Plots -### Lab -Die Übungsaufgaben findet ihr als ```task ggplot``` in RStudio Cloud. \ No newline at end of file +--- + +# Lab +Die Übungsaufgaben findet ihr als `task ggplot` in RStudio Cloud. + +--- + +class: inverse2, mline, center, middle + +# Das war's! \ No newline at end of file diff --git a/slides/pre-slides.Rmd b/slides/pre-slides.Rmd index fc2cab97a081afb6ef15d623e40dd29e733442ef..d3ab6196e5812209fbb543d6affbf0157d8218ad 100644 --- a/slides/pre-slides.Rmd +++ b/slides/pre-slides.Rmd @@ -1,45 +1,53 @@ --- -title: "Lasset die Spiele beginnen!" -subtitle: "Daten bändigen & visualisieren \n Methodentage 2021" -author: "B. Philipp Kleer" -date: "11. Oktober 2021" -output: - slidy_presentation: - footer: "CC BY-SA 4.0, B. Philipp Kleer" - widescreen: true - highlight: pygments - theme: readable - css: ./styles/style-slides.css - df_print: paged - mathjax: default - self_contained: false - incremental: false #True dann jedes Bullet einzeln - collapse: true # means the text output will be merged into the R source code block +output: + xaringan::moon_reader: + css: ["./styles/slides.css"] + nature: + seal: false + highlightStyle: github + highlightLines: true + highlightLanguage: ["r"] + countIncrementalSlides: false + ratio: '16:9' + slideNumberFormat: | + <div class="progress-bar-container"> + <div class="progress-bar" style="width: calc(%current% / %total% * 100%);"> + </div> + </div>` + navigation: + scroll: false --- ```{r setup, include=FALSE} library("knitr") library("rmarkdown") +library("tidyverse") +library("DT") +library("icons") -statistics <- readRDS("../datasets/statistics.rds") +uni <- readRDS("../datasets/uni.rds") -opts_chunk$set(fig.path = 'pics/s6-', # path for calculated figures - fig.align = 'center', # alignment of figure (also possible right, left, default) - fig.show = 'hold', # how to show figures: hold -> direct at the end of code chunk; animate: all plots in an animation - fig.width = 3, # figure width - fig.height = 4, # figure height +opts_chunk$set(fig.path = "pics/dplyr-", # path for calculated figures + fig.align = "center", # alignment of figure (also possible right, left, default) + fig.show = "hold", # how to show figures: hold -> direct at the end of code chunk; animate: all plots in an animation + out.width = "60%", echo = TRUE, # Code is printed eval = FALSE, # Code is NOT evaluated warning = FALSE, # warnings are NOT displayed message = FALSE, # messages are NOT displayed - size = "tiny", # latex-size of code chunks background = "#E7E7E7", # background color of code chunks comment = "", # no hashtags before output - options(width = 80), results = "markdown", rows.print = 15 ) +options(DT.options = list(pageLength = 5, + language = list(search = 'Filter:'), + autowidth = TRUE, + lengthChange = FALSE, + formatStyle = (fontSize = "40%")) + ) + htmltools::tagList( xaringanExtra::use_clipboard( button_text = "<i class=\"fa fa-clipboard\"></i>", @@ -48,158 +56,230 @@ htmltools::tagList( ), rmarkdown::html_dependency_font_awesome() ) + ``` -## Was ist tidyverse? -:::: {class="flex-container"} +class: center, title-slide, middle + +# Daten bändigen & visualisieren + +## Was wir machen und wie wir uns organisieren + +### B. Philipp Kleer + +### Methodentage 2021 + +### 11. Oktober 2021 -::: {style="text-align: left; flex-grow:1; flex-basis: 30%"} +.social[ +   [`r icons::icon_style(fontawesome("orcid"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://orcid.org/0000-0003-1935-387X)   [`r icons::icon_style(fontawesome("gitlab"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://gitlab.com/bpkleer)   [`r icons::icon_style(fontawesome("university"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://www.uni-giessen.de/faculties/f03/departments/dps/staff/researchers/kleer?set_language=en)   [`r icons::icon_style(fontawesome("researchgate"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://www.researchgate.net/profile/Benedikt_Kleer) +] + +--- + +# Was ist tidyverse? + +.pull-left[ **Tidyverse** ist ein Paket, dass mehrere Pakete beinhaltet, die alle nach ähnlicher Syntax funktionieren und untereinander kompatibel sind. Es bietet somit einen sehr großen Funktionsumfang und wird daher auch viel genutzt. -::: +] -::: {style="text-align: center; flex-grow:1"} + +.pull-right[ +.center[  -::: +] +] + +--- -:::: +# The Core tidyverse -## The Core tidyverse -:::: {class="flex-container"} +.pull-left[ +**Tidyverse** beinhaltet Kernpakete, die allesamt mit dem Befehl `library("tidyverse")` geladen werden. Dies sind: -::: {style="text-align: left; flex-grow:1; flex-basis: 30%"} -**Tidyverse** beinhaltet Kernpakete, die allesamt mit dem Befehl ```library("tidyverse")``` geladen werden. Dies sind: +- **dplyr** (Datenbereinigung) + +- **ggplot2** (Grafiken) + +- **stringr** (Umgang mit Textdaten) -- **dplyr** -- **ggplot2** -- forcats -- tibble -- readr -- **stringr** -- **tidyr** -- purrr +- **tidyr** (Umgang mit Datensätzen) -::: +- forcats (Umgang mit Faktoren) -::: {style="text-align: center; flex-grow:1"} -{width="300px"} -::: +- tibble (Tabellentool) -:::: +- readr (Import von Daten) -## Drei Pakete im Fokus -:::: {class="flex-container"} +- purrr (Umgang mit Funktionen und Vektoren) -::: {class="flex-text"} -**tidyr** beinhaltet eine Grammatik, um Datensätze in ein *tidy* Format zu bringen. Ziel ist es, dass jede -Spalte eine Variable ist, jede Zeile eine Beobachtung und jede Zelle einen Wert beinhaltet. Hier gibt es das [Cheat-Sheet](https://github.com/rstudio/cheatsheets/blob/master/data-import.pdf) zu ```tidyr```. -::: +] +.pull-right[ +.center[ + +] +] -::: {class="flex-picture"} -{width="300px"} -::: +--- + +# Vier Pakete im Fokus + +.pull-left[ +.pull-left[ +.center[ + + +[Cheat-Sheet](https://github.com/rstudio/cheatsheets/blob/master/data-import.pdf) +] +] + +.pull-right[ +.center[ + + +[Cheat-Sheet](https://github.com/rstudio/cheatsheets/blob/master/data-transformation.pdf) +] +] +] + +.pull-right[ +.pull-left[ +.center[ + + +[Cheat-Sheet](https://github.com/rstudio/cheatsheets/blob/master/data-visualization-2.1.pdf) +] +] -::: {class="flex-text"} -**dplyr** beinhaltet eine Grammatik, um Datenmanipulationen zu machen. Es besticht dabei durch seine Einfachheit und der Kombinationsmöglichkeit der Grundbefehle. Für eine erste Dateneinsicht ist dies meist hilfreich zu nutzen, oder um die Daten umzustrukturieren (manipulieren). Hier gibt es das [Cheat-Sheet](https://github.com/rstudio/cheatsheets/blob/master/data-transformation.pdf). -::: +.pull-right[ +.center[ + -::: {class="flex-picture"} -{width="300px"} -::: +[Cheat-Sheet](https://github.com/rstudio/cheatsheets/blob/master/strings.pdf) +] +] +] -::: {class="flex-text"} -**ggplot2** ist **das** Grafikpaket in R. Es bietet die Möglichkeit Grafiken genauestens den eigenen Präferenzen anzupassen und darzustellen. Dazu gibt es viele Erweiterungspakete, die ebenfalls auf der Syntax von **ggplot2** aufbauen. Hier gibt es das [Cheat-Sheet](https://github.com/rstudio/cheatsheets/blob/master/data-visualization-2.1.pdf). +??? -::: +**tidyr** beinhaltet eine Grammatik, um Datensätze in ein *tidy* Format zu bringen. Ziel ist es, dass jede +Spalte eine Variable ist, jede Zeile eine Beobachtung und jede Zelle einen Wert beinhaltet. Hier gibt es das zu `tidyr`. + +**dplyr** beinhaltet eine Grammatik, um Datenmanipulationen zu machen. Es besticht dabei durch seine Einfachheit und der Kombinationsmöglichkeit der Grundbefehle. Für eine erste Dateneinsicht ist dies meist hilfreich zu nutzen, oder um die Daten umzustrukturieren (manipulieren). Hier gibt es das -::: {class="flex-picture"} -{width="300px"} -::: +**ggplot2** ist **das** Grafikpaket in R. Es bietet die Möglichkeit Grafiken genauestens den eigenen Präferenzen anzupassen und darzustellen. Dazu gibt es viele Erweiterungspakete, die ebenfalls auf der Syntax von **ggplot2** aufbauen. Hier gibt es das -::: {class="flex-text"} -**stringr** ist ein Paket um ```string```-Variablen vernünftig in R bearbeiten zu können. Es gibt einige Alternativen, aber dennoch bleibt **stringr** ein beliebtes Paket zur Bearbeitung von ```string```-Variablen. Insbesondere bei Textanalysen muss man im Vorfeld die ```string```-Variablen bearbeiten. Hier gibt es das [Cheat-Sheet](https://github.com/rstudio/cheatsheets/blob/master/strings.pdf). +**stringr** ist ein Paket um `string`-Variablen vernünftig in R bearbeiten zu können. Es gibt einige Alternativen, aber dennoch bleibt **stringr** ein beliebtes Paket zur Bearbeitung von `string`-Variablen. Insbesondere bei Textanalysen muss man im Vorfeld die p`string`-Variablen bearbeiten. -::: -::: {class="flex-picture"} -{width="300px"} -::: +--- -:::: +# Ziel des Workshops -## Ziel des Workshops Die Teilnehmenden können am Ende des Workshops ... -> - ... die Grammatik der Pakete ggplot2 und tidyverse verstehen und auf eigene Zwecke anwenden. -> - ... Daten zielführend aufbereiten. -> - ... Daten und Ergebnisse sinnvoll darstellen. -> - ... erste eigene Funktionen programmieren. +-- + +- ... die Grammatik der Pakete ggplot2 und tidyverse verstehen und auf eigene Zwecke anwenden. + +-- + +- ... Daten zielführend aufbereiten. + +-- + +- ... Daten und Ergebnisse sinnvoll darstellen. +-- -## Ziel des Workshops +- ... erste eigene Funktionen programmieren. + +--- + +# Ziel des Workshops Es geht in diesem kurzen 1-Tages-Kurs vor allem um **Readbility-Skills**. Ziel ist es, dass man neue Probleme mit dem hier gezeigten lösen kann. Dafür sollte der Inhalt aber während des Workshops gut aufbereitet bzw. nachbereitet werden (eigene Notizen in den Skripten etc.). +-- + In meinen Kursen gilt immer folgendes Prinzip: **Was man nicht schreibt, lernt man auch nicht!** -Es ist also eine didaktische Entscheidung von mir, dass es keine fertige Skripts gibt. Selbst programmieren heißt eben auch selbst Code schreiben und nicht nur einzelnen Felder austauschen (wie bei Click and Play mit SPSS). Ebenso wird meiner Meinung nach das Verständnis von Funktionen viel besser vermittelt. +-- + +Es ist also eine didaktische Entscheidung von mir, dass es keine fertige Skripts gibt. Selbst programmieren heißt eben auch selbst Code schreiben und nicht nur einzelnen Felder austauschen (wie bei *Click and Play* mit SPSS). Ebenso wird meiner Meinung nach das Verständnis von Funktionen viel besser vermittelt. + +-- Die Slides bzw. HTML-Dokumente haben aber kopierbaren Code (ein bisschen Erleichterung). Wenn man in diesen Dokumenten über den Code geht, erscheint oben rechts ein *Clipboard*, mit dem man den Code in die Zwischenablage kopiert. -{width=50%} +-- + + + +--- + +# Wer ich bin und wie ich Workshops leite? -## Wer ich bin und wie ich Workshops leite? +.pull-left[ **Wer bin ich?** -> - seit 2015 Mitarbeiter an der Professur für Methoden (viele praktische Methoden-/Projektkurse bisher gegeben) -> - nutze seit mehreren Jahren bereits R bzw. RStudio (vor allem Projekt-Funktion -> - derzeit: gefördertes Lehrprojekt, in dem R-Kursmaterial für Personen aufbereitet wird, die keine Computer-/Programmierkenntnisse haben +- seit 2015 Mitarbeiter an der Professur für Methoden (viele praktische Methoden-/Projektkurse bisher gegeben) +- nutze seit mehreren Jahren bereits R bzw. RStudio (vor allem Projekt-Funktion +- derzeit: gefördertes Lehrprojekt, in dem R-Kursmaterial für Personen aufbereitet wird, die keine Computer-/Programmierkenntnisse haben +] + +-- +.pull-right[ **Wie ich meine Rolle als Workshopleiter sehe?** -> 1. kollegiales, respektvolles Mitaneinander -> 2. Interesse daran, anderen zu helfen/zu unterstützen -> 3. in der Ansprache ziehe ich das Du vor -> 4. Kurz-Inputs und dann eigenes *trial-and-error* -> 5. Unterstützung beim Code-Crashing -> 6. gebe Input, aktive Mitarbeit aber erforderlich +1. kollegiales, respektvolles Mitaneinander +2. Interesse daran, anderen zu helfen/zu unterstützen +3. in der Ansprache ziehe ich das Du vor +4. Kurz-Inputs und dann eigenes *trial-and-error* +5. Unterstützung beim Code-Crashing +6. gebe Input, aktive Mitarbeit aber erforderlich +] + +-- + +Alle Kursmaterialien sind entweder auf [gitlab](https://gitlab.com/bpkleer/mtg-21-tidyverse) oder in der [R Studio Cloud](https://rstudio.cloud/spaces/146374/join?access_code=xfg7axLLy1bVgItjpH869s1Jg3Lmd9ajntRdEKMH) runterzuladen. In [ILIAS](https://ilias.uni-giessen.de/ilias/goto.php?target=grp_247939&client_id=JLUG) finden sich Links an die betreffenden Stellen. -> * alle Kursmaterialien sind entweder auf [gitlab](https://gitlab.com/bpkleer/mtg-21-tidyverse) oder in der [R Studio Cloud](https://rstudio.cloud/spaces/146374/join?access_code=xfg7axLLy1bVgItjpH869s1Jg3Lmd9ajntRdEKMH) runterzuladen. In [ILIAS](https://ilias.uni-giessen.de/ilias/goto.php?target=grp_247939&client_id=JLUG) finden sich Links an die betreffenden Stellen. +--- -## Arbeiten mit R +# Arbeiten mit R Arbeiten mit R heißt in der Regel immer wieder auf Probleme zu stoßen und willens zu sein, diese Probleme zu lösen. In meiner jetzt fast zehnjährigen Arbeit mit R bin ich noch nie auf ein Problem gestoßen, dass man nicht lösen konnte (auch wenn es manchmal umständlich war). +-- + **Wichtig dafür:** Lesefähigkeit von Code. Also das Verständnis von Code. Dies ist auch das primäre Ziel des heutigen Tages. -## Kursmaterialien + +--- + +# Kursmaterialien Wie gesagt, sind die Kursmaterialien direkt in der [RStudio Cloud](https://rstudio.cloud/spaces/146374/join?access_code=xfg7axLLy1bVgItjpH869s1Jg3Lmd9ajntRdEKMH) verlinkt, aber auch in [gitlab](https://gitlab.com/bpkleer/mtg-21-tidyverse). -Die ```code chunks``` haben ein integriertes *Clipboard*, mit dem der Code direkt in ein R Skript kopiert werden kann. Dafür geht man einfach beim betreffenden Code oben rechts auf das *Clipboard*-Zeichen. +Die `code chunks` haben ein integriertes *Clipboard*, mit dem der Code direkt in ein R Skript kopiert werden kann. Dafür geht man einfach beim betreffenden Code oben rechts auf das *Clipboard*-Zeichen. ```{r codechunk} install.packages("tidyverse", dependencies = TRUE) ``` -**Wichtig**: Bei den HTML-Präsentationen führt ein Mausklick immer zur nächsten Slide. Dies einfach deaktiveren, in dem man die Taste ```k``` drückt. Dann kann man in Ruhe den Code kopieren und mit den Pfeiltasten durch die Slide navigieren. In den HTML-Dokumenten am Nachmittag kann man einfach klicken, da es keine Präsentationen mehr sind. - -**Weitere Shortcuts für die Präsentationen:** - -```b```, ```>```, ```+```: Text wird größer - -```s```, ```<```, ```-```: Text wird kleiner +-- -```f```: Fußzeile zeigen/verstecken - -```c```, ```t```: Inhaltsverzeichnis (springen auf andere Seite möglich) +**Wichtig**: Es handelt sich um *.html*-Präsentationen, die grafisch nur dann korrekt angezeigt werden, wenn eben auch die anderen Ordner relativ genauso lokal gespeichert sind, wie es beim Download entsteht. +--- -## Coding Konvention +# Coding Konvention Jede Person hat eigene Vorlieben, was den geschriebenen Code angeht. Im Folgenden möchte ich nur kurz meine Präferenzen darlegen. -Für neue Variablen oder Dataframes nutze ich in der Regel das Format ```lowerCamelCase```: +Für neue Variablen oder Dataframes nutze ich in der Regel das Format `lowerCamelCase`: ``` {r format} df$newVar <- NA @@ -209,7 +289,9 @@ newDf <- subset(df, ) ``` -Selbst geschrieben Funktionen schreibe ich mit ```_```: +-- + +Selbst geschrieben Funktionen schreibe ich mit `_`: ```{r format2} own_mean <- function(x){ mean = sum(x) / length(x) @@ -217,7 +299,11 @@ own_mean <- function(x){ } ``` -Wie im ersten Fall bereits sichtbar, trenne ich Argumente mit ```Enter``` (Zeilenumbruch) und setze Klammern ebenfalls in neue Zeilen. Das hat den Vorteil, dass die Kommentierung einfacher erfolgen kann. Hat eine Funktion nur ein einziges Argument bleibt die Klammer in der gleichen Zeile. + +--- + +# Code Konvention +Wie im ersten Fall bereits sichtbar, trenne ich Argumente mit `Enter` (Zeilenumbruch) und setze Klammern ebenfalls in neue Zeilen. Das hat den Vorteil, dass die Kommentierung einfacher erfolgen kann. Hat eine Funktion nur ein einziges Argument bleibt die Klammer in der gleichen Zeile. ```{r format3} # einzelnes Argument @@ -231,22 +317,28 @@ str_sub(tweet$text[23], # Quelle ``` -## Start {.break} +--- + +# Start Der Kurs setzt Grundkenntnisse voraus. Ihr lernt euch jetzt in Breakout-Rooms kennen. In den Breakout-Rooms sollt ihr euch kennenlernen und ein paar Grundaufgaben in R lösen. Dies dient auch der Auffrischung. Ich schaue abwechselnd in den Breakout-Rooms nach. Ihr könnte mich aber auch rufen. Folgende Aufgaben sind zu erledigen: -> 1. Den Datensatz ```pss.rds``` in das environment laden (fiktiver Datensatz Panem Social Survey) +1. Den Datensatz `pss.rds` in das environment laden (fiktiver Datensatz Panem Social Survey) -> 2. Die Variable ```agea``` deskriptiv beschreiben. +2. Die Variable `agea` deskriptiv beschreiben. -> 3. Schafft eine neue Variable, die dem Datensatz hinzugefügt werden soll. Diese Variable soll ```socgroup``` heißen und einfach eine Sequenz von ```1, 2, 3, 4``` über die Länge des Datensatzes beinhalten. **Wichtig**: Jede Zahl soll gleich oft vorkommen. Ob sich die Sequenz immer wiederholt (also Reihenfolge ```1, 2, 3, 4, 1, 2, 3, 4, ...```), oder ihr erst alle ```1```, dann alle ```2``` etc. abbildet, ist euch überlassen. +3. Schafft eine neue Variable, die dem Datensatz hinzugefügt werden soll. Diese Variable soll `socgroup` heißen und einfach eine Sequenz von `1, 2, 3, 4` über die Länge des Datensatzes beinhalten. **Wichtig**: Jede Zahl soll gleich oft vorkommen. Ob sich die Sequenz immer wiederholt (also Reihenfolge `1, 2, 3, 4, 1, 2, 3, 4, ...`), oder ihr erst alle `1`, dann alle `2` etc. abbildet, ist euch überlassen. -> 4. Vergesst nicht, euch gegenseitig vorzustellen! +4. Vergesst nicht, euch gegenseitig vorzustellen! **Zeit:** 30 Minuten. Wenn jemand nicht seine lokale R-Installation nutzen möchte, kann er einfach auf die **RStudio Cloud** zurückgreifen. Dort sind die Datensätze, Skripte & Folien auch bereits hinterlegt und müssen nicht direkt runtergeladen werden. -# Viel Spaß! +--- + +class: inverse2, mline, center, middle + +# Das war's! diff --git a/slides/quiz.Rmd b/slides/quiz.Rmd index 3b789a398af068972e2f2968a5f464e87fbb7cb7..ab5c3e135128872d18a45f1570dbeab9fb9492fa 100644 --- a/slides/quiz.Rmd +++ b/slides/quiz.Rmd @@ -1,26 +1,28 @@ --- -title: "Quiz" -subtitle: "Daten bändigen & visualisieren" -author: "B. Philipp Kleer" -date: "11. Oktober 2021" -output: - slidy_presentation: - footer: "CC BY-SA 4.0, B. Philipp Kleer" - widescreen: true - highlight: pygments - theme: readable - css: ./styles/style-slides.css - df_print: paged - mathjax: default - self_contained: false - incremental: false #True dann jedes Bullet einzeln - collapse: true # means the text output will be merged into the R source code block +output: + xaringan::moon_reader: + css: ["./styles/slides.css"] + nature: + seal: false + highlightStyle: github + highlightLines: true + highlightLanguage: ["r"] + countIncrementalSlides: false + ratio: '16:9' + slideNumberFormat: | + <div class="progress-bar-container"> + <div class="progress-bar" style="width: calc(%current% / %total% * 100%);"> + </div> + </div>` + navigation: + scroll: false --- ```{r setup, include=FALSE} library("knitr") library("rmarkdown") library("tidyverse") +library("icons") uni <- readRDS("../datasets/uni.rds") @@ -51,7 +53,27 @@ htmltools::tagList( ) ``` -## Was macht der folgende Code? +class: center, title-slide, middle + +# Einführung in ggplot-Grammatik + +## Daten bändigen & visualisieren + +### B. Philipp Kleer + +### Methodentage 2021 + +### 11. Oktober 2021 + +.social[ +   [`r icons::icon_style(fontawesome("orcid"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://orcid.org/0000-0003-1935-387X)   [`r icons::icon_style(fontawesome("gitlab"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://gitlab.com/bpkleer)   [`r icons::icon_style(fontawesome("university"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://www.uni-giessen.de/faculties/f03/departments/dps/staff/researchers/kleer?set_language=en)   [`r icons::icon_style(fontawesome("researchgate"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://www.researchgate.net/profile/Benedikt_Kleer) +] + +--- + +# Was macht der folgende Code? + +.pull-left-40[ ``` {r quiz1} df %>% slice(seq(1, @@ -61,13 +83,22 @@ df %>% group_by(device) %>% summarize(mean(timeOfUse)) ``` +] + +-- -> - der Datensatz ```df``` wird geteilt, wie wählen mit ```seq()``` jeden 100. Fall -> - wir nehmen daraus dann nur Fälle, deren Variable ```os``` gleich ```"iOS"``` ist -> - dann sortieren wir nachd er Variable ```device``` -> - zuletzt lassen wir den Mittelwert der Variable ```timeOfUse``` gruppiert nach ```device``` ausgeben. +.pull-right-60[ +**Lösung:** +- der Datensatz `df` wird geteilt, wir wählen mit `seq()` jeden 100. Fall +- wir nehmen daraus dann nur Fälle, deren Variable `os` gleich `"iOS"` ist +- dann sortieren wir nach der Variable `device` +- zuletzt lassen wir den Mittelwert der Variable `timeOfUse` gruppiert nach `device` ausgeben. +] + +--- + +# Und was macht dieser Code? -## Und was macht dieser Code? ``` {r quiz2} df %>% select(gndr, income, residence, inhabitants) %>% @@ -77,11 +108,18 @@ df %>% inhabitants > 50000 ~ "large city")) ``` -> - aus dem Datensatz ```df``` werden die Variablen ```gndr```, ```income```, ```residence``` und ```inhabitants``` gefiltert -> - danach fügen wir die Fälle aus dem Datensatz ```df2``` hinzu. -> - zuletzt schaffen wir eine neue Variable ```sizeOfTown```, die der Höhe der Einwohner:innen eine Beschreibung zuordnet (ordinale Variable) +-- + +**Lösung:** +- aus dem Datensatz `df` werden die Variablen `gndr`, `income`, `residence` und `inhabitants` gefiltert +- danach fügen wir die Fälle aus dem Datensatz `df2` hinzu. +- zuletzt schaffen wir eine neue Variable `sizeOfTown`, die der Höhe der Einwohner:innen eine Beschreibung zuordnet (ordinale Variable) + + +--- + +# Und was macht dieser Code? -## Und was macht dieser Code? ``` {r quiz3} df %>% bind_rows(df3) %>% @@ -91,8 +129,16 @@ df %>% inhabitants > 50000 ~ "large city")) ``` -> - fehlerhaft Code! -> - in ```filter()``` genutzte Anweisungen sind falsch! -> - korrekt ist: ```filter(income > 1000 & gndr == "female")``` -## Das war's! Ab zu ```ggplot```! \ No newline at end of file +-- + +**Lösung:** +- fehlerhaft Code! +- in `filter()` genutzte Anweisungen sind falsch! +- korrekt ist: `filter(income > 1000 & gndr == "female")` + + +--- +class: inverse2, mline, center, middle + +# Das war's! Ab zu `ggplot`! \ No newline at end of file diff --git a/slides/tidyr-grammar.Rmd b/slides/tidyr-grammar.Rmd index 37b7d4ad38a09b17f80a9366b5395012ed0f6112..7eba16aaf938d7df1fb038930c317e2e97c297d1 100644 --- a/slides/tidyr-grammar.Rmd +++ b/slides/tidyr-grammar.Rmd @@ -1,22 +1,28 @@ --- -title: "Datensätze zusammenführen" -subtitle: "Daten bändigen & visualisieren" -author: "B. Philipp Kleer" -date: "11. Oktober 2021" -output: - slidy_presentation: - slide_level: 3 - footer: "Copyright: CC BY-SA 4.0, B. Philipp Kleer" - css: styles/style-slides.css - df_print: paged - highlight: espresso - theme: readable +output: + xaringan::moon_reader: + css: ["./styles/slides.css"] + nature: + seal: false + highlightStyle: github + highlightLines: true + highlightLanguage: ["r"] + countIncrementalSlides: false + ratio: '16:9' + slideNumberFormat: | + <div class="progress-bar-container"> + <div class="progress-bar" style="width: calc(%current% / %total% * 100%);"> + </div> + </div>` + navigation: + scroll: false --- ```{r setup, include=FALSE} library("knitr") library("rmarkdown") library("tidyverse") +library("icons") uni <- readRDS("../datasets/uni.rds") uni1 <- readRDS("../datasets/uni1.rds") @@ -28,19 +34,15 @@ statclass <- readRDS("../datasets/statclass.rds") statclass2 <- readRDS("../datasets/statclass2.rds") points <- readRDS("../datasets/points.rds") -opts_chunk$set(fig.path = 'pics/s6-', # path for calculated figures +opts_chunk$set(#fig.path = 'pics/s6-', # path for calculated figures fig.align = 'center', # alignment of figure (also possible right, left, default) fig.show = 'hold', # how to show figures: hold -> direct at the end of code chunk; animate: all plots in an animation - fig.width = 3, # figure width - fig.height = 4, # figure height + out.width = "60%", echo = TRUE, # Code is printed eval = FALSE, # Code is NOT evaluated warning = FALSE, # warnings are NOT displayed message = FALSE, # messages are NOT displayed - size = "tiny", # latex-size of code chunks - background = "#E7E7E7", # background color of code chunks comment = "", # no hashtags before output - options(width = 80), results = "markdown", rows.print = 15 ) @@ -55,56 +57,171 @@ htmltools::tagList( ) ``` -## ```Tidyr``` und ```dplyr``` -Wenn wir mit Sekundärdaten arbeiten, sind diese oftmals bereits so vorbereitet, dass die Daten entweder im *long*- oder im *wide*-Format vorliegen. Wenn wir aber selbst Daten erheben oder z.B. über eine API laden, kann es sein, dass dies nicht der Fall ist. Auch kommt es vor, dass die Datenerhebung in Teilschritten erfolgt oder zum Beispiel Daten aus zwei verschiedenen Zeitpunkten für die Datenanalyse zusammengefügt werden sollen. Auch bei Multi-Level-Modellen werden oftmals Makrodaten mit Mikrodaten vor der Analyse verbunden. +class: center, title-slide, middle -Diese verschiedenen Varianten werden wir uns nun in ```tidyverse``` mithilfe von ```tidyr``` und ```dplyr``` anschauen. +# Umgang mit Datensätzen -## Das Gerüst von Datensätzen mit ```tidyr``` -Es ist für die Verarbeitung wichtig, dass die Datensätz *tidy* sind, damit die Funktionen in R problemlos mit den Daten laufen. Das bedeutet, dass die Daten in einem bestimmten Format vorliegen müssen, damit die Funktionen in R auch gut mit den Daten funktionieren (weniger *troubleshooting*). +## Daten bändigen & visualisieren + +### B. Philipp Kleer + +### Methodentage 2021 + +### 11. Oktober 2021 + +.social[ +   [`r icons::icon_style(fontawesome("orcid"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://orcid.org/0000-0003-1935-387X)   [`r icons::icon_style(fontawesome("gitlab"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://gitlab.com/bpkleer)   [`r icons::icon_style(fontawesome("university"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://www.uni-giessen.de/faculties/f03/departments/dps/staff/researchers/kleer?set_language=en)   [`r icons::icon_style(fontawesome("researchgate"), fill=rgb(235, 129, 27, maxColorValue = 255), scale = 1)`](https://www.researchgate.net/profile/Benedikt_Kleer) +] + +--- + +# Tidyr und dplyr +Daten können in verschiedenen Formaten vorliegen. Die zwei bekanntesten sind wohl das + +-- + +1. *long*-Format + +-- + +2. *wide*-Format (*standard*) + +-- + +Aber auch andere Formate für Daten sind möglich. Z.B. wenn wir Daten über eine API laden. Auch bei Zusammenführen von Datensätzen muss das Format beachtet werden. + +-- + +Diese verschiedenen Varianten werden wir uns nun in `tidyverse` mithilfe von `tidyr` und `dplyr` anschauen. + +??? +Das Wide-Format eignet sich für die Darstellung von Querschnittsdaten oder Zeitreihendaten. Im Wide-Format enthalten mehrere Spalten die Messwerte der gleichen Variable zu unterschiedlichen Zeitpunkten oder Versuchswiederholungen, während das Individuum oder Beobachtungseinheit die ganze Zeile der Tabelle beansprucht. Diese Art der Darstellung ist intuitiv für den Vergleich von verschiedenen Werten der gleichen Variable, aber ungeeignet für die Darstellung von Paneldaten mit mehr als einer Variable. + +Im Long-Format werden alle Werte der messwiederholten Variable in der gleichen Spalte und der zugehörige Zeitpunkt in einer eigenen Variable dargestellt. Daher werden die Daten im Long-Format auch als „gestapelt“ bezeichnet. Weitere Variablen werden in einer eigenen Spalte notiert, nutzen jedoch ebenfalls die Werte der Zeitvariable. Die Zeitvariable (die auch die Versuchswiederholung oder den Kontext der Messung angeben kann), ist somit explizit angegeben. + +--- + +class: inverse2, mline, center, middle + +# tidyr und Datensatz-Formate + +--- + +# Das Gerüst von Datensätzen mit tidyr + +Es ist für die Verarbeitung in R wichtig, dass die Datensätze *tidy* sind, damit die Funktionen in R problemlos mit den Daten laufen. Zum Beispiel für die Anwendung von `ggplot` empfehlen sich *tidy*-Datensätze. Das bedeutet, dass die Daten in einem bestimmten Format vorliegen müssen, damit die Funktionen in R auch gut mit den Daten funktionieren (weniger *troubleshooting*). + +-- **Was ist ein Datensatz?** + +-- + Ein Datensatz ist generell immer eine Sammlung von Werten, sei es numerisch oder eine Zeichenkette. Diese Werte sind immer auf zwei Arten organisiert: Jeder Wert gehört zu einer **Variable** und zu einer **Beobachtung**. Eine **Variable** inkludiert alle Werte, die für diese gemessen worden sind (also alle Beobachtungen auf dieser Variable). Eine **Beobachtung** inkludiert alle Werte, die für diese Beobachtung gemessen wurden (also alle Variablenwerte dieser *Einheit*). +-- + Damit Daten in R gut mit den Funktionen genutzt werden können, müssen diese in einem *tidy*-Format vorliegen (auch *long*-Format genannt). Ein Datensatz ist dann *tidy*, wenn ... -> - ... jede Variable eine Spalte ist, -> - ... jede Beobachtung eine Zeile ist, -> - ... und jede Beobachtungseinheit eine Tabelle formt. +-- + +- ... jede Variable eine Spalte ist, + +-- + +- ... jede Beobachtung eine Zeile ist, +-- -### untidy data sets -Die folgenden zwei Datensätze zeigen Daten, die jeweils nicht *tidy* sind. Wir werden diese im Folgenden bereinigen. +- ... und jede Beobachtungseinheit eine Tabelle formt. + + +??? + +Also das long-format! + +--- + +# untidy data sets (Beispiel 1) +Im Folgenden bearbeiten wir zwei Datensätze, die jeweils nicht *tidy* sind. + +-- + +.center[ ```{r statclass, eval=TRUE} statclass ``` +] + +-- Dieser Datensatz ist im sogenannten *wide*-Format. D.h. wenn wir neue Prüfungen hätten, würden wir einfach weitere Spalten hinzufügen. Dies ist aber für die Verarbeitung mit R teilweise problematisch, denn wir benötigen oft ein *long*-Format. +--- + +# untidy data sets (Beispiel 2) + +.center[ ``` {r statclass2, eval=TRUE} statclass2 ``` +] -In diesem Fall haben wir mehrere Probleme: Zum einen sind in den Spalten nicht überall Variablen, sondern Beobachtungen (```momo```, ```kim```, ```sascha```) und in ```exam``` finden wir wiederum Variablennamen. +-- -Fangen wir mit ```statclass``` an. +In diesem Fall haben wir mehrere Probleme: Zum einen sind in den Spalten nicht überall Variablen, sondern Beobachtungen (`momo`, `kim`, `sascha`) und in `exam` finden wir wiederum Variablennamen. + +-- + +Fangen wir mit `statclass` an. + + +--- + +# Tidy-up statclass -### Tidy-up ```statclass``` In der Tabelle kann die Note jeder Person aus jeder Prüfung ausgelesen werden. Überlegt kurz, welche Variablen wir bei diesem Satz generieren möchten! ```{r statclassa, eval=TRUE} statclass ``` -> - ```names```: momo, sascha, kim -> - ```course```: statI, statII, r, spss -> - ```grade```: Wert in Abhängigkeit der zwei oberen. +-- + +- `names`: momo, sascha, kim +- `course`: statI, statII, r, spss +- `grade`: Wert in Abhängigkeit der zwei oberen. + +-- -Es sind also zwei Informationen in den Spalten ```stat1```, ```stat2```, ```r``` und ```spss```. Nämlich welcher Test es ist (implizit über Variablenname) und die Note. D.h. hier sind Werte als Variablenname angegeben und das verstößt gegen die Regeln eines *tidy* Datensatzes. Wir benötigen in einem *tidy*-Format aber beide Informationen explizit! Denn die Spaltennamen sind hier Werte (Art der Prüfung) und nicht einfach Namen. +Es sind also zwei Informationen in den Spalten `stat1`, `stat2`, `r` und `spss`. Nämlich welcher Test es ist (implizit über Variablenname) und die Note. D.h. hier sind Werte als Variablenname angegeben und das verstößt gegen die Regeln eines *tidy* Datensatzes. Wir benötigen in einem *tidy*-Format aber beide Informationen explizit! Denn die Spaltennamen sind hier Werte (Art der Prüfung) und nicht einfach Namen. -Um dies zu bereinigen, nutzt man ```pivot_longer()```. Hierbei geben wir zuerst an, welche Spalten neugeordnet werden sollen (in unserem Fall ```stat1``` bis ```spss```), dann in welche neuen Variablen die Namen bzw. die Werte gespeichert werden sollen. Mit ```names_to``` benennen wir die neue Variable, die den Test unterscheidet und mit ```values_to``` benennen wir die Variable, die die Noten beinhaltet. +--- + +# Tidy-up statclass -``` {r pivot-longer, eval=TRUE} +Um dies zu bereinigen, nutzt man `pivot_longer()`. Hierbei geben wir zuerst an, welche Spalten neugeordnet werden sollen (in unserem Fall `stat1` bis `spss`), dann in welche neuen Variablen die Namen bzw. die Werte gespeichert werden sollen. Mit `names_to` benennen wir die neue Variable, die den Test unterscheidet und mit `values_to` benennen wir die Variable, die die Noten beinhaltet. + +.pull-left-40[ +``` {r pivot-longer} +statclassTidy <- statclass %>% + pivot_longer(stat1:spss, #<< + names_to = "course", #<< + values_to = "grade" #<< + ) %>% #<< + arrange(name, + course + ) + +statclassTidy + +``` +] + +.pull-right-60[ +.center[ +.code60[ +``` {r pivot-longer-out, eval=TRUE, echo=FALSE} statclassTidy <- statclass %>% pivot_longer(stat1:spss, names_to = "course", @@ -116,12 +233,34 @@ statclassTidy <- statclass %>% statclassTidy +``` +] +] +] + +Jetzt haben wir ein *long*-Format, dass die Datenbearbeitung oft einfacher macht (z.B. mit `ggplot2`). **Aber Aufpassen**: Man kann jetzt nicht einfach mehr einen Mittelwert von `grade` berechnen, da dies verschiedene Kurse beinhaltet. Man muss dabei also Bedingungen setzen (wenn man im *long*-Format ist). + +--- + +# Back to wide +Möchte man dies wieder umkehren, nutzt man die Funktion `pivot_wider()`: + +.pull-left-40[ +``` {r pivot-wider} +statclassRe <- statclassTidy %>% + pivot_wider(names_from = course, #<< + values_from = grade, #<< + ) #<< + +statclassRe ``` +] -Jetzt haben wir ein *long*-Format, dass die Datenbearbeitung oft einfacher macht (z.B. mit ```ggplot2```). **Aber Aufpassen**: Man kann jetzt nicht einfach mehr einen Mittelwert von ```grade``` berechnen, da dies verschiedene Kurse beinhaltet. Man muss dabei also Bedingungen setzen (wenn man im *long*-Format ist). +-- -Möchte man dies wieder umkehren, nutzt man die Funktion ```pivot_wider()```: -``` {r pivot-wider, eval=TRUE} +.pull-right-60[ +.center[ +``` {r pivot-wider-out, eval=TRUE, echo=FALSE} statclassRe <- statclassTidy %>% pivot_wider(names_from = course, values_from = grade, @@ -129,18 +268,46 @@ statclassRe <- statclassTidy %>% statclassRe ``` +] +] + +--- +# Tidy-up statclass2 -### Tidy-up ```statclass2``` -Wo liegt hier das Problem? +Wo liegt hier noch ein weiteres Problem? +.center[ ``` {r statclass2a, eval=TRUE} statclass2 ``` +] -> - Namenwerte sind Spaltennamen! +-- +$\Rightarrow$ Namenwerte sind Spaltennamen! + +--- + +# Tidy-up statclass2 Und hier die Lösung: Auch hier wandeln wir wieder in das *long*-Format um! -```{r statclass2tidy, eval=TRUE} + +.pull-left-40[ +```{r statclass2tidy} +statclass2Tidy <- statclass2 %>% + pivot_longer(momo:sascha, #<< + names_to = "names", #<< + values_to = "grade" #<< + ) + +statclass2Tidy +``` +] + +-- + +.pull-right-60[ +.center[ +```{r statclass2tidy-out, eval=TRUE, echo=FALSE} statclass2Tidy <- statclass2 %>% pivot_longer(momo:sascha, names_to = "names", @@ -149,12 +316,41 @@ statclass2Tidy <- statclass2 %>% statclass2Tidy ``` +] +] + +-- Gibt es evtl. noch mehr Probleme? -> - ```exam``` beinhaltet keine Werte, sondern Namen von Variablen, nämlich von ```exam1``` und ```exam2```! Variablen, die die Note in der Prüfung angeben, deren Wert noch in ```grade``` steht. Deshalb nutzen wir hier jetzt ```pivot_wider()```, um die Daten final *tidy* zu machen: +-- + +`exam` beinhaltet keine Werte, sondern Variablennamen, nämlich `exam1` und `exam2`! Variablen, die die Note in der Prüfung angeben, deren Wert noch in `grade` steht. Deshalb nutzen wir hier jetzt `pivot_wider()`, um die Daten final *tidy* zu machen. + +--- + +# Tidy-up statclass2 + +.pull-left-40[ +``` {r statclass2tidy2} +statclass2Tidy <- statclass2Tidy %>% + pivot_wider(names_from = exam, #<< + values_from = grade #<< + ) %>% #<< + relocate(names) %>% + arrange(names, + test + ) + +statclass2Tidy +``` +] -``` {r statclass2tidy2, eval=TRUE} +-- + +.pull-right-60[ +.center[ +``` {r statclass2tidy2-out, eval=TRUE, echo=FALSE} statclass2Tidy <- statclass2Tidy %>% pivot_wider(names_from = exam, values_from = grade @@ -166,27 +362,77 @@ statclass2Tidy <- statclass2Tidy %>% statclass2Tidy ``` +] +] + +--- + +# Und wieder *wider* +Nur zur Übung könnte man auch dies wiederum in den Ursprungsdatensatz mit `pivot_wider()` verändern: + +-- +.pull-left-40[ +``` {r statclass2tidy3} +statclass2re <- statclass2Tidy %>% + pivot_wider(names_from = test, #<< + values_from = c(exam1, #<< + exam2 #<< + ) + ) + +statclass2re +``` +] -Nur zur Übung könnte man auch dies wiederum in den Ursprungsdatensatz mit ```pivot_wider()``` verändern: +-- -``` {r statclass2tidy3, eval=TRUE} +.pull-right-60[ +``` {r statclass2tidy3-out, eval=TRUE, echo=FALSE} statclass2re <- statclass2Tidy %>% pivot_wider(names_from = test, values_from = c(exam1, exam2 ) ) + +statclass2re ``` +] + +--- + +class: inverse2, mline, middle, center -## Daten bearbeiten mit ```dplyr``` +# Daten bearbeiten mit dplyr -### Teilen eines Datensatzes +--- + +# Teilen eines Datensatzes Zuerst wiederholen wir noch einmal, wie wir einen Datensatz teilen: Wir filtern die Fälle, die für unsere spätere Analyse relevant sind. Zum Beispiel wollen wir nur über Psychologie-Studierende aus Marburg forschen. Anschließend möchten wir eine neue Variable erstellen, die eine Beschreibung für die Studiendauer inkludiert (als Faktor) Welche Funktionen müssen wir anwenden? -### Teilen eines Datensatzes -```{r split-df, eval=TRUE} + +--- + +# Teilen eines Datensatzes + +```{r split-df} +uniPumPsy <- uni %>% + filter(city == "Marburg" & study == "Psychology") %>% + mutate(term.group = factor(case_when(term <= 2 ~ "Anfänger:in", + term > 2 & term <= 6 ~ "Regelstudienzeit", + term > 6 ~ "Langzeit" + ) + ) + ) + +head(uniPumPsy) +``` + +-- + +```{r split-df-out, eval=TRUE, echo=FALSE} uniPumPsy <- uni %>% filter(city == "Marburg" & study == "Psychology") %>% mutate(term.group = factor(case_when(term <= 2 ~ "Anfänger:in", @@ -199,13 +445,41 @@ uniPumPsy <- uni %>% head(uniPumPsy) ``` -### Datensätze zusammenführen (Fälle hinzufügen) -Im nächsten Schritt nehmen wir nun an, dass die Datenerfassung von 4 verschiedenen Personen durchgeführt wurde und es somit 4 Teildatensätze gibt, die nun zu einem vollständigen Datensatz verbunden werden sollen. Dazu nutzen wir die Funktion ```bind_rows()```. In unserem Beispiel haben alle 4 Teildatensätze genau die gleiche Anzahl an Variablen, die dazu auch noch genau gleich benannt sind! Mit dem Argument ```.id``` erstellen wir eine Variable names ```"origin"```, die die Herkunft des Falles erfasst. Dies ist automatisch nummeriert. Mit ```mutate()``` machen wir daraus einen Faktor, der eine bessere Beschreibung beinhaltet (*coder1*, *coder2*, *coder3*, *coder4*) +--- + +# Datensätze zusammenführen (Fälle hinzufügen) +Im nächsten Schritt nehmen wir nun an, dass die Datenerfassung von 4 verschiedenen Personen durchgeführt wurde und es somit 4 Teildatensätze gibt, die nun zu einem vollständigen Datensatz verbunden werden sollen. Dazu nutzen wir die Funktion `bind_rows()`. In unserem Beispiel haben alle 4 Teildatensätze genau die gleiche Anzahl an Variablen, die dazu auch noch genau gleich benannt sind! Mit dem Argument `.id` erstellen wir eine Variable names `"origin"`, die die Herkunft des Falles erfasst. Dies ist automatisch nummeriert. Mit `mutate()` machen wir daraus einen Faktor, der eine bessere Beschreibung beinhaltet (*coder1*, *coder2*, *coder3*, *coder4*) + +.pull-left-40[ +.code60[ +```{r merge1} +uniAll <- uni1 %>% + bind_rows(list(uni2, + uni3, + uni4 + ), + .id = "origin" + ) %>% + mutate(origin = factor(origin, + labels = c("coder1", + "coder2", + "coder3", + "coder4" + ) + ) + ) + +table(uniAll$origin) +head(uniAll$origin) +``` +] +] -### Datensätze zusammenführen (Fälle hinzufügen) -Im nächsten Schritt nehmen wir nun an, dass die Datenerfassung von 4 verschiedenen Personen durchgeführt wurde und es somit 4 Teildatensätze gibt, die nun zu einem vollständigen Datensatz verbunden werden sollen. Dazu nutzen wir die Funktion ```bind_rows()```. In unserem Beispiel haben alle 4 Teildatensätze genau die gleiche Anzahl an Variablen, die dazu auch noch genau gleich benannt sind! Mit dem Argument ```.id``` erstellen wir eine Variable names ```"origin"```, die die Herkunft des Falles erfasst. Dies ist automatisch nummeriert. Mit ```mutate()``` machen wir daraus einen Faktor, der eine bessere Beschreibung beinhaltet (*coder1*, *coder2*, *coder3*, *coder4*) +-- -```{r merge1, eval=TRUE} +.pull-right-60[ +.center[ +```{r merge1-out, eval=TRUE, echo=FALSE} uniAll <- uni1 %>% bind_rows(list(uni2, uni3, @@ -225,14 +499,25 @@ uniAll <- uni1 %>% table(uniAll$origin) head(uniAll$origin) ``` +] Wir haben hier jetzt also aus vier Teildatensätzen einen gesamten Datensatz erstellt, der alle Fälle der vier Teildatensätze enthält. Wichtig, in diesem Fall waren alle Variablennamen gleich! +] -### Datensätze zusammenführen (Fälle hinzufügen) + +--- + +# Datensätze zusammenführen (Fälle hinzufügen) Nun probieren wir einmal aus, was passiert, wenn es zum Beispiel in einem Teildatensatz einen Typo gibt. Zuerst erstellen wir dazu einfach zwei neue Datensätze, die jeweils nur 3 Fälle inkludieren, und unterschiedliche Variablen. -``` {r merge-prob, eval=TRUE} -uniA <- uni[1:3, 4:5] +-- + +.pull-left-40[ +.code60[ +``` {r merge-prob} +uniA <- uni[1:3, + 4:5 + ] City <- c("Giessen", "Marburg", @@ -252,25 +537,100 @@ head(uniA) head(uniB) ``` +] +] +-- + +.pull-right-60[ +.center[ +``` {r merge-prob-out, eval=TRUE, echo=FALSE} +uniA <- uni[1:3, + 4:5 + ] + +City <- c("Giessen", + "Marburg", + "Marburg" + ) -Wir haben also in beiden Datensätzen die zwei Variablen, die Studienort und die Distanz zum Studienort angeben. im Datensatz ```uniB``` ist aber die Variable des Studienorts anders geschrieben. Probieren wir ```bind_rows()``` aus. +distance <- c(21, + 30, + 45 + ) + +uniB <- data.frame(City, + distance + ) -### Datensätze zusammenführen (Fälle hinzufügen) -``` {r test-bind-rows, eval=TRUE} +head(uniA) + +head(uniB) +``` +] +] + +-- + +Wir haben also in beiden Datensätzen die zwei Variablen, die Studienort und die Distanz zum Studienort angeben. im Datensatz `uniB` ist aber die Variable des Studienorts anders geschrieben. Probieren wir `bind_rows()` aus. + +--- + +# Datensätze zusammenführen (Fälle hinzufügen) + +.pull-left-40[ +``` {r test-bind-rows} +uniTest <- uniA %>% + bind_rows(uniB) #< + +uniTest +``` +] + +-- + +.pull-right-60[ +``` {r test-bind-rows-out, eval=TRUE, echo=TRUE} uniTest <- uniA %>% bind_rows(uniB) uniTest ``` +] + +**Wo ist der Fehler?** + +??? + +Da die Variablennamen nicht genau gleich sind, werden nun drei Variablen geschaffen: `city`, `distance` und `City`. Wo die Variable nicht vorliegt, werden automatisch `NAs` erzeugt. Dies ist vorteilhaft, kann aber auch frickelig werden, wenn bei der Datensatzerstellung nicht streng nach einem Codenamen-Schema gearbeitet wurde. **Lösung**: Im Vorfeld Variablen abklären und umbenennen. Andernfalls kann man `full_join()` nutzen. + +--- + +# Unterschiedliche Spaltennamen + +Wenn die Spalten verschiedene Namen haben, benötigt man das Argument `by`. Hierin werden die Kombinationen der zusammengehörigen Variablen bestimmt. -Da die Variablennamen nicht genau gleich sind, werden nun drei Variablen geschaffen: ```city```, ```distance``` und ```City```. Wo die Variable nicht vorliegt, werden automatisch ```NAs``` erzeugt. Dies ist vorteilhaft, kann aber auch frickelig werden, wenn bei der Datensatzerstellung nicht streng nach einem Codenamen-Schema gearbeitet wurde. **Lösung**: Im Vorfeld Variablen abklären und umbenennen. Andernfalls kann man ```full_join()``` nutzen. +In unserem Beispiel würden wir also angeben, dass aus Datensatz `uniA` die Spalte `city` gleich der Spalte `City` aus dem Datensatz `uniB` ist. Gleiches gilt für die `distance` Variable. -### Datensätze zusammenführen (unterschiedliche Spaltennamen) -Dieser Ansatz ist nicht weniger aufwändig, als das Umbenennen von Spaltennamen, bietet aber dennoch eine Alternative. Mit ```full_join()``` kombinieren wir zwei Datensätze und können im Argument ```by``` angeben, welche Spalten jeweils denselben Inhalt haben. Schreibaufwand hierbei ist, dass gleiche Spaltennamen auch aufgeführt werden müssen, da ansonsten (hier im Beispiel) die Variablen ```distance.x``` und ```distance.y``` gebildet werden. Dies liegt daran, da ```full_join()``` eigentlich dafür gedacht ist, neue/zusätzliche Variablen hinzuzufügen. +-- -In unserem Beispiel würden wir also angeben, dass aus Datensatz ```uniA``` die Spalte ```city``` gleich der Spalte ```City``` aus dem Datensatz ```uniB``` ist. Gleiches gilt für die ```distance``` Variable. +.pull-left-40[ +``` {r full-join} +uniTest2 <- uniA %>% + full_join(uniB, + by = c("city" = "City", #<< + "distance" = "distance" #<< + ) #<< + ) + +head(uniTest2) +``` +] -``` {r full-join, eval=TRUE} +-- + +.pull-right-60[ +.center[ +``` {r full-join-out, eval=TRUE, echo=FALSE} uniTest2 <- uniA %>% full_join(uniB, by = c("city" = "City", @@ -280,17 +640,52 @@ uniTest2 <- uniA %>% head(uniTest2) ``` +] +] -### Zwei Datensätze kombinieren -Dies ist oft bei Mehrebenenansätzen nötig. Man hat Daten auf zwei verschiedenen Ebene und führt diese vor der Analyse in einem Datensatz zusammen, um alle Variablen aus einem Datensatz ansprechen zu können. Hierbei möchten wir Daten zu einem Datensatz hinzufügen, wobei wir eine Variable angeben, die als **Matching**-Variable dient. In diesem Beispiel haben wir im Datensatz ```uniMacro``` noch zusätzliche Variablen zu den jeweiligen Studienorten: Neben ```city``` und ```study``` sind hierin auch ```supervision``` (Betreuungsrelation) und ```maxsem``` (max. Seminargröße) pro Studienort und pro Studienfach eingetragen. +??? -Für das **Mergen** von Datensätzen, kann man je nach Ausgangspunkt ```left_join()``` bzw. ```right_join()``` oder auch ```full_join()``` nutzen. Um die Daten korrekt zu mergen, müssen wir sowohl die Variable ```city``` als auch ```study``` nutzen, da sich die Makro-Variablen eben nach Studienort und Studienfach unterscheiden! Dies geben wir im Argument ```by``` an. Bei ```left_join()``` geben wir den Datensatz, an dem die Daten hinzugefügt werden sollen, per **Piping** weiter. Bei ```right_join()``` werden die Daten an den zweiten Datensatz angehängt. +Dieser Ansatz ist nicht weniger aufwändig, als das Umbenennen von Spaltennamen, bietet aber dennoch eine Alternative. Mit `full_join()` kombinieren wir zwei Datensätze und können im Argument `by` angeben, welche Spalten jeweils denselben Inhalt haben. Schreibaufwand hierbei ist, dass gleiche Spaltennamen auch aufgeführt werden müssen, da ansonsten (hier im Beispiel) die Variablen `distance.x` und `distance.y` gebildet werden. Dies liegt daran, da `full_join()` eigentlich dafür gedacht ist, neue/zusätzliche Variablen hinzuzufügen. +--- + +# Zwei Datensätze kombinieren +Mögliche Anwendungsfälle: + +- mehrere Erhebungen + +- Multi-Level-Daten + +Für das **Mergen** von Datensätzen, kann man je nach Ausgangspunkt `left_join()` bzw. `right_join()` oder auch `full_join()` nutzen. Um die Daten korrekt zu mergen, müssen wir sowohl die Variable `city` als auch `study` nutzen, da sich die Makro-Variablen eben nach Studienort und Studienfach unterscheiden! Dies geben wir im Argument `by` an. Bei `left_join()` geben wir den Datensatz, an dem die Daten hinzugefügt werden sollen, per **Piping** weiter. Bei `right_join()` werden die Daten an den zweiten Datensatz angehängt. + +??? +Dies ist oft bei Mehrebenenansätzen nötig. Man hat Daten auf zwei verschiedenen Ebene und führt diese vor der Analyse in einem Datensatz zusammen, um alle Variablen aus einem Datensatz ansprechen zu können. Hierbei möchten wir Daten zu einem Datensatz hinzufügen, wobei wir eine Variable angeben, die als **Matching**-Variable dient. In diesem Beispiel haben wir im Datensatz `uniMacro` noch zusätzliche Variablen zu den jeweiligen Studienorten: Neben `city` und `study` sind hierin auch `supervision` (Betreuungsrelation) und `maxsem` (max. Seminargröße) pro Studienort und pro Studienfach eingetragen. + +--- + +# Zwei Datensätze kombinieren + +Wir möchten nun die Makrodaten aus `uniMacro` jeweils passend auf Studienort und Studienfach in den Mikrodatensatz hinzufügen, um anschließend ein Multi-Level-Modell zu berechnen. Hierzu nutzen wir `left_join()` und geben im Argument `by` an, dass sowohl `city` als auch `study` als **Matching**-Variablen genutzt werden sollen. + +-- + +.pull-left-40[ +``` {r left-join} +uniMerged <- uni %>% + left_join(uniMacro, + by = c("city", #<< + "study" #<< + ) #<< + ) +uniMerged +``` +] -### Zwei Datensätze kombinieren -Wir möchten nun die Makrodaten aus ```uniMacro``` jeweils passend auf Studienort und Studienfach in den Mikrodatensatz hinzufügen, um anschließend ein Multi-Level-Modell zu berechnen. Hierzu nutzen wir ```left_join()``` und geben im Argument ```by``` an, dass sowohl ```city``` als auch ```study``` als **Matching**-Variablen genutzt werden sollen. +-- -``` {r left-join, eval=TRUE} +.pull-right-60[ +.scrollable[ +``` {r left-join-out, eval=TRUE, echo=FALSE} uniMerged <- uni %>% left_join(uniMacro, by = c("city", @@ -299,10 +694,33 @@ uniMerged <- uni %>% ) uniMerged ``` +] +] +--- + +# Zwei Datensätze kombinieren mit full_join() + +Alternativ geht dies auch mit `full_join()`: + +-- + +.pull-left-40[ +``` {r full-join2} +uniMerged2 <- uni %>% + full_join(uniMacro, #<< + by = c("city", #<< + "study" #<< + ) + ) +uniMerged2 +``` +] -Alternativ geht dies auch mit ```full_join()```: +-- -``` {r full-join2, eval=TRUE} +.pull-right-60[ +.scrollable[ +``` {r full-join2-out, eval=TRUE, echo=FALSE} uniMerged2 <- uni %>% full_join(uniMacro, by = c("city", @@ -311,32 +729,79 @@ uniMerged2 <- uni %>% ) uniMerged2 ``` +] +] + +--- -### Datensätze zusammenführen (neue Variablen hinzufügen) -Will man nur weitere Variablen in einen Datensatz hinzufügen, kann man auch hierfür ```full_join()``` nutzen. Wir haben zum Beispiel in einem weiteren Datensatz aus dem Prüfungsverwaltungssystem vor der Anonymisierung der Daten die geleisteten Creditpoints der Befragtena ausgelesen. Diese haben wir im Datensatz ```points``` getrennt gespeichert und dort ebenfalls eine ID-Variable genutzt, die auf die ID-Variable des Datensatzes ```uni``` matcht. Wir fügen jetzt die Creditpoints dem Datensatz ```uni``` mit ```full_join()``` hinzu. Schauen wir uns zuerst nochmal die zwei Datensätze an: +# Neue Variablen hinzufügen +Will man nur weitere Variablen in einen Datensatz hinzufügen, kann man auch hierfür `full_join()` nutzen. Wir haben zum Beispiel in einem weiteren Datensatz aus dem Prüfungsverwaltungssystem vor der Anonymisierung der Daten die geleisteten Creditpoints der Befragtena ausgelesen. Diese haben wir im Datensatz `points` getrennt gespeichert und dort ebenfalls eine ID-Variable genutzt, die auf die ID-Variable des Datensatzes `uni` matcht. Wir fügen jetzt die Creditpoints dem Datensatz `uni` mit `full_join()` hinzu. Schauen wir uns zuerst nochmal die zwei Datensätze an: + +.pull-left-30[ +.center[ +.scrollable[ ```{r points, eval=TRUE} points - +``` +] +] +] + +.pull-right-70[ +.center[ +.scrollable[ +```{r uni, eval=TRUE} uni ``` -Wir haben zwar in beiden Variablen eine ID-Variable, allerdings ist die Spalte unterschiedlich benannt. Wir können jetzt - wie zuvor oben - wieder im ```by```-Argument dies angeben. Diesmal wollen wir einfach schnell vorher den Spaltennamen in einem der Datensätze anpassen. Dazu nutzen wir einfach ```rename()```. Die Logik in der Funktion ist ```neuer Name = alter Name```. +] +] +] + +-- -``` {r add-points, eval=TRUE} +Wir haben zwar in beiden Variablen eine ID-Variable, allerdings ist die Spalte unterschiedlich benannt. + +--- + +# Neue Variablen hinzufügen + +Wir können jetzt - wie zuvor oben - wieder im `by`-Argument dies angeben. Diesmal wollen wir einfach schnell vorher den Spaltennamen in einem der Datensätze anpassen. Dazu nutzen wir einfach `rename()`. Die Logik in der Funktion ist `neuer Name = alter Name`. Dann sind die Spaltennamen gleich und wir können die Datensätze mergen. + +.pull-left-40[ +``` {r add-points} points <- points %>% - rename(ID = id) + rename(ID = id) #<< -points +uni <- uni %>% + full_join(points, #<< + by = "ID" #<< + ) #<< +uni ``` +] + +-- -Jetzt sind die Spaltennamen gleich und wir können die Datensätze mergen. +.pull-right-60[ +.center[ +.scrollable[ +```{r add-points-out, eval=TRUE, echo=FALSE} +points <- points %>% + rename(ID = id) -```{r final-merge, eval=TRUE} uni <- uni %>% full_join(points, by = "ID" ) uni ``` +] +] +] + +--- + +class: inverse2, center, middle, mline -## Das war's! Nun machen wir ein kleines Quiz! \ No newline at end of file +# Das war's! Nun machen wir ein kleines Quiz! \ No newline at end of file