Ich zeige mal ein Beispiel mit dem data.table package.
Zuerst einmal brauchen wir einpaar Daten, da ja kein
reproduzierbares Beispiel geliefert wurde:
Der iris Datensatz besteht auch vier numerischen Variablen ["Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width"] und einer Kategorischen ["Species"]. Es sind Längenmessungen von Blütenblättern für 3 Irisarten. Ziel ist das den Mittelwert und Konfidenzinterval je Art und für jede Variable zu bestimmen.
Zunächst laden wir data.table und machen aus dem iris-Daten ein data.table:
Das Konfidenzinterval errechne ich aus dem Standardfehler (SE). Da es dafür keine Funktion in R gibt, schreibe ich mir selbst eine:
Code: Alles auswählen
# function for standard error
se <- function(x) sd(x) / sqrt(length(x))
Jetzt zeige ich zunächst mal wie man den Mittelwert für eine Variable berechnen kann:
Code: Alles auswählen
iris[ , list(mean.Sepal.Length = mean(Sepal.Length)),
by = Species]
Das liest sich also: Für jede Gruppe in "Species" berechne mir den Mittelwert von "Sepal.Length" und speichere das in einer Spalte "mean.Sepal.Length".
Das lässt sich auf mehrere Funktionen ausweiten (hier das Konfindenzinterval, sowie min und max):
Code: Alles auswählen
iris[ ,list(mean = mean(Sepal.Length),
upr = mean(Sepal.Length) + qnorm(0.975) * se(Sepal.Length),
lwr = mean(Sepal.Length) - qnorm(0.975) * se(Sepal.Length),
min = min(Sepal.Length),
max = max(Sepal.Length)
),
by = Species]
Wir müssen auch nicht unbedingt die se() Funktion von oben verwenden, sonder können da auch direkt in data.table machen:
Code: Alles auswählen
iris[ ,list(mean = mean(Sepal.Length),
upr = mean(Sepal.Length) + qnorm(0.975) * sd(Sepal.Length) / sqrt(.N),
lwr = mean(Sepal.Length) - qnorm(0.975) * sd(Sepal.Length) / sqrt(.N)
),
by = Species]
.N ist hier eine schnelle Abkürzung für length(x).
OK, aber wir haben 4 variablen. wie machen wir das auch einmal?
Man könnte alles für jede Variable einzeln tippen:
Code: Alles auswählen
iris[ , list(mean.Sepal.Length = mean(Sepal.Length),
mean.Sepal.Width = mean(Sepal.Width),
mean.Petal.Length = mean(Petal.Length),
mean.Petal.Width = mean(Petal.Width)),
by = Species]
Das ist nicht falsch, aber was macht man bei 100 Variablen?
Wendet man für jede Gruppen und Variable an. Das ist aber ein ziemliches Monstrum durch, dass man erst mal durchsteigen muss.
Das lapply(.SD, mean) ist hier die Crux.
Hier eine Erklärung
.SD steht für "Subset of Data" . Das gibt einfach nur den kompletten Datenframe zurück, aber in Stücke geteilt für jede Art / Gruppe:
Das mit dem lapply ist ein toller Kniff.
lapply wendet eine Funktion (in dem Fall "mean") auf jeden Eintrag einer Liste an (deshalb
lapply, list*apply).
.SD gibt uns einen data.frame und ein data.frame ist eine spezielle Art von Listen und jede Spalte ist eigentlich eine Listeneintrag.
Wenn wir lapply auf einen data.frame anwenden, bekommen wir z.b. den Mittelwert für jede Spalte insgesamt:
Code: Alles auswählen
# umwandelt zurück zu data.frame
iris_df <- data.frame(iris)
lapply(iris_df, mean)
Die Warnung die wir bekommen ist klar, weil wir nur Art drin haben und aus kategorischen variablen können wir kein Mittelwert berechnen [Lösung wäre die letzte Spalte auszuklammern.
heißt also:
Für jede Art ('species') berechnen den Mittelwert für jede Variable.
Mit .SDCols können wir die Spalten einschränken:
Code: Alles auswählen
iris[ , lapply(.SD, mean),
by = Species,
.SDcols = c('Sepal.Width', 'Petal.Width')]
Klinkt kompliziert? Ist es auch! data.table zu lernen braucht Zeit. Wenn man die invertiert hat, rentiert es sich aber über lange Sicht hin. Vorallem wenn man mir großen Datenmengen zu tun hat ist data.table unschlagbar was die Performanz anbelangt.
Aber was ist nun wenn wir Mittelwert und Konfidenzintervall für jede Gruppe und jede Variable haben wollen?
Nun, das ist am kompliziertesten...
Ich schreibe mir mal wieder eine Funktion, die Mittelwert udn Konfindezintervall für einen Vektor / Spalten berechnet:
Code: Alles auswählen
my_summary = function(x) list(mean = mean(x),
upr = mean(x) + qnorm(0.975) * se(x),
lwr = mean(x) - qnorm(0.975) * se(x))
Das für eine Variable / Spalte anzuwenden ist einfach und funktioniert wie oben beschrieben:
Und mittels .SD geht das auch für alle Spalten:
nachteil hierbei ist, dass wir nicht mehr wissen was Mittelwert und Konfidenzinterval ist. Ein einfach Lösung wäre einfach eine Spalten hinzuzufügen:
Code: Alles auswählen
(res <- iris[ , lapply(.SD, my_summary),
by = Species][ , type := c('mean', 'upr', 'lwr')])
Ich gebe zu, ist etwas unschön. Die reihenfolge ergibt sich aus der Reihenfolge un my_summary()
Ein weitere Möglichkeit wäre dieses Monstrum (was ich nicht erklären werde):
Code: Alles auswählen
iris[ , as.list(unlist(lapply(.SD, my_summary))),
by = Species]
Seht ihr den Unterschied? Finde ich aber auch nicht zufrieden stellend, das die Spaltennamen genutzt werden um Information zu kodieren.
Vielleicht weiß ja jemand eine elegantere Lösung?
So, genug Einführung in das Daten aggregieren mit data.table.
Weitere möglichkeiten wären das dplyr package, plyr, doBy und viele mehr. Vielleicht zeigt ja jemand anderes wie man das da machen kann.