20.0.1 apply()
Eine wichtige Funktion zur Datenmanipulation ist die Funktion apply()
(wie auch ihre Tochterfunktionen lapply()
, sapply()
und tapply()
). Mit ihr können beliebige Funktionen auf die Reihen und/oder Spalten eines Datenframes (oder einer Matrix) angewendet werden. Dadurch erlaubt sie es in vielen Fällen, Loop-Schleifen und Redundanzen zu vermeiden.
Zur Verdeutlichung sei folgendes Beispiel gegeben:
# erzeuge Testdatensatzdf <- data.frame(Name = c("Hans", "Gerda", "Kurt", "Maria"), Alter = c(56, 59, 58, 58), Groesse = c(178, 172, 181, 166), Gewicht = c(105, 67, 95, 89))# anzeigendf
Name Alter Groesse Gewicht1 Hans 56 178 1052 Gerda 59 172 673 Kurt 58 181 954 Maria 58 166 89
Angenommen, wir wollen die Mittelwerte der drei numerischen Variablen bestimmen, dann könnten wir so vorgehen:
mean(df$Alter)
[1] 57.75
mean(df$Groesse)
[1] 174.25
mean(df$Gewicht)
[1] 89
Die Funktion apply()
erlaubt es, die Werte in nur einem Aufruf zu erzeugen, indem die Funktion mean()
auf die Werte jeder Spalte angewendet wird.
apply(df[,-1], MARGIN=2, FUN=mean)
Alter Groesse Gewicht 57.75 174.25 89.00
Wir übergeben der Funktion zunächst das Datenframe df
ohne die Spalte Name
(df[,-1]
). Mit dem Parameter MARGIN
wird festgelegt, wie apply()
auf das Datenframe schauen soll:
MARGIN=1
- Reihe für ReiheMARGIN=2
- Spalte für SpalteMARGIN=3
- beides zusammen
Über den Parameter FUN
wird die zu verwendende Funktion (in diesem Falle mean()
) ohne Klammern angegeben.
Möchten wir nun die Standardabweichung der drei Variablen berechnen, lautet der Aufruf entsprechend:
apply(df[,-1], MARGIN=2, FUN=sd)
Alter Groesse Gewicht 1.258306 6.652067 16.083117
Es ist wichtig, die nicht-numerische Variable Name
vor dem apply()
-Aufruf herauszufiltern. Denn wenn die zu verwendende Funktion bei nur einer Reihe fehl schlägt, liefert apply()
nur eine Reihe NA
s und Warnmeldungen zurück.
# aufruf mit Spalte "Name" schlägt fehlapply(df, MARGIN=2, FUN=mean)
Warning in mean.default(newX[, i], ...): Argument ist weder numerisch nochboolesch: gebe NA zurückWarning in mean.default(newX[, i], ...): Argument ist weder numerisch nochboolesch: gebe NA zurückWarning in mean.default(newX[, i], ...): Argument ist weder numerisch nochboolesch: gebe NA zurückWarning in mean.default(newX[, i], ...): Argument ist weder numerisch nochboolesch: gebe NA zurück
Name Alter Groesse Gewicht NA NA NA NA
Möchten wir die Werte der Probanden beispielsweise aufaddieren, lassen wir per apply()
die Funktion sum()
reihenweise über die Daten laufen. Hierzu setzen wir den Parameter MARGIN=1
.
# laufe pro-Reihe über die Datenapply(df[,-1], MARGIN=1, FUN=sum)
[1] 339 298 334 313
Wir können jede Funktion per apply()
auf die Variablen loslassen:
# Wurzel aus jedem Wert ziehenapply(df[,-1], MARGIN=2, sqrt)
Alter Groesse Gewicht[1,] 7.483315 13.34166 10.246951[2,] 7.681146 13.11488 8.185353[3,] 7.615773 13.45362 9.746794[4,] 7.615773 12.88410 9.433981
Da wir wissen, dass der Parameter MARGIN
an zweiter Stelle kommt, müssen wir ihn dort nicht immer ausschreiben, sondern können einfach 1
, 2
oder 3
schreiben.
# Häufigkeitstabelle für jede Variable# MARGIN=2 muss nicht ausgeschrieben werdenapply(df[,-1], 2, FUN=jgsbook::freqTable)
$Alter Wert Haeufig Hkum Relativ Rkum1 56 1 1 25 252 58 2 3 50 753 59 1 4 25 100$Groesse Wert Haeufig Hkum Relativ Rkum1 166 1 1 25 252 172 1 2 25 503 178 1 3 25 754 181 1 4 25 100$Gewicht Wert Haeufig Hkum Relativ Rkum1 67 1 1 25 252 89 1 2 25 503 95 1 3 25 754 105 1 4 25 100
20.0.2 Funktionsparameter spezifizieren
Parameter für die auszuführende Funktion können einfach mit einem Komma angehängt werden.
# Führen quantile(x, type=6) ausapply(df[,-1], MARGIN=2, FUN=quantile, type=6)
Alter Groesse Gewicht0% 56.00 166.00 67.025% 56.50 167.50 72.550% 58.00 175.00 92.075% 58.75 180.25 102.5100% 59.00 181.00 105.0
Das funktioniert auch mit mehreren Parametern.
# Führen quantile(x, type=6) ausapply(df[,-1], MARGIN=2, FUN=quantile, type=6, probs=c(0.1, 0.6, 0.8))
Alter Groesse Gewicht10% 56 166 6760% 58 178 9580% 59 181 105
Es ist möglich, die Parameter, welche an die auszuführende Funktion übergeben werden müssen, aus dem Datenframe selbst zu ziehen. Erzeugen wir zur Verdeutlichung folgendes Datenframe:
# Testdatendf <- data.frame(Name = c("Jerome", "Nadine", "Dominik", "Ella"), Geschwindigkeit = c(120, 25, 32, 80), Strecke = c(200, 34, 50, 100))# anzeigendf
Name Geschwindigkeit Strecke1 Jerome 120 2002 Nadine 25 343 Dominik 32 504 Ella 80 100
Jetzt programmieren wir die Funktion zeit()
, welche ausrechnet, wie lange die Personen für die Strecke mit der angegebenen Geschwindigkeit benötigt haben. Die Funktion nimmt die Werte velo
(Geschwindigkeit) und dist
(Strecke) entgegen.
# Funktion programmierenzeit <- function(velo, dist){ # rechne Distanz / Geschwindigkeit # runde auf 2 Stellen zeit <- round(dist/velo, 2) return(zeit)}
Um die Funktion zeit()
auf jede Reihe des Datenframes anzuwenden, wobei die Werte von Geschwindigkeit
und Strecke
jeweils als Parameter übergeben werden, müssen wir zunächst per MARGIN=1
angeben, dass apply()
die Daten Reihe-für-Reihe verarbeiten soll.
Der Trick besteht darin, im Paramter FUN
eine temporäre Funktion function(line)
anzugeben. Diese ruft ihrerseits die Funktion zeit()
(diesmal mit Klammern) auf, und übergibt die Parameter per line("SPALTENNAME")
.
# Übergebe die Werte an die Funktion zeit()apply(df[,-1], MARGIN=1, function(line) zeit(line["Geschwindigkeit"], line["Strecke"]))
[1] 1.67 1.36 1.56 1.25
Wenn die Parameter dist
und velo
mit ihrem Namen angegeben werden, ist die Reihenfolge der Parameter egal.
# Parameter dist und velo direkt angebenapply(df[,-1], MARGIN=1, FUN=function(line) zeit(dist=line["Strecke"], velo=line["Geschwindigkeit"]))
[1] 1.67 1.36 1.56 1.25
Als temporäre Hilfsfunktionen können verschiedene Arten verwendet werden:
function(x)
- übergibt jeden Wert einzeln und nacheinander jeweils als Parameter an die Funktion, wobeix
für spezifische Parameter referenziert werden kann (siehe Abschnitt 20.0.3 für ein Beispiel).function(line)
- übergibt die Werte der gesamte Datenreihe als Parameter an die Funktion, wobei die einzelnen Variablenwerte perline["SPALTENNAME"]
referenziert werden können.
In einem weiteren Beispiel wollen wir Fallzahlen für spezifische Werte von e
, P
und dem Konfidenzlevel berechnen (siehe Abschnitt 32.12.2). Hierzu nutzen wir die Funktion samplingbook::sample.size.prop()
, wobei die Erweiterung samplingbook::sample.size.prop()$n
nur die Fallzahlen zurückgibt.
# beispielhafter Funktionsaufrufsamplingbook::sample.size.prop(e=0.05, P=0.65, level=0.95, N=1500)$n
[1] 284
# Erzeuge Kombinationen von e, P und leveldf <- data.frame(e = c(0.05, 0.04, 0.03), P = c(0.65, 0.6, 0.5), level = c(0.9, 0.95, 0.99))# anzeigendf
e P level1 0.05 0.65 0.902 0.04 0.60 0.953 0.03 0.50 0.99
# Berechne Fallzahlen für jede Reihe (Kombination)apply(df, 1, function(line) samplingbook::sample.size.prop(e = line["e"], P = line["P"], level = line["level"])$n )
[1] 247 577 1844
20.0.3 sapply()
Die Tochterfunktion sapply()
kann auf Vektoren angewendet werden.
# erzeuge einen Vektor mit 3 Werten für NN <- c(10, 30, 100)# ziehe für jeden Wert die Wurzelsapply(N, sqrt)
[1] 3.162278 5.477226 10.000000
Wenn die verwendete Funktion Wertereihen zurückgibt, wird das Ergebnis als Liste zurückgegeben.
# erzeuge jewils N Zufallszahlensapply(N, sample)
[[1]] [1] 2 4 10 9 5 1 7 8 3 6[[2]] [1] 7 20 21 24 30 11 4 28 3 6 9 14 29 2 5 25 8 1 17 18 12 10 19 26 13[26] 22 23 15 16 27[[3]] [1] 75 89 14 90 29 67 30 19 5 31 53 57 59 33 69 64 18 36 [19] 49 51 96 40 50 84 7 87 32 4 11 98 13 92 55 93 85 56 [37] 37 41 42 2 88 46 20 15 76 95 97 48 12 63 71 28 60 17 [55] 22 80 54 44 83 65 77 78 24 10 16 26 72 25 27 68 39 91 [73] 1 35 66 8 43 100 9 62 74 6 99 23 73 81 70 94 47 86 [91] 82 45 38 34 61 3 79 52 58 21
Da die Funktion sample()
jeweils einen Datenvektor zurückgibt, liegt das Ergebnis als Liste vor.
Auch bei sapply()
werden Funktionsparameter per Komma angehängt.
# erzeuge jewils N Zufallszahlen# mit doppeltensapply(N, sample, replace=TRUE)
[[1]] [1] 10 3 6 4 6 8 8 7 5 4[[2]] [1] 15 2 27 24 9 13 21 22 17 17 15 28 28 6 27 27 17 20 2 6 1 11 27 28 5[26] 17 1 11 6 22[[3]] [1] 22 74 72 6 32 46 53 61 60 5 80 88 47 20 5 32 43 2 [19] 44 94 73 15 11 86 93 62 99 24 6 15 20 24 36 19 72 49 [37] 67 26 1 52 11 85 56 11 70 20 16 90 83 22 68 76 78 22 [55] 8 1 88 52 73 77 78 22 3 14 57 42 19 69 83 39 38 67 [73] 99 64 56 26 53 65 58 6 57 15 63 100 33 92 57 64 53 60 [91] 94 23 71 46 29 25 85 46 5 79
Sollen die Werte des Ausgangsvektors als spezifischer Parameter der auszuführenden Funktion übergeben werden, muss wieder auf eine temporäre Hilfsfunktion function(x)
zurückgegriffen werden. Im folgenden Aufruf sollen die Werte des Vektors N
jeweils als Paramenter N
an die Funktion samplingbook::sample.size.mean()
übergeben werden.
# Rufe für jeden Wert x im Vektor N die Funktion auf, # wobei Parameter N=x istsapply(N, FUN=function(x) samplingbook::sample.size.mean(e=0.05, S=0.3, level=0.90, N=x) )
[,1] [,2] [,3] call list,4 list,4 list,4n 10 23 50
Per samplingbook::sample.size.mean()$n
wird nur die benötigte Fallzahl zurückgegeben. Diese Anweisung können wir auch auf sapply()
übertragen:
# mit $n nur Fallzahlensapply(N, FUN=function(x) samplingbook::sample.size.mean(e=0.05, S=0.3, level=0.90, N=x)$n )
[1] 10 23 50
20.0.4 lapply()
Die Funktion lapply()
liefert ihr Ergebnis immer als Liste zurück, wobei für jedes x
ein eigener Listeneintrag erzeugt wird.
# erzeuge einen Vektor mit 3 Werten für NN <- c(10, 30, 100)# ziehe für jeden Wert die Wurzellapply(N, sqrt)
[[1]][1] 3.162278[[2]][1] 5.477226[[3]][1] 10
Mit der Funktion unlist()
kann die Liste in Vektoren überführt werden.
unlist(lapply(N, sqrt))
[1] 3.162278 5.477226 10.000000
20.0.5 tapply()
Mit der Tochterfunktion tapply()
lassen sich gruppierte Auswertungen erzeugen. Die Gruppierung erfolgt anhand eines Factors im Datenframe.
# erzeuge Testdatendf <- data.frame(Geschlecht = factor(c("m", "w", "m", "m", "w", "w", "d", "d", "d")), Alter = c(45, 34, 46, 41, 31, 29, 24, 25, 26))# anzeigendf
Geschlecht Alter1 m 452 w 343 m 464 m 415 w 316 w 297 d 248 d 259 d 26
Angenommen, wir möchten nun die Mittelwerte des Alters in Abhängigkeit zum Geschlecht bestimmen, so können wir tapply()
verwenden.
tapply(df$Alter, df$Geschlecht, mean)
d m w 25.00000 44.00000 31.33333
Wie bei den Schwesterfunktionen lassen sich weitere Parameter per Komma übergeben.
# Berecne IQR so wie SPSS (type=6)tapply(df$Alter, df$Geschlecht, IQR, type=6)
d m w 2 5 5