Neben dem zuvor besprochenen tidyverse
steht mit data.table
ein weiterer R-Dialekt zur Verfügung, der sich immer größerer Beliebtheit erfreut. Im Kern sind data.tables
verbesserte Versionen von data.frames
, die schneller und speichereffizienter arbeiten und mit einer prägnanteren Syntax manipuliert werden können. Das Paket stellt außerdem eine Reihe zusätzlicher Funktionen zum Lesen und Schreiben von tabellarischen Dateien, zum Umformen von Daten zwischen langen und breiten Formaten und zum Verbinden von Datensätzen zur Verfügung.
29.1 Installation
Alle Funktionen sind über das Paket data.table
implementiert, welches wie gewohnt installiert und aktiviert werden kann.
# installiere data.tableinstall.packages("data.table", dependencies=TRUE)
# data.table aktivierenlibrary(data.table)
29.2 Modify-in-Place
Der größte Unterschied besteht darin, dass data.table
die Modify-in-Place-Methode verwendet. Das klassische R
und auch das Tidyverse verwenden die Copy-on-Modify-Methode, welche besagt, dass bei der Manipulation eines Objektes das Ergebnis in einem neuen Objekt gespeichert wird.
# klassisches "Copy-on-Modify"meine.daten %>% mutate(Neu = Alt*10)
Bei oben stehendem Code wird das Objekt meine.daten
nicht verändert. Das Ergebnis der mutate()
-Funktion wird als neues Objekt ausgegeben. Dieses neue Objekt ist eine Kopie der Ursprungsdaten meine.daten
, an welcher die Veränderungen vorgenommen werden.
Mit data.table
wird der Ansatz Modify-in-Place verfolgt.
# Modify-in-Placemeine.daten[, Neu := Alt*10]
Der oben stehende Code erzeugt keine Kopie von meine.daten
. Vielmehr wird das Objekt meine.daten
direkt verändert. Im klassischen R
entspricht diese Vorgehensweise dem Code
meine.daten$Neu <- meine.daten$Alt*10
Durch Modify-in-Place wird data.table
sehr effizient, wenn größere Datenmengen verarbeitet werden sollen. Es kann jedoch auch dazu führen, dass der Code schwieriger zu verstehen ist und überraschende Ergebnisse liefert (insbesondere, wenn ein data.table
innerhalb einer Funktion modifiziert wird).
29.3 Grundlegende Syntax
Die generelle Syntax von data.table
lautet
dt[i, j, by]
wobei
dt
eindata.table
-Objekt ist.i
zum Filtern und für join-Funktionen genutzt wird.j
zum Manipulieren, Transformieren und Zusammenfassen der Daten verwendet wird.by
zum Gruppieren genutzt wird.
Man kann die Syntax lesen als:
„In diesen Zeilen, mache dies, gruppiert nach jenem“.
29.4 Daten einlesen
Der erste Schritt der meisten Datenanalysen besteht darin, Daten in den Speicher zu laden. Wir können die Funktion data.table::fread()
verwenden (das f
steht für fast (schnell)), um reguläre, durch Trennzeichen getrennte Dateien wie txt
- oder csv
-Dateien zu lesen. Diese Funktion ist nicht nur schnell, sondern erkennt automatisch das Trennzeichen und errät die Klasse jeder Spalte sowie die Anzahl der Zeilen in der Datei.
# Daten einlesen mit fread()dt <- fread("data/Befragung22.csv")# anschauenstr(dt)
Classes 'data.table' and 'data.frame': 37 obs. of 6 variables: $ alter : int 20 28 41 34 26 38 28 21 27 26 ... $ geschlecht: chr "weiblich" "weiblich" "männlich" "weiblich" ... $ stifte : int 12 7 1 13 18 25 29 1 2 5 ... $ geburtsort: chr "Düren" "Neuss" "Bonn" "Düsseldorf" ... $ fahrzeit : int 1 45 60 25 15 50 40 60 60 40 ... $ podcast : chr "selten" "selten" "selten" "oft" ... - attr(*, ".internal.selfref")=<externalptr>
Das Objekt dt
gehört sowohl zur Klasse data.frame
als auch zu der neuen Klasse data.table
.
Die Daten können auch direkt über eine URL eingelesen werden.
# lade per URLdt <- fread("https://www.produnis.de/R/data/Befragung22.csv")
Liegen die Daten bereits als data.frame
vor, können sie per as.data.table()
umgewandelt werden.
# lade klassisches Datenframedf <- read.table("https://www.produnis.de/R/data/Datentabelle.txt", header=TRUE)# wandle in data.table umdt2 <- as.data.table(df)
Classes 'data.table' and 'data.frame': 10 obs. of 4 variables: $ Geschlecht: chr "m" "w" "w" "m" ... $ Alter : int 28 18 25 29 21 19 27 26 31 22 $ Gewicht : int 80 55 74 101 84 74 65 56 88 78 $ Groesse : int 170 174 183 190 185 178 169 163 189 184 - attr(*, ".internal.selfref")=<externalptr>
Sollen die Daten von Hand eingegeben werden, wird die Funktion data.table()
verwendet.
# erzeuge von Handdt3 <- data.table(x = 1:10, y = 11:20, z = factor(rep(c("foo", "bar"), 5)) )# anschauenstr(dt3)
Classes 'data.table' and 'data.frame': 10 obs. of 3 variables: $ x: int 1 2 3 4 5 6 7 8 9 10 $ y: int 11 12 13 14 15 16 17 18 19 20 $ z: Factor w/ 2 levels "bar","foo": 2 1 2 1 2 1 2 1 2 1 - attr(*, ".internal.selfref")=<externalptr>
29.5 Daten speichern
Mit der Funktion fwrite()
können data.tables
(aber auch data.frames
) in eine Datei gespeichert werden. Sie funktioniert ähnlich wie write.csv
, ist aber wesentlich schneller. Wird kein Dateiname angegeben, erfolgt die Ausgabe in der Konsole. So kann überprüft werden, was in die Datei geschrieben würde.
# schreibe Objekt "dt2" in die Konsolefwrite(dt2)
Geschlecht,Alter,Gewicht,Groessem,28,80,170w,18,55,174w,25,74,183m,29,101,190m,21,84,185w,19,74,178w,27,65,169w,26,56,163m,31,88,189m,22,78,184
# schreibe Objekt "dt" in datei "dt.csv"fwrite(dt2, "dt2.csv")# schreibe Objekt "dt" in datei "dt.txt"fwrite(dt2, "dt2.txt")
29.6 Fälle filtern mit i
Wir erinnern uns, dass die allgemeine Syntax dt[i, j, by]
lautet. Über den Parameter i
können die Daten gefilter werden, so dass nur bestimmte Fälle berücksichtigt werden. Beispielsweise könnten wir im Objekt dt
nur solche Fälle auswählen, bei denen das Alter größer als 30 ist.
dt[alter > 30]
alter geschlecht stifte geburtsort fahrzeit podcast <int> <char> <int> <char> <int> <char>1: 41 männlich 1 Bonn 60 selten2: 34 weiblich 13 Düsseldorf 25 oft3: 38 weiblich 25 Dinslaken 50 oft4: 38 männlich 5 Donezk 57 manchmal5: 31 weiblich 16 Charkov Ukraine 135 oft6: 36 weiblich 1 Rybnik 90 manchmal7: 45 männlich 1 Gelsenkirchen 85 oft
Dies ist Vergleichbar mit dem klassischen R
-Aufruf
# klassischer R-Befehldt[dt$alter > 30]
alter geschlecht stifte geburtsort fahrzeit podcast <int> <char> <int> <char> <int> <char>1: 41 männlich 1 Bonn 60 selten2: 34 weiblich 13 Düsseldorf 25 oft3: 38 weiblich 25 Dinslaken 50 oft4: 38 männlich 5 Donezk 57 manchmal5: 31 weiblich 16 Charkov Ukraine 135 oft6: 36 weiblich 1 Rybnik 90 manchmal7: 45 männlich 1 Gelsenkirchen 85 oft
Da alle Ausdrücke in i
im Kontext der data.table
ausgewertet werden, müssen wir den (eventuell sehr langen) Namen des Objektes nicht erneut eingeben. Dies ist vor allem bei längeren Ausdrücken sehr bequem.
# erzeuge langen Objektnamenlanger.Objekt.name <- dt
Der klassische R
-Aufruf
# klassischer R-Befehllanger.Objekt.name[langer.Objekt.name$alter > 25 & langer.Objekt.name$geschlecht=="männlich" | langer.Objekt.name$stifte > 30]
alter geschlecht stifte geburtsort fahrzeit podcast <int> <char> <int> <char> <int> <char>1: 41 männlich 1 Bonn 60 selten2: 26 männlich 5 Düsseldorf 40 nie3: 38 männlich 5 Donezk 57 manchmal4: 20 weiblich 32 Wesel 89 nie5: 45 männlich 1 Gelsenkirchen 85 oft
verkürzt sich auf
langer.Objekt.name[alter > 25 & geschlecht=="männlich" | stifte > 30]
alter geschlecht stifte geburtsort fahrzeit podcast <int> <char> <int> <char> <int> <char>1: 41 männlich 1 Bonn 60 selten2: 26 männlich 5 Düsseldorf 40 nie3: 38 männlich 5 Donezk 57 manchmal4: 20 weiblich 32 Wesel 89 nie5: 45 männlich 1 Gelsenkirchen 85 oft
29.7 Fälle sortieren mit i
Dem Parameter i
können auch Funktionen übergeben werden. So lassen sich die Daten beispielsweise über die order()
-Funktion sortieren.
# nehme anderen (kürzeren) Datensatz zur Demonstrationdt2[order(Alter)]
Geschlecht Alter Gewicht Groesse <char> <int> <int> <int> 1: w 18 55 174 2: w 19 74 178 3: m 21 84 185 4: m 22 78 184 5: w 25 74 183 6: w 26 56 163 7: w 27 65 169 8: m 28 80 170 9: m 29 101 19010: m 31 88 189
# absteigenddt2[order(Gewicht, decreasing = TRUE)]
Geschlecht Alter Gewicht Groesse <char> <int> <int> <int> 1: m 29 101 190 2: m 31 88 189 3: m 21 84 185 4: m 28 80 170 5: m 22 78 184 6: w 25 74 183 7: w 19 74 178 8: w 27 65 169 9: w 26 56 16310: w 18 55 174
29.8 Daten verarbeiten mit j
Nachdem der Datensatz mittels i
eventuell vorsortiert und -gefiltert wurde, erfolgen die eigentlichen Operationen über den Parameter j
. So können wir den Mittelwert des Alters der Probanden wie folgt bestimmen:
# Mittelwert des Altersdt[, mean(alter)]
[1] 25.2973
# Mittelwert des Alters der Männerdt[geschlecht == "männlich", mean(alter)]
[1] 29
Innerhalb von j
kann jede Funktion verwendet werden. So könnten wir überprüfen, ob die Variablen fahrzeit
und alter
miteinander korrelieren (ja, das ist quatsch).
# korrelieren alter und fahrzeit? dt[, cor(alter, fahrzeit)]
[1] 0.1504465
Es können auch mehrere Funktionen angewendet werden. Hierfür müssen diese per list()
an den Parameter j
übergeben werden. Auf diese Weise könnten wir Median, Mittelwert und Standardabweichung des Alters der Probanden bestimmen.
# mehrere Funktionen per list()dt[, list(Median = median(alter), Mittelw = mean(alter), Stdabw = sd(alter))]
Median Mittelw Stdabw <int> <num> <num>1: 22 25.2973 6.765373
Da der Parameter j
immer eine Liste erwartet, kann die Funktion list()
mit einem Punkt abgekürzt werden.
# geht auch mit "."dt[, .(Median = median(alter), Mittelw = mean(alter), Stdabw = sd(alter), InterquA = IQR(alter))]
Median Mittelw Stdabw InterquA <int> <num> <num> <num>1: 22 25.2973 6.765373 6
29.9 Daten bearbeiten mit j
Über den Parameter j
können die Daten auch manipuliert werden, ähnlich wie bei der mutate()
-Funktion des Tidyverse. Eine neue Variable kann über die Zeichenkette :=
definiert werden (dem so genannten Walrus Operator (Walross-Operator), der so heisst, weil die Zeichenfolge :=
an die Stoßzähne eines Walrosses erinnert. Das Logo des data.table
-Pakets zeigt eine Robbe, was zur humorvollen Verbindung beigetragen hat).
Mit folgendem Aufruf erzeugen wir eine neue Variable FahrzeitH
, welche die fahrzeit
in Stunden beinhalten soll.
# FahrzeitH in Stundendt[, FahrzeitH := fahrzeit/60]# anzeigenstr(dt)
Classes 'data.table' and 'data.frame': 37 obs. of 7 variables: $ alter : int 20 28 41 34 26 38 28 21 27 26 ... $ geschlecht: chr "weiblich" "weiblich" "männlich" "weiblich" ... $ stifte : int 12 7 1 13 18 25 29 1 2 5 ... $ geburtsort: chr "Düren" "Neuss" "Bonn" "Düsseldorf" ... $ fahrzeit : int 1 45 60 25 15 50 40 60 60 40 ... $ podcast : chr "selten" "selten" "selten" "oft" ... $ FahrzeitH : num 0.0167 0.75 1 0.4167 0.25 ... - attr(*, ".internal.selfref")=<externalptr> - attr(*, "index")= int(0) ..- attr(*, "__geschlecht")= int [1:37] 3 8 10 11 13 31 33 34 37 1 ...
So können wir auch mittels der cut()
-Funktion die Daten klassieren, zum Beispiel das Alter:
dt[, alterK := cut(alter, breaks=c(0,20,25,30,40,50), ordered=TRUE)]# anzeigenstr(dt)
Classes 'data.table' and 'data.frame': 37 obs. of 8 variables: $ alter : int 20 28 41 34 26 38 28 21 27 26 ... $ geschlecht: chr "weiblich" "weiblich" "männlich" "weiblich" ... $ stifte : int 12 7 1 13 18 25 29 1 2 5 ... $ geburtsort: chr "Düren" "Neuss" "Bonn" "Düsseldorf" ... $ fahrzeit : int 1 45 60 25 15 50 40 60 60 40 ... $ podcast : chr "selten" "selten" "selten" "oft" ... $ FahrzeitH : num 0.0167 0.75 1 0.4167 0.25 ... $ alterK : Ord.factor w/ 5 levels "(0,20]"<"(20,25]"<..: 1 3 5 4 3 4 3 2 3 3 ... - attr(*, ".internal.selfref")=<externalptr> - attr(*, "index")= int(0) ..- attr(*, "__geschlecht")= int [1:37] 3 8 10 11 13 31 33 34 37 1 ...
Pro Aufruf kann der Walross-Operator nur einmal verwendet werden. Sollen mehrere Variablen verändert oder hinzugefügt werden, steht die let()
-Funktion bereit. Innerhalb von let()
werden wie gewohnt einfache Gleichheitszeichen verwendet.
# mehrere Manipulationen per let()dt[, let(geschlecht = factor(geschlecht), geburtsort = factor(geburtsort), podcast = factor(podcast, ordered=TRUE, levels=c("nie", "selten", "manchmal", "oft", "immer")))]# anzeigenstr(dt)
Classes 'data.table' and 'data.frame': 37 obs. of 8 variables: $ alter : int 20 28 41 34 26 38 28 21 27 26 ... $ geschlecht: Factor w/ 2 levels "männlich","weiblich": 2 2 1 2 2 2 2 1 2 1 ... $ stifte : int 12 7 1 13 18 25 29 1 2 5 ... $ geburtsort: Factor w/ 26 levels "Bagdad","Bonn",..: 9 21 2 10 8 5 21 7 18 10 ... $ fahrzeit : int 1 45 60 25 15 50 40 60 60 40 ... $ podcast : Ord.factor w/ 5 levels "nie"<"selten"<..: 2 2 2 4 NA 4 4 3 1 1 ... $ FahrzeitH : num 0.0167 0.75 1 0.4167 0.25 ... $ alterK : Ord.factor w/ 5 levels "(0,20]"<"(20,25]"<..: 1 3 5 4 3 4 3 2 3 3 ... - attr(*, ".internal.selfref")=<externalptr> - attr(*, "index")= int(0)
Die Änderungen wurden direkt im Objekt dt
gespeichert.
29.10 data.table kopieren
Eine weitere wesentliche Eigenschaft von data.table
-Objekten besteht darin, dass man sie gesondert kopieren muss. Wir eine data.table
auf klassischem Wege in ein neues Objekt “kopiert”, so erfolgt keine echte Kopie, sondern lediglich ein symbolischer Link auf das ursprüngliche Objekt.
# weise dt einem neuen Objekt zuneu <- dtstr(neu)
Classes 'data.table' and 'data.frame': 37 obs. of 8 variables: $ alter : int 20 28 41 34 26 38 28 21 27 26 ... $ geschlecht: Factor w/ 2 levels "männlich","weiblich": 2 2 1 2 2 2 2 1 2 1 ... $ stifte : int 12 7 1 13 18 25 29 1 2 5 ... $ geburtsort: Factor w/ 26 levels "Bagdad","Bonn",..: 9 21 2 10 8 5 21 7 18 10 ... $ fahrzeit : int 1 45 60 25 15 50 40 60 60 40 ... $ podcast : Ord.factor w/ 5 levels "nie"<"selten"<..: 2 2 2 4 NA 4 4 3 1 1 ... $ FahrzeitH : num 0.0167 0.75 1 0.4167 0.25 ... $ alterK : Ord.factor w/ 5 levels "(0,20]"<"(20,25]"<..: 1 3 5 4 3 4 3 2 3 3 ... - attr(*, ".internal.selfref")=<externalptr> - attr(*, "index")= int(0)
Wir haben das Objekt dt
nur scheinbar in das neue Objekt neu
kopiert. Wenn wir Änderungen am Objekt neu
vornehmen, so sind diese auch im Objekt dt
präsent, weil eben nicht kopiert, sondern nur ein Verweis erstellt wurde.
# erstelle neue Variable in "neu"neu[, kuckuck := fahrzeit * stifte]# die neue Variable ist auch in "dt" enthaltenstr(dt)
Classes 'data.table' and 'data.frame': 37 obs. of 9 variables: $ alter : int 20 28 41 34 26 38 28 21 27 26 ... $ geschlecht: Factor w/ 2 levels "männlich","weiblich": 2 2 1 2 2 2 2 1 2 1 ... $ stifte : int 12 7 1 13 18 25 29 1 2 5 ... $ geburtsort: Factor w/ 26 levels "Bagdad","Bonn",..: 9 21 2 10 8 5 21 7 18 10 ... $ fahrzeit : int 1 45 60 25 15 50 40 60 60 40 ... $ podcast : Ord.factor w/ 5 levels "nie"<"selten"<..: 2 2 2 4 NA 4 4 3 1 1 ... $ FahrzeitH : num 0.0167 0.75 1 0.4167 0.25 ... $ alterK : Ord.factor w/ 5 levels "(0,20]"<"(20,25]"<..: 1 3 5 4 3 4 3 2 3 3 ... $ kuckuck : int 12 315 60 325 270 1250 1160 60 120 200 ... - attr(*, ".internal.selfref")=<externalptr> - attr(*, "index")= int(0)
Dies ist ein häufiger fataler Anfängerfehler, der zum Datenverlust führen kann!
Um das Objekt tatsächlich zu kopieren, muss die Funktion copy()
verwendet werden.
# kopieren dt2 nach neu2neu2 <- copy(dt2)# anzeigenstr(neu2)
Classes 'data.table' and 'data.frame': 10 obs. of 4 variables: $ Geschlecht: chr "m" "w" "w" "m" ... $ Alter : int 28 18 25 29 21 19 27 26 31 22 $ Gewicht : int 80 55 74 101 84 74 65 56 88 78 $ Groesse : int 170 174 183 190 185 178 169 163 189 184 - attr(*, ".internal.selfref")=<externalptr>
# manipulierenneu2[, Kuckuck := Groesse/Gewicht]# dt2 ist unverändertstr(dt2)
Classes 'data.table' and 'data.frame': 10 obs. of 4 variables: $ Geschlecht: chr "m" "w" "w" "m" ... $ Alter : int 28 18 25 29 21 19 27 26 31 22 $ Gewicht : int 80 55 74 101 84 74 65 56 88 78 $ Groesse : int 170 174 183 190 185 178 169 163 189 184 - attr(*, ".internal.selfref")=<externalptr>
29.11 pipen
Innerhalb von data.table
kann auch die Pipe verwendet werden. Wird die R-Base-Pipe |>
verwendet, kann mittels Unterstrich _
auf den weitergeleiteten Datenstrom zugegriffen werden. Bei der Tidyverse-Pipe (eigentlich von magrittr
) mit der Zeichenfolge %>%
muss ein Punkt .
verwendet werden.
Folgende Aufrufe filtern das geschlecht
und pipen den Datenstrom weiter. Anschließend wird nach alter
sortiert.
# Daten pipen mit R_Basedt2[Geschlecht=="m"] |> _[order(Alter)]
Geschlecht Alter Gewicht Groesse <char> <int> <int> <int>1: m 21 84 1852: m 22 78 1843: m 28 80 1704: m 29 101 1905: m 31 88 189
# Daten pipen mit magrittrdt2[Geschlecht=="m"] %>% .[order(Alter)]
Geschlecht Alter Gewicht Groesse <char> <int> <int> <int>1: m 21 84 1852: m 22 78 1843: m 28 80 1704: m 29 101 1905: m 31 88 189
Oder wir erstellen ein linerares Modell und pipen es an die summary()
-Funktion weiter.
dt2[, lm(Gewicht ~ Groesse)] |> summary()
Call:lm(formula = Gewicht ~ Groesse)Residuals: Min 1Q Median 3Q Max -14.9024 -3.4756 -0.3902 1.0915 15.0732 Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) -146.5366 58.3503 -2.511 0.03630 * Groesse 1.2439 0.3265 3.810 0.00516 **---Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1Residual standard error: 8.992 on 8 degrees of freedomMultiple R-squared: 0.6447, Adjusted R-squared: 0.6003 F-statistic: 14.51 on 1 and 8 DF, p-value: 0.005164
Wir können den Ausdruck aber auch direkt in die summary()
-Funktion schreiben.
summary(dt2[, lm(Gewicht ~ Groesse)])
Call:lm(formula = Gewicht ~ Groesse)Residuals: Min 1Q Median 3Q Max -14.9024 -3.4756 -0.3902 1.0915 15.0732 Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) -146.5366 58.3503 -2.511 0.03630 * Groesse 1.2439 0.3265 3.810 0.00516 **---Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1Residual standard error: 8.992 on 8 degrees of freedomMultiple R-squared: 0.6447, Adjusted R-squared: 0.6003 F-statistic: 14.51 on 1 and 8 DF, p-value: 0.005164
29.12 Ergebnisse gruppieren mit by
Über den Paramter by
können die Ergebnisse gruppiert werden.
# gruppiert nach Geschlechtdt[, .(Median = median(alter), Mittelw = mean(alter), Stdabw = sd(alter)), by = geschlecht]
geschlecht Median Mittelw Stdabw <fctr> <num> <num> <num>1: weiblich 21.5 24.10714 5.2517322: männlich 25.0 29.00000 9.617692
Die Ausgabe kann gepipet und weiterverarbeitet werden. In folgendem Beispiel berechnen wir den Variationskoeffizienten (\(sd / \bar x\)) aus den gruppierten Ergebnissen.
dt[, .(Median = median(alter), Mittelw = mean(alter), Stdabw = sd(alter)), by = geschlecht] |> # berechnen _[, VK := Stdabw / Mittelw] |> # anzeigen _[]
geschlecht Median Mittelw Stdabw VK <fctr> <num> <num> <num> <num>1: weiblich 21.5 24.10714 5.251732 0.21784962: männlich 25.0 29.00000 9.617692 0.3316446
Bitte beachten Sie, dass wir in diesem Beispiel die Anzeige der Endergebnisse mittels |> _[]
erzwingen mussten. Dies ist notwendig, wenn per by
gruppierte Ergebnisse weiter manipuliert werden sollen. Data.table
speichert Änderungen durch :=
immer direkt im Objekt, wobei keine Ausgabe der Daten erfolgt. Im vorliegenden Fall von VK := Stdabw / Mittelw
ist diese Speicherung jedoch nicht möglich (ausgegeben wird ja eh nichts), da sich das Endergebnis nicht mehr auf das ursprüngliche Objekt dt
bezieht. In diesem Fall ist es (sogar) möglich und üblich, das Ergebnis wie gewohnt in einem neuen Objekt zu speichern, ohne dass dabei ein symbolischer Link angelegt wird.
neu3 <- dt[, .(Median = median(alter), Mittelw = mean(alter), Stdabw = sd(alter)), by = geschlecht] |> _[, VK := Stdabw / Mittelw] |> _[]# anzeigenneu3
geschlecht Median Mittelw Stdabw VK <fctr> <num> <num> <num> <num>1: weiblich 21.5 24.10714 5.251732 0.21784962: männlich 25.0 29.00000 9.617692 0.3316446
Wir können den letzten Pipevorgang abkürzen, indem wir einfach eckige Klammern []
an unseren Aufruf anhängen.
neu4 <- dt[, .(Median = median(alter), Mittelw = mean(alter), Stdabw = sd(alter)), by = geschlecht] |> _[, VK := Stdabw / Mittelw][]# anzeigenneu4
geschlecht Median Mittelw Stdabw VK <fctr> <num> <num> <num> <num>1: weiblich 21.5 24.10714 5.251732 0.21784962: männlich 25.0 29.00000 9.617692 0.3316446
29.13 Weitere Funktionen aus dem data.table
Paket
Das Paket data.table
bringt zahlreiche eigene Funktionen mit, um typische Aufgabenstellungen effizienter bearbeiten zu können.
29.13.1 Einzigartige bestimmen mit uniqueN
Um zum Beispiel die Anzahl verschiedener Städte innerhalb der Variable geburtsort
zu bestimmen, können wir auf die paketeigene Funktion uniqueN()
zurückgreifen:
# wieviele unterschiedliche Städte sind in "geburtsort"?dt[, uniqueN(geburtsort)]
[1] 26
29.13.2 Anzahl der Fälle mit .N
Mit der Funktion .N
kann die Anzahl der Fälle ermittelt werden.
dt[, .(Anzahl = .N), by = geschlecht]
geschlecht Anzahl <fctr> <int>1: weiblich 282: männlich 9
Mit Hilfe von nrow()
können so prozentuale Anteile berechnet werden.
dt[, .(Anzahl = .N, Prozent = .N/nrow(dt)*100), by = alterK]
alterK Anzahl Prozent <ord> <int> <num>1: (0,20] 9 24.3243242: (25,30] 6 16.2162163: (40,50] 2 5.4054054: (30,40] 5 13.5135145: (20,25] 15 40.540541
Die Ergebnisse können an ggplot()
weitergereicht werden.
# ggplotlibrary(ggplot2)dt[, .(Anzahl = .N, Prozent = .N/nrow(dt)*100), by = alterK] |> ggplot(aes(x=alterK, y=Prozent)) + geom_col(color="black", fill="orchid")
29.13.3 Lange Tabelle erzeugen mit melt()
Mit der Funktion melt() können breite Tabellen in lange (tidy) umgewandelt werden, ähnlich wie mit dplyr::pivot_longer()
. Zur Demonstration verwenden wir die Pflegetabelle von Isfort (2018)
# lade Testdaten load("https://www.produnis.de/R/data/Pflegeberufe.RData")
1999 2001 2003 2005 2007 2009 2011 2013Krankenpflegeassistenz 16624 19061 19478 21537 27731 36481 46517 54371Altenpflegehilfe 55770 52710 49727 45776 48326 47903 47978 48363Kinderkrankenpflege 47779 48203 48822 48519 49080 49307 48291 48937Krankenpflege 430983 436767 444783 449355 457322 465446 468192 472580Altenpflege 109161 124879 141965 158817 178902 194195 208304 227154 2015Krankenpflegeassistenz 64127Altenpflegehilfe 49507Kinderkrankenpflege 48913Krankenpflege 476416Altenpflege 246412
Die Tabelle ist nicht tidy und liegt im breiten Format vor. Ausserdem ist sie von der Klasse matrix
.
# wandle um in data.tablepf <- as.data.table(Pflegeberufe, keep.rownames = "Berufsgruppe")# anzeigenpf
Berufsgruppe 1999 2001 2003 2005 2007 2009 2011 <char> <num> <num> <num> <num> <num> <num> <num>1: Krankenpflegeassistenz 16624 19061 19478 21537 27731 36481 465172: Altenpflegehilfe 55770 52710 49727 45776 48326 47903 479783: Kinderkrankenpflege 47779 48203 48822 48519 49080 49307 482914: Krankenpflege 430983 436767 444783 449355 457322 465446 4681925: Altenpflege 109161 124879 141965 158817 178902 194195 208304 2013 2015 <num> <num>1: 54371 641272: 48363 495073: 48937 489134: 472580 4764165: 227154 246412
Mittels melt()
transformieren wir pf
in eine lange (tidy) Tabelle. Dabei übergeben wir dem Parameter
id.vars
alle Variabelen, welche “Identifikatoren” beinhalten. Damit sind alle Spalten gemeint, die keine konkrekten Messwerte enhalten, sondern weitere bezeichnende Kennwerte. Klassischer Weise sind dies vor allem die Zeilennamen, in unserem Falle alsoBerufsgruppe
. Es können mehrereid.vars
mittelsc()
aneinandergereiht werden.measure.vars
alle Spalten, welche die eigentlichen Messwerte enthalten, in unserem Falle 1999:2015 (alles außerBerufsgruppe
). Wird dieser Parameter leer gelassen, nimmtdata.table
automatisch alle Spalten, die keineid.vars
sind.variable.name
den Name der neuen Spalte, in welche die Bezeichnungen dermeasure.vars
überführt werden sollen, in unserem FallJahr
.value.name
den Name der neuen Spalte, in welche die Werte dermeasure.vars
überführt werden sollen, in unserem FallAnzahl
.
Da wir alle Spalten außer Berufsgruppe melten wollen, kann der Parameter measure.vars
weggelassen werden.
# pf mit melt() tidy machenpf_tidy <- melt(pf, id.vars = "Berufsgruppe", variable.name = "Jahr", value.name = "Anzahl")# anschauenhead(pf_tidy)
Berufsgruppe Jahr Anzahl <char> <fctr> <num>1: Krankenpflegeassistenz 1999 166242: Altenpflegehilfe 1999 557703: Kinderkrankenpflege 1999 477794: Krankenpflege 1999 4309835: Altenpflege 1999 1091616: Krankenpflegeassistenz 2001 19061
29.13.4 Breite Tabelle erzeugen mit dcast()
Mittels dcast()
können lange Tabellen wieder in breite Tabellen transformiert werden, so wie bei dplyr::pivot_wider()
.
Der Aufruf folgt der Semantik:
dcast(Bezeichner ~ Spaltenname, value.var = "Wertename")
wobei
Bezeichner
die Spalten derid.vars
meint.Spaltenname
die Spalte mit dervariable.name
meint.value.var
den Namen der Spalte meint, welche die konkreten Messwerte enthält. Diese muss in Anführungszeichen angegeben werden. Wird dieser Parameter weggelassen, versuchtdata.table
die korrekte Spalte zu erraten (was einfach ist, wenn nur noch eine Spalte übrig bleibt).
# wandle pf_tdiy mit dcast() in breite Tabellepf_wide <- dcast(pf_tidy, Berufsgruppe ~ Jahr, value.var = "Anzahl")# anschauenhead(pf_wide)
Key: <Berufsgruppe> Berufsgruppe 1999 2001 2003 2005 2007 2009 2011 <char> <num> <num> <num> <num> <num> <num> <num>1: Altenpflege 109161 124879 141965 158817 178902 194195 2083042: Altenpflegehilfe 55770 52710 49727 45776 48326 47903 479783: Kinderkrankenpflege 47779 48203 48822 48519 49080 49307 482914: Krankenpflege 430983 436767 444783 449355 457322 465446 4681925: Krankenpflegeassistenz 16624 19061 19478 21537 27731 36481 46517 2013 2015 <num> <num>1: 227154 2464122: 48363 495073: 48937 489134: 472580 4764165: 54371 64127