In aller Regel sind Vorarbeiten am Datensatz notwendig, um Analyseschritte durchzuführen. Diese Schritte sind oft von großer Bedeutung und auch recht zeitintensiv.
Es gibt einige Pakete, die das Bearbeiten von Datensätzen ermöglichen. Eines der bekanntesten und aufgrund seiner Verständlichkeit oft genutzten Pakete ist dplyr
. Die Idee hinter dplyr
ist das zum einen nur Tabellen (data frames oder tibbles) verarbeitet werden und zum anderen die Verarbeitung über das sogenannte pipen erleichtert wird. Der Code wird in dplyr
in eine lange Kette gefasst, was zuerst eventuell unnötig erscheint. Aber dadurch ist der Code gut lesbar und es müssen nur wenige Funktionen gelernt werden, die aber in Kombination die gängigsten Verarbeitungsschritte abbilden können. Durch diese Zerlegung in Teilschritte haben wir in den einzelnen Funktionen einen umgrenzten Funktionsumfang, der leicht verständlich ist. Durch die Kombination mehrer Funktionen können aber auch komplexere Schritte vollzogen werden. Auch liefern Funktionen aus dem Paket dplyr
immer einen tibble (Tabelle) aus.
Die umfangreiche Dokumentation zu dplyr
findet sich hier.
Die häufigsten Funktionen, die mit dplyr
genutzt werden sind die folgenden:
Funktion | Operation |
---|---|
select() | Spalte(n) wählen |
slice() | Zeile(n) wählen |
filter() | Zeile(n) filtern |
arrange() | Zeile(n) ordnen |
mutate() | neue Spalten / Variablen |
summarize() | Werte zusammenfassen |
group_by() | gruppieren |
Diese werden dir nun Schritt für Schritt vorgestellt, bevor wir das piping, das zweite Prinzip von dplyr
kennenlernen. Diese Beispiele sind in gewisserweise künstlich, da man dplyr
fast ausschließlich mit piping nutzt. Für das Verständnis der Funktionen ist es aber hilfreich, diese erstmal vorab kennenzulernen.
In allen Beispiel nutzen wir einen fiktiven Datensatz, der die Studienmotivation (mot
) von Studierenden der Universitäten Gießen, Marburg und Frankfurt abgefragt hat.
Im Datensatz wurde u.a. die Zufriedenheit mit der Demokratie (stfdem
), der Distrikt (district
), das Vertrauen in das Parlament (trstprl
) und das Geschlecht (gndr
) abgefragt.
Das package dplyr
kann einzeln geladen werden, es empfiehlt sich aber das package tidyverse
zu laden, das weitere Pakete wie ggplot2
direkt mitlädt.
# install.packages(
# "tidyverse",
# dependencies = TRUE
# )
library("tidyverse")
Mit der select()
Funktion können einzelne oder mehrere Spalten aus einem Datensatz ausgewählt werden. Die Ausgabe ist immer ein tibble.
Im Beispiel wollen wir uns nur die Variablen Geschlecht (gndr
) und den Distrikt (district
) anzeigen lassen.
head(pss)
## idno district gndr agea edu wkhtot income stfdem stfeco
## 1 10000 Distrikt 1 male 41 ES-ISCED IV 34 7th decile 7 6
## 2 10001 Distrikt 1 male 65 ES-ISCED II 20 6th decile 8 7
## 3 10002 Distrikt 1 male 48 ES-ISCED IV 27 7th decile 6 6
## 4 10003 Distrikt 1 female 49 ES-ISCED V 30 6th decile 5 4
## 5 10004 Distrikt 1 female 48 ES-ISCED IV 29 5th decile 4 5
## 6 10005 Distrikt 1 female 64 ES-ISCED V 30 6th decile 6 6
## trstprl trstprt trstplt trstlgl lrscale
## 1 3 5 4 6 4
## 2 5 5 5 4 3
## 3 4 4 6 5 6
## 4 2 7 4 3 6
## 5 6 6 6 6 2
## 6 1 3 2 4 7
select(
pss,
c(
gndr,
district
)
)
Dagegen können wir mit slice()
einzelne Zeilen ausgeben lassen. So zum Beispiel Zeile \(50\) bis \(55\) oder in Kombination mit der Funktion seq()
jede \(100.\) Zeile.
slice(
pss,
50:55
)
## idno district gndr agea edu wkhtot income stfdem stfeco
## 1 10049 Distrikt 1 female 65 ES-ISCED II 30 4th decile 5 7
## 2 10050 Distrikt 1 female 47 ES-ISCED IV 30 5th decile 5 2
## 3 10051 Distrikt 1 female 57 ES-ISCED III 21 5th decile 4 5
## 4 10052 Distrikt 1 male 42 ES-ISCED IV 27 9th decile 3 5
## 5 10053 Distrikt 1 female 47 ES-ISCED III 38 5th decile 5 5
## 6 10054 Distrikt 1 female 43 ES-ISCED III 42 2nd decile 10 6
## trstprl trstprt trstplt trstlgl lrscale
## 1 6 5 6 7 7
## 2 2 5 7 4 5
## 3 5 6 2 4 3
## 4 5 4 5 5 5
## 5 4 3 0 5 2
## 6 8 5 7 4 3
slice(
pss,
seq(
0,
1000,
100
)
)
## idno district gndr agea edu wkhtot income stfdem stfeco
## 1 10099 Distrikt 1 female 53 <NA> 41 7th decile 5 6
## 2 10199 Distrikt 1 male 58 ES-ISCED III 39 5th decile 6 5
## 3 10299 Distrikt 1 female 45 ES-ISCED III 29 5th decile 4 6
## 4 10399 Distrikt 1 female 63 ES-ISCED III 37 5th decile 7 6
## 5 10499 Distrikt 1 female 56 ES-ISCED IV 38 6th decile 5 5
## 6 10600 Distrikt 1 male 71 ES-ISCED II 44 5th decile 7 7
## 7 10700 Distrikt 1 male 70 ES-ISCED III 30 6th decile 4 6
## 8 10800 Distrikt 1 male 75 ES-ISCED II 48 5th decile 8 6
## 9 10900 Distrikt 1 male 67 ES-ISCED II 48 3rd decile 4 4
## 10 11000 Distrikt 1 female 69 ES-ISCED III 41 5th decile 4 5
## trstprl trstprt trstplt trstlgl lrscale
## 1 1 4 4 8 8
## 2 6 4 4 5 8
## 3 7 3 6 2 5
## 4 4 5 6 6 8
## 5 3 5 4 7 6
## 6 5 4 4 4 7
## 7 3 7 7 3 8
## 8 5 10 4 5 6
## 9 3 3 4 3 5
## 10 6 3 5 4 7
Neben der Auswahl von bestimmten Zeilen (Fällen) oder Spalten (Variablen) können wir mit filter()
den Datensatz eingrenzen. So können wir zum Beispiel uns nur die Fälle anzeigen lassen, die in Distrikt 1 leben.
filter(
pss,
district == "Distrikt 1"
)
Auch können wir mehrere Bedingungen einführen. Zum Beispiel nur Personen, die in Distrikt 5 leben und männlich (male) sind.
filter(
pss,
district == "Distrikt 5" & gndr == "male"
)
Alle bereits bekannten logischen Verbindungen funktionieren auch hier. Zur Erinnerung hier nochmal die logischen Konnektoren:
logisches und: &
logisches oder: |
logisches gleich: ==
logisches ungleich: !=
logisches größer: >
logisches kleiner: <
logisches kleiner gleich: <=
logisches größer gleich: >=
Zur Ordnung von Datensätzen kann die Funktion arrange()
genutzt werden. Hierbei kann man entweder aufsteigend oder absteigend sortieren lassen. Zum Beispiel nach den Arbeitsstunden:
pssAsc <- arrange(
pss,
wkhtot
)
head(pssAsc)
## idno district gndr agea edu wkhtot income stfdem stfeco
## 1 20438 Distrikt 5 male 37 ES-ISCED II 6 6th decile 4 4
## 2 10078 Distrikt 1 male 54 ES-ISCED IV 7 9th decile 6 4
## 3 20249 Distrikt 5 male 48 ES-ISCED IV 7 9th decile 5 5
## 4 10072 Distrikt 1 male 52 ES-ISCED IV 8 9th decile 2 5
## 5 10757 Distrikt 1 male 37 ES-ISCED IV 9 8th decile 4 5
## 6 20103 Distrikt 5 female 25 ES-ISCED IV 9 6th decile 3 0
## trstprl trstprt trstplt trstlgl lrscale
## 1 6 5 4 3 3
## 2 1 7 3 7 9
## 3 4 5 4 6 6
## 4 3 1 4 2 7
## 5 4 6 4 5 7
## 6 1 5 3 4 1
Über die Funktion desc()
innerhalb von arrange()
werden die Fälle absteigend sortiert. desc
steht für descending, also absteigend. Alternativ kann man einfach ein Minuszeichen vor den Variablennamen setzen und erhält ebenfalls absteigend sortierte Fälle.
pssDesc <- arrange(
pss,
desc(wkhtot)
)
head(pssDesc)
## idno district gndr agea edu wkhtot income stfdem stfeco
## 1 40446 Distrikt 10 male 63 <NA> 65 1st decile 5 6
## 2 50618 Distrikt 12 female 51 <NA> 63 2nd decile 5 6
## 3 50494 Distrikt 12 female NA ES-ISCED II 62 2nd decile 3 4
## 4 50491 Distrikt 12 female NA ES-ISCED III 60 4th decile 5 4
## 5 20294 Distrikt 5 male 59 ES-ISCED III 59 6th decile 7 8
## 6 40525 Distrikt 10 male 40 ES-ISCED II 59 4th decile 3 7
## trstprl trstprt trstplt trstlgl lrscale
## 1 2 2 9 5 7
## 2 5 4 6 4 5
## 3 3 3 4 2 1
## 4 3 6 6 6 3
## 5 6 4 7 3 5
## 6 1 3 4 1 4
# Alternativ Minuszeichen vor Variable
pssDesc2 <- arrange(
pss,
-wkhtot
)
Um neue Variablen zu berechnen oder eine Variable zu rekodieren, wird mutate()
verwendet. Wir berechnen im Beispiel eine Variable, die die Differenz zur durchschnittlichen Arbeitszeit innerhalb unserer Erhebung ausgibt.
mutate(
pss,
wkhtotCen = wkhtot - mean(wkhtot, na.rm = TRUE)
)
Wenn wir Dummy- oder kategoriale Variablen neu erschaffen möchten, benötigen wir die Funktion case_when()
zusätzlich. In der Funktion case_when()
gibt man hierarchisch fest, welche Bedingung geprüft und wie rekodiert werden soll. Wir könnten zum Beispiel die Variable district
recoden und aus der character-Variable eine integer-Variable machen. Wichtig hierbei ist, dass die Überprüfung wie in einer if-Bedingung nacheinander geprüft werden. Dabei sollte man von der spezifischten Bedingung zur allgemeinen Bedingung codieren (andernfalls gibt es unsinnige Variablen!).
mutate(
pss,
districtRec = case_when(
district == "Distrikt 1" ~ 1,
district == "Distrikt 5" ~ 5,
district == "Distrikt 7" ~ 7,
district == "Distrikt 10" ~ 10,
district == "Distrikt 12" ~ 12,
)
)
Auch können hierbei mehrere Bedingungen verknüpft werden: Jetzt wollen wir einen Dummy berechnen, der anzeigt, ob Personen in Distrikt 12 leben und weiblich (female) sind.
mutate(
pss,
d12gndr = case_when(
district == "Distrikt 12" & gndr == "female" ~ 1
)
)
In diesem Beispiel haben wir jetzt nur die eine Bedingung für den Wert 1
auf der neuen Variable angegeben. Wie wir sehen, wird dann allen anderen Fälle automatisch NA
zugeordnet. Wir wollen aber allen anderen Fällen den Wert 0
zuordnen. Um jetzt nicht die verschiedenen Kombinationen aufschreiben zu müssen bedienen wir uns eines weiteren Arguments der Funktion case_when()
:
mutate(
pss,
d12gndr = case_when(
district == "Distrikt 12" & gndr == "female" ~ 1,
TRUE ~ 0
)
)
Mit dem Argument TRUE ~ 0
legen wir fest, dass alle anderen Werte den Wert 0
zugeordnet bekommen. So müssen nicht alle anderen Kombinationen als Code geschrieben werden.
Die Funktion summarize()
erlaubt es uns aus Spalten einen Wert zusammenzufassen. So zum Beispiel den Mittelwert einer Spalte.
summarize(
pss,
mean(wkhtot)
)
## mean(wkhtot)
## 1 34.3008
Nutzbar ist jede Funktion, die eine Spalte als Input verlangt: Also unter anderen first()
, last()
, nth()
, n()
, n_distinct()
, IQR()
, min()
, max()
, mean()
, median()
, var()
und sd()
.
Oft haben wir in Datensätzen kategoriale Variablen nach denen wir den Datensatz gruppieren wollen. So könnten wir zum Beispiel den Datensatz nach dem Studienfach gruppieren. Dies machen wir mit der Funktion group_by()
:
group_by(
pss,
gndr
)
Scheinbar hat sich im Datensatz nichts geändert, aber hier ist der wichtige Unterschied zu arrange()
: group_by()
sortiert nicht den Datensatz, sondern gruppiert den Datensatz. Die Ausgabe ändert sich daher nicht. Wenn wir uns zum Beispiel den Mittelwert der Semester nach Studiengang anzeigen lassen möchten, verketten wir group_by()
und summarize()
(weiter unten machen wir das eleganter mit dem Piping-Operator):
summarize(
group_by(
pss,
gndr
),
mean(wkhtot)
)
## # A tibble: 2 × 2
## gndr `mean(wkhtot)`
## <fct> <dbl>
## 1 female 34.5
## 2 male 34.1
Hier sehen wir nun was group_by()
macht: Anstatt eines Mittelwertes erhalten wir hier nun vier Mittelwerte (für jeden Studiengang im Datensatz einen). Wichtig: Das Gruppieren sollte immer im Nachfolgenden mit der Funktion ungroup()
gelöst werden.