Gruppierungsfunktionen (tapply, durch, Aggregat) und die * an Familie
Frage
Jedes Mal, wenn ich etwas „Karte“ py in R tun will, versuche ich in der Regel eine Funktion in der apply
Familie zu verwenden.
Allerdings habe ich nie ganz verstanden, die Unterschiede zwischen ihnen - wie {sapply
, lapply
usw.} gelten die Funktion zum Ein- / gruppiert Eingang, wie die Ausgabe aussehen wird, oder was auch der Eingang sein kann -. so dass ich nur gehen oft durch sie alle, bis ich bekommen, was ich will
Kann jemand erklären, wie zu verwenden, die man, wenn?
Mein aktuelles (wahrscheinlich falsch / unvollständig) Verständnis ist ...
-
sapply(vec, f)
: Eingang ist ein Vektor. Ausgang ist ein Vektor / Matrix, wo Elementi
istf(vec[i])
, so dass Sie eine Matrix, wennf
einen Multi-Element-Ausgang -
lapply(vec, f)
: gleiche wiesapply
, aber Ausgang ist eine Liste -
apply(matrix, 1/2, f)
: Eingang ist eine Matrix. Ausgang ist ein Vektor, in dem Elementei
ist f (Zeile / Spalte der Matrix i) -
tapply(vector, grouping, f)
: Ausgang ist eine Matrix / Anordnung, wobei ein Element in der Matrix / Anordnung ist der Wert vonf
bei einer Gruppierungsg
des Vektors undg
wird auf die Zeile / Spalte-Namen geschoben -
by(dataframe, grouping, f)
: Lassen Sieg
eine Gruppierung sein. giltf
zu jeder Spalte der Gruppe / Datenrahmen. drucken ziemlich die Gruppierung und den Wert vonf
bei jeder Spalte. -
aggregate(matrix, grouping, f)
. Ähnlich wieby
, aber anstatt ziemlich Drucken der Ausgabe, Aggregat-Sticks alles in einen Datenrahmen
Side Frage: ich noch nicht gelernt plyr oder reshape haben - würde plyr
oder reshape
alle diese vollständig ersetzen
Lösung
R hat viele * an Funktionen, die in den Hilfedateien geschickt beschrieben sind (z ?apply
). Es gibt genug von ihnen aber, dass Anfang Benutzer Schwierigkeiten haben, zu entscheiden, welche man für ihre Situation geeignet ist oder sogar zu erinnern sie alle. Sie können ein allgemeines Gefühl haben, dass „Ich soll ein * wurde mit der Funktion gilt hier“, aber es kann schwierig sein, sie zuerst alle gerade zu halten.
Trotz der Tatsache, (festgestellt in anderen Antworten), dass ein großer Teil der Funktionalität des * anwenden Familie durch die äußerst beliebte plyr
Paket abgedeckt wird, bleiben die Grundfunktionen nützlich und wissenswert.
Diese Antwort soll als eine Art handeln Wegweiser für neue Benutzer zu Hilfe, um sie auf den richtigen zu lenken * Funktion für ihr besonderes Problem anzuwenden. Beachten Sie, das ist nicht soll einfach regurgitate oder die R-Dokumentation ersetzen! Die Hoffnung ist, dass diese Antwort hilft Ihnen, welche * anwenden Funktion passt Ihre Situation zu entscheiden, und dann ist es an Ihnen, um es weiter zu erforschen. Mit einer Ausnahme, die Leistungsunterschiede werden nicht angesprochen werden.
-
Anwendung - Wenn Sie eine Funktion zu den Zeilen oder Spalten anwenden eine Matrix (und höherdimensionalen Analoga); im Allgemeinen nicht ratsam, für Datenrahmen, wie es zunächst zu einer Matrix zwingen wird.
# Two dimensional matrix M <- matrix(seq(1,16), 4, 4) # apply min to rows apply(M, 1, min) [1] 1 2 3 4 # apply max to columns apply(M, 2, max) [1] 4 8 12 16 # 3 dimensional array M <- array( seq(32), dim = c(4,4,2)) # Apply sum across each M[*, , ] - i.e Sum across 2nd and 3rd dimension apply(M, 1, sum) # Result is one-dimensional [1] 120 128 136 144 # Apply sum across each M[*, *, ] - i.e Sum across 3rd dimension apply(M, c(1,2), sum) # Result is two-dimensional [,1] [,2] [,3] [,4] [1,] 18 26 34 42 [2,] 20 28 36 44 [3,] 22 30 38 46 [4,] 24 32 40 48
Wenn Sie Zeilen / Spalten-Mittel oder Summen für eine 2D-Matrix wollen, achten Sie darauf, untersuchen die hochoptimierte, blitzschnelle
colMeans
,rowMeans
,colSums
,rowSums
. -
lapply - Wenn Sie eine Funktion auf jedes Element eines anzuwenden Liste der Reihe nach und eine Liste zurück.
Dies ist das Zugpferd von vielen der anderen * an Funktionen. Schälen zurück, ihren Code und Sie werden oft
lapply
darunter finden.x <- list(a = 1, b = 1:3, c = 10:100) lapply(x, FUN = length) $a [1] 1 $b [1] 3 $c [1] 91 lapply(x, FUN = sum) $a [1] 1 $b [1] 6 $c [1] 5005
-
sapply - Wenn Sie eine Funktion auf jedes Element eines anzuwenden Liste wiederum, aber Sie wollen eine Vektor zurück, anstatt eine Liste.
Wenn Sie sich
unlist(lapply(...))
, Stop eingeben und prüfen,sapply
.x <- list(a = 1, b = 1:3, c = 10:100) # Compare with above; a named vector, not a list sapply(x, FUN = length) a b c 1 3 91 sapply(x, FUN = sum) a b c 1 6 5005
In fortgeschritteneren Anwendungen von
sapply
wird es versuchen, die zwingen führen zu einer multidimensionalen Array, gegebenenfalls. Zum Beispiel, wenn unsere Funktion zurück Vektoren der gleichen Länge,sapply
wird sie als Spalten einer Matrix verwendet werden:sapply(1:5,function(x) rnorm(3,x))
Wenn unsere Funktion eine 2-dimensionale Matrix zurückgibt, wird
sapply
tun im Wesentlichen die gleiche Sache, die jeweils zurück Matrix als eine einzige lange Vektor Behandlung:sapply(1:5,function(x) matrix(x,2,2))
Wenn wir nicht angeben
simplify = "array"
, in welchem ??Fall sie die einzelnen Matrizen verwenden ein mehrdimensionales Array zu erstellen:sapply(1:5,function(x) matrix(x,2,2), simplify = "array")
Jede dieser Verhaltensweisen ist natürlich abhängig von unserer Funktion Vektoren oder Matrizen der gleichen Länge oder Dimension zurück.
-
vapply - Wenn Sie
sapply
verwenden möchten, aber vielleicht müssen Squeeze etwas mehr Geschwindigkeit aus Ihrem Code.Für
vapply
, geben Sie im Grunde R ein Beispiel, welche Art von Sache Ihre Funktion gibt, die einige Zeit Nötigung zurück sparen Werte in einem einzigen Atom-Vektor passen.x <- list(a = 1, b = 1:3, c = 10:100) #Note that since the advantage here is mainly speed, this # example is only for illustration. We're telling R that # everything returned by length() should be an integer of # length 1. vapply(x, FUN = length, FUN.VALUE = 0L) a b c 1 3 91
-
mapply - Für, wenn Sie mehrere Datenstrukturen (z Vektoren, Listen), und Sie wollen eine Funktion auf die ersten Elemente anzuwenden jeder, und dann werden die zweiten Elemente jeder usw., Nötigung das Ergebnis auf einen Vektor / Array wie in
sapply
.Dies ist multivariate in dem Sinne, dass Ihre Funktion übernehmen müssen mehrere Argumente.
#Sums the 1st elements, the 2nd elements, etc. mapply(sum, 1:5, 1:5, 1:5) [1] 3 6 9 12 15 #To do rep(1,4), rep(2,3), etc. mapply(rep, 1:4, 4:1) [[1]] [1] 1 1 1 1 [[2]] [1] 2 2 2 [[3]] [1] 3 3 [[4]] [1] 4
-
Karte -. Ein Wrapper zu
mapply
mitSIMPLIFY = FALSE
, so gewährleistet ist, eine Liste zurückMap(sum, 1:5, 1:5, 1:5) [[1]] [1] 3 [[2]] [1] 6 [[3]] [1] 9 [[4]] [1] 12 [[5]] [1] 15
-
rapply - Fürwenn Sie eine Funktion auf jedes Element einer verschachtelten Liste Struktur rekursiv anzuwenden.
Um Ihnen eine Vorstellung davon, wie ungewöhnlich
rapply
ist, dass ich es vergessen, als erste Antwort Posting! Natürlich, ich bin sicher, dass viele Leute es verwenden, aber YMMV.rapply
am besten mit einer benutzerdefinierten Funktion dargestellt anwenden:# Append ! to string, otherwise increment myFun <- function(x){ if(is.character(x)){ return(paste(x,"!",sep="")) } else{ return(x + 1) } } #A nested list structure l <- list(a = list(a1 = "Boo", b1 = 2, c1 = "Eeek"), b = 3, c = "Yikes", d = list(a2 = 1, b2 = list(a3 = "Hey", b3 = 5))) # Result is named vector, coerced to character rapply(l, myFun) # Result is a nested list like l, with values altered rapply(l, myFun, how="replace")
-
tapply - Für, wenn Sie wollen, eine Funktion Untergruppen anzuwenden eines Vektor und die Teilmengen werden von einem anderen Vektor definiert ist, in der Regel ein Faktor.
Das schwarze Schaf der Familie * anwenden, der Arten. Die Verwendung der Hilfedatei der Ausdruck „ragged array“ kann ein Bit sein verwirrend, aber es ist tatsächlich ganz einfach.
Ein Vektor:
x <- 1:20
Ein Faktor (von gleicher Länge!), Die Gruppen:
y <- factor(rep(letters[1:5], each = 4))
Fügen Sie die Werte in
x
innerhalb jeder Untergruppe vony
definiert up:tapply(x, y, sum) a b c d e 10 26 42 58 74
Komplexere Beispiele können behandelt werden, bei denen die Untergruppen definiert sind, durch die einzigartige Kombination aus einer Liste von mehreren Faktoren ab.
tapply
ist im Geiste ähnlich wie die Split-apply-kombinieren Funktionen, die sind gemeinsam in R (aggregate
,by
,ave
,ddply
, etc.) Daher ihre schwarze Schaf-Status.
Andere Tipps
Auf der Seite beachten, hier ist, wie die verschiedenen plyr
Funktionen an die Basis *apply
Funktionen entsprechen (aus dem Intro zu plyr Dokument aus der plyr Webseite http://had.co.nz/plyr/ )
Base function Input Output plyr function
---------------------------------------
aggregate d d ddply + colwise
apply a a/l aaply / alply
by d l dlply
lapply l l llply
mapply a a/l maply / mlply
replicate r a/l raply / rlply
sapply l a laply
Eines der Ziele von plyr
steht im Einklang Namenskonventionen für jede der Funktionen zur Verfügung zu stellen, das Codieren der Eingangs- und Ausgangsdatentypen in den Funktionsnamen. Es bietet auch die Konsistenz in der Ausgabe, in dieser Ausgabe von dlply()
zu ldply()
gut befahrbar ist nützlich Ausgabe zu erzeugen, etc.
Konzeptionell plyr
Lernen ist nicht schwieriger als Funktionen der Basis *apply
zu verstehen.
plyr
und reshape
Funktionen haben fast alle diese Funktionen in meinem täglichen Gebrauch ersetzt. Aber auch aus dem Intro zu Plyr Dokument:
Damit zusammenhängende Funktionen
tapply
undsweep
haben keine inplyr
entsprechende Funktion und nützlich bleiben.merge
ist nützlich für die Zusammenfassungen mit den Originaldaten kombiniert werden.
Von Schieber 21 von http://www.slideshare.net/ Hadley / plyr-one-data-analytisch-Strategie :
(Hoffentlich ist es klar, dass apply
entspricht @ Hadleys aaply
und aggregate
entspricht @ Hadleys ddply
usw. Slide 20 derselben Slide klären, wenn Sie es von diesem Bild nicht bekommen.)
(auf der linken Seite eingegeben wird, werden auf der Oberseite wird ausgegeben)
Starten Sie zunächst mit Jorans ausgezeichneter Antwort -. Zweifelhaft, etwas besser, dass
Dann gelten die folgenden Mnemotechnik kann dazu beitragen, die Unterschiede zwischen den einzelnen zu erinnern. Während einige offensichtlich sind, können andere weniger sein --- für diese Sie Rechtfertigung in Jorans Diskussionen finden.
Mnemotechnik
-
lapply
ist eine Liste anwenden, die wirkt auf eine Liste oder Vektor und gibt eine Liste. -
sapply
ist ein einfachlapply
(Funktion standardmäßig auf Zurückgeben eines Vektors oder einer Matrix, wenn möglich) -
vapply
ist ein verifizierten gelten (erlaubt die Rückkehr Objekttyp vorgegeben werden) -
rapply
ist ein rekursive gelten für verschachtelte Listen, das heißt Listen innerhalb von Listen -
tapply
ist ein getaggten Anwendung, wenn die Tags, um die Untergruppen identifizieren
-
apply
ist generic : wendet eine Funktion auf eine Matrix von Zeilen oder Spalten (oder, allgemeiner, auf Abmessungen eines Arrays)
Der Aufbau des richtigen Hintergrund
Bei Verwendung der apply
Familie noch ein wenig fremd Sie fühlt, dann könnte es sein, dass Sie einen Schlüssel Sicht sind vermisst.
können diese beiden Artikel helfen. Sie sorgen für den nötigen Hintergrund der funktionalen Programmiertechniken zu motivieren , die durch die apply
Familie von Funktionen zur Verfügung gestellt werden.
Benutzer von Lisp wird das Paradigma sofort erkennen. Wenn Sie mit Lisp nicht vertraut sind, wenn Sie Ihren Kopf bekommen um FP, haben Sie eine leistungsfähige Sicht für den Einsatz in Forschung gewonnen -. Und apply
wird viel mehr Sinn machen
- Erweiterte R: Functional Programming , von Hadley Wickham
- Einfache funktionale Programmierung in R von Michael Barton
Da erkannte ich, dass (die sehr gute) Antworten dieses Postens Mangel an by
und aggregate
Erklärungen. Hier ist mein Beitrag.
von
Die by
Funktion, wie in der Dokumentation angegeben, kann allerdings sein, als „Wrapper“ für tapply
. Die Macht des by
entsteht, wenn wir eine Aufgabe berechnen möchten, dass tapply
nicht verarbeiten kann. Ein Beispiel ist dieser Code:
ct <- tapply(iris$Sepal.Width , iris$Species , summary )
cb <- by(iris$Sepal.Width , iris$Species , summary )
cb
iris$Species: setosa
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.300 3.200 3.400 3.428 3.675 4.400
--------------------------------------------------------------
iris$Species: versicolor
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.000 2.525 2.800 2.770 3.000 3.400
--------------------------------------------------------------
iris$Species: virginica
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.200 2.800 3.000 2.974 3.175 3.800
ct
$setosa
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.300 3.200 3.400 3.428 3.675 4.400
$versicolor
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.000 2.525 2.800 2.770 3.000 3.400
$virginica
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.200 2.800 3.000 2.974 3.175 3.800
Wenn wir diese beiden Objekte, ct
und cb
drucken, wir „im wesentlichen“ die gleichen Ergebnisse und die einzigen Unterschiede sind, wie sie dargestellt sind und die verschiedenen class
Attribute bzw. by
für cb
und array
für ct
.
Wie ich gesagt habe, stellt sich die Macht des by
, wenn wir nicht tapply
verwenden können; Der folgende Code ist ein Beispiel:
tapply(iris, iris$Species, summary )
Error in tapply(iris, iris$Species, summary) :
arguments must have same length
R sagt, dass Argumente müssen die gleiche Länge haben, sagen: „wir die summary
alle Variablen in iris
entlang des Faktor Species
berechnen wollen“. Aber R kann nur das nicht tun, weil sie nicht wissen, wie zu handhaben
Mit der by
Funktion R Versand eine bestimmte Methode für data frame
Klasse und dann lassen Sie die summary
Funktion funktioniert auch, wenn die Länge des ersten Arguments (und die Art zu) unterschiedlich ist.
bywork <- by(iris, iris$Species, summary )
bywork
iris$Species: setosa
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.300 Min. :2.300 Min. :1.000 Min. :0.100 setosa :50
1st Qu.:4.800 1st Qu.:3.200 1st Qu.:1.400 1st Qu.:0.200 versicolor: 0
Median :5.000 Median :3.400 Median :1.500 Median :0.200 virginica : 0
Mean :5.006 Mean :3.428 Mean :1.462 Mean :0.246
3rd Qu.:5.200 3rd Qu.:3.675 3rd Qu.:1.575 3rd Qu.:0.300
Max. :5.800 Max. :4.400 Max. :1.900 Max. :0.600
--------------------------------------------------------------
iris$Species: versicolor
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.900 Min. :2.000 Min. :3.00 Min. :1.000 setosa : 0
1st Qu.:5.600 1st Qu.:2.525 1st Qu.:4.00 1st Qu.:1.200 versicolor:50
Median :5.900 Median :2.800 Median :4.35 Median :1.300 virginica : 0
Mean :5.936 Mean :2.770 Mean :4.26 Mean :1.326
3rd Qu.:6.300 3rd Qu.:3.000 3rd Qu.:4.60 3rd Qu.:1.500
Max. :7.000 Max. :3.400 Max. :5.10 Max. :1.800
--------------------------------------------------------------
iris$Species: virginica
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.900 Min. :2.200 Min. :4.500 Min. :1.400 setosa : 0
1st Qu.:6.225 1st Qu.:2.800 1st Qu.:5.100 1st Qu.:1.800 versicolor: 0
Median :6.500 Median :3.000 Median :5.550 Median :2.000 virginica :50
Mean :6.588 Mean :2.974 Mean :5.552 Mean :2.026
3rd Qu.:6.900 3rd Qu.:3.175 3rd Qu.:5.875 3rd Qu.:2.300
Max. :7.900 Max. :3.800 Max. :6.900 Max. :2.500
es funktioniert in der Tat, und das Ergebnis ist sehr überraschend. Es ist eine Aufgabe der Klasse by
dass entlang Species
(sagen wir, für jeden von ihnen) berechnet die summary
jeder Variablen.
Beachten Sie, dass, wenn das erste Argument ein data frame
ist, die versandt Funktion eine Methode für diese Klasse von Objekten haben. Zum Beispiel ist verwenden wir diesen Code mit der mean
Funktion werden wir diesen Code haben, die keinen Sinn überhaupt hat:
by(iris, iris$Species, mean)
iris$Species: setosa
[1] NA
-------------------------------------------
iris$Species: versicolor
[1] NA
-------------------------------------------
iris$Species: virginica
[1] NA
Warning messages:
1: In mean.default(data[x, , drop = FALSE], ...) :
argument is not numeric or logical: returning NA
2: In mean.default(data[x, , drop = FALSE], ...) :
argument is not numeric or logical: returning NA
3: In mean.default(data[x, , drop = FALSE], ...) :
argument is not numeric or logical: returning NA
GESAMT
aggregate
als eine andere sein, eine andere Art und Weise der Verwendung tapply
gesehen, wenn wir es in einer solchen Art und Weise verwendet werden.
at <- tapply(iris$Sepal.Length , iris$Species , mean)
ag <- aggregate(iris$Sepal.Length , list(iris$Species), mean)
at
setosa versicolor virginica
5.006 5.936 6.588
ag
Group.1 x
1 setosa 5.006
2 versicolor 5.936
3 virginica 6.588
Die beiden unmittelbaren Unterschiede sind, dass das zweite Argument von aggregate
muss eine Liste sein, während tapply
können (nicht zwingend) eine Liste sein und dass der Ausgang des aggregate
ein Datenrahmen während der eine von tapply
ein array
ist.
Die Macht des aggregate
ist, dass es leicht Teilmengen der Daten mit subset
Argumente umgehen kann und dass es Methoden für ts
Objekte und formula
auch.
Diese Elemente machen aggregate
zur Arbeit leichter mit diesem tapply
in einigen Situationen.
Hier sind einige Beispiele (in der Dokumentation):
ag <- aggregate(len ~ ., data = ToothGrowth, mean)
ag
supp dose len
1 OJ 0.5 13.23
2 VC 0.5 7.98
3 OJ 1.0 22.70
4 VC 1.0 16.77
5 OJ 2.0 26.06
6 VC 2.0 26.14
Wir können das gleiche mit tapply
erreichen, aber die Syntax ist etwas härter und der Ausgang (unter bestimmten Umständen) weniger lesbar:
att <- tapply(ToothGrowth$len, list(ToothGrowth$dose, ToothGrowth$supp), mean)
att
OJ VC
0.5 13.23 7.98
1 22.70 16.77
2 26.06 26.14
Es gibt auch andere Zeiten, in denen wir nicht by
oder tapply
verwenden können, und wir haben aggregate
verwenden.
ag1 <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, mean)
ag1
Month Ozone Temp
1 5 23.61538 66.73077
2 6 29.44444 78.22222
3 7 59.11538 83.88462
4 8 59.96154 83.96154
5 9 31.44828 76.89655
Wir können nicht das vorherige Ergebnis mit tapply
in einem Aufruf erhalten, aber wir haben den Mittelwert zu berechnen entlang Month
für jedes Element und dann kombinieren (auch beachten Sie, dass wir die na.rm = TRUE
nennen müssen, weil die formula
Methoden der aggregate
Funktion standardmäßig die na.action = na.omit
):
ta1 <- tapply(airquality$Ozone, airquality$Month, mean, na.rm = TRUE)
ta2 <- tapply(airquality$Temp, airquality$Month, mean, na.rm = TRUE)
cbind(ta1, ta2)
ta1 ta2
5 23.61538 65.54839
6 29.44444 79.10000
7 59.11538 83.90323
8 59.96154 83.96774
9 31.44828 76.90000
, während mit by
wir einfach nicht erreichen, dass in der Tat die folgende Funktionsaufruf einen Fehler zurückgibt (aber wahrscheinlich ist es im Zusammenhang mit der mitgelieferten Funktion, mean
):
by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE)
Andere Zeiten sind die Ergebnisse der gleichen und die Unterschiede sind nur in der Klasse (und dann, wie es dargestellt / gedruckt und nicht nur - Beispiel, wie es zur Teilmenge) Objekt:
byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)
Der vorherige Code das gleiche Ziel und Ergebnisse erzielen, an einigen Punkten, welches Werkzeug zu verwenden ist nur eine Frage der persönlichen Vorlieben und Bedürfnisse; die letzten beiden Objekte haben sehr unterschiedliche Bedürfnisse in Bezug auf subsetting.
Es gibt viele große Antworten, die für jede Funktion Unterschiede in den Anwendungsfällen diskutieren. Keiner der Antwort, die Unterschiede in der Leistung diskutieren. Das ist vernünftig Ursache verschiedene Funktionen verschiedene Eingabe erwartet und verschiedene Ausgabe erzeugt, doch die meisten von ihnen haben ein allgemeines gemeinsames Ziel von Serie / Gruppen zu bewerten. Meine Antwort wird auf die Performance konzentrieren. Aufgrund über der Eingabe Schaffung von den Vektoren in dem Timing enthalten ist, wird auch die Funktion apply
nicht gemessen.
Ich habe auf einmal zwei verschiedene Funktionen sum
und length
getestet. Volume getestet 50M ist am Eingang und am Ausgang 50K. Ich habe auch zwei derzeit populäre Pakete enthält, die nicht weit zu der Zeit verwendet wurden, als Frage gestellt wurde, data.table
und dplyr
. Beide sind auf jeden Fall wert aussehen, wenn Sie für eine gute Leistung anstreben.
library(dplyr)
library(data.table)
set.seed(123)
n = 5e7
k = 5e5
x = runif(n)
grp = sample(k, n, TRUE)
timing = list()
# sapply
timing[["sapply"]] = system.time({
lt = split(x, grp)
r.sapply = sapply(lt, function(x) list(sum(x), length(x)), simplify = FALSE)
})
# lapply
timing[["lapply"]] = system.time({
lt = split(x, grp)
r.lapply = lapply(lt, function(x) list(sum(x), length(x)))
})
# tapply
timing[["tapply"]] = system.time(
r.tapply <- tapply(x, list(grp), function(x) list(sum(x), length(x)))
)
# by
timing[["by"]] = system.time(
r.by <- by(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
)
# aggregate
timing[["aggregate"]] = system.time(
r.aggregate <- aggregate(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
)
# dplyr
timing[["dplyr"]] = system.time({
df = data_frame(x, grp)
r.dplyr = summarise(group_by(df, grp), sum(x), n())
})
# data.table
timing[["data.table"]] = system.time({
dt = setnames(setDT(list(x, grp)), c("x","grp"))
r.data.table = dt[, .(sum(x), .N), grp]
})
# all output size match to group count
sapply(list(sapply=r.sapply, lapply=r.lapply, tapply=r.tapply, by=r.by, aggregate=r.aggregate, dplyr=r.dplyr, data.table=r.data.table),
function(x) (if(is.data.frame(x)) nrow else length)(x)==k)
# sapply lapply tapply by aggregate dplyr data.table
# TRUE TRUE TRUE TRUE TRUE TRUE TRUE
# print timings
as.data.table(sapply(timing, `[[`, "elapsed"), keep.rownames = TRUE
)[,.(fun = V1, elapsed = V2)
][order(-elapsed)]
# fun elapsed
#1: aggregate 109.139
#2: by 25.738
#3: dplyr 18.978
#4: tapply 17.006
#5: lapply 11.524
#6: sapply 11.326
#7: data.table 2.686
Es ist vielleicht erwähnenswert ave
. ave
ist tapply
freundliche Cousin. Es gibt Ergebnisse in einer Form, die Sie gerade wieder in den Datenrahmen anschließen können.
dfr <- data.frame(a=1:20, f=rep(LETTERS[1:5], each=4))
means <- tapply(dfr$a, dfr$f, mean)
## A B C D E
## 2.5 6.5 10.5 14.5 18.5
## great, but putting it back in the data frame is another line:
dfr$m <- means[dfr$f]
dfr$m2 <- ave(dfr$a, dfr$f, FUN=mean) # NB argument name FUN is needed!
dfr
## a f m m2
## 1 A 2.5 2.5
## 2 A 2.5 2.5
## 3 A 2.5 2.5
## 4 A 2.5 2.5
## 5 B 6.5 6.5
## 6 B 6.5 6.5
## 7 B 6.5 6.5
## ...
Es gibt nichts im Basispaket, das funktioniert wie ave
für ganzen Datenrahmen (wie by
ist wie tapply
für Datenrahmen). Aber man kann es frisieren:
dfr$foo <- ave(1:nrow(dfr), dfr$f, FUN=function(x) {
x <- dfr[x,]
sum(x$m*x$m2)
})
dfr
## a f m m2 foo
## 1 1 A 2.5 2.5 25
## 2 2 A 2.5 2.5 25
## 3 3 A 2.5 2.5 25
## ...
Trotz aller großen Antworten hier gibt es 2 weitere Basisfunktionen, die erwähnt zu werden verdienen, die nützlich outer
Funktion und die dunkle eapply
Funktion
äußere
outer
ist eine sehr nützliche Funktion als eher banalen einen versteckt. Wenn Sie die Hilfe für outer
seine Beschreibung lesen sagt:
The outer product of the arrays X and Y is the array A with dimension
c(dim(X), dim(Y)) where element A[c(arrayindex.x, arrayindex.y)] =
FUN(X[arrayindex.x], Y[arrayindex.y], ...).
, die es scheinen, wie dies macht, ist nur nützlich für die lineare Algebra Art Dinge. Es kann jedoch ähnlich wie mapply
verwendet werden, um eine Funktion zu zwei Vektoren von Eingaben anzuwenden. Der Unterschied besteht darin, dass mapply
wird die Funktion der ersten beiden Elemente anzuwenden und dann die zweite zwei etc, während outer
wird die Funktion auf jede Kombination von einem Element aus dem ersten Vektor und eine von der zweiten Anwendung. Zum Beispiel:
A<-c(1,3,5,7,9)
B<-c(0,3,6,9,12)
mapply(FUN=pmax, A, B)
> mapply(FUN=pmax, A, B)
[1] 1 3 6 9 12
outer(A,B, pmax)
> outer(A,B, pmax)
[,1] [,2] [,3] [,4] [,5]
[1,] 1 3 6 9 12
[2,] 3 3 6 9 12
[3,] 5 5 6 9 12
[4,] 7 7 7 9 12
[5,] 9 9 9 9 12
Ich habe dies persönlich in Anspruch genommen, wenn ich einen Vektor von Werten und einen Vektor von Bedingungen und Wunsch die Werte erfüllen, die Bedingungen zu sehen.
eApply
eapply
ist wie lapply
außer, dass anstatt eine Funktion auf jedes Element in einer Liste anwenden, ist es eine Funktion in einer Umgebung jedes Element gilt. Zum Beispiel, wenn Sie eine Liste der benutzerdefinierten Funktionen im globalen Umfeld zu finden:
A<-c(1,3,5,7,9)
B<-c(0,3,6,9,12)
C<-list(x=1, y=2)
D<-function(x){x+1}
> eapply(.GlobalEnv, is.function)
$A
[1] FALSE
$B
[1] FALSE
$C
[1] FALSE
$D
[1] TRUE
Ehrlich gesagt ich nicht diese sehr viel verwenden, aber wenn Sie viele Pakete bauen oder viele Umgebungen schaffen es nützlich sein kann.
ich das ziemlich nützlich sweep
Funktion vor kurzem entdeckt und fügen Sie ihn hier der Vollständigkeit halber:
Sweep
Die Grundidee ist es, Sweep durch eine Anordnung zeilen- oder spaltenweise und eine modifizierte Array zurück. Ein Beispiel soll dies deutlich (Quelle: datacamp ) machen :
Angenommen, Sie haben eine Matrix und wollen standardisieren es spaltenweise:
dataPoints <- matrix(4:15, nrow = 4)
# Find means per column with `apply()`
dataPoints_means <- apply(dataPoints, 2, mean)
# Find standard deviation with `apply()`
dataPoints_sdev <- apply(dataPoints, 2, sd)
# Center the points
dataPoints_Trans1 <- sweep(dataPoints, 2, dataPoints_means,"-")
print(dataPoints_Trans1)
## [,1] [,2] [,3]
## [1,] -1.5 -1.5 -1.5
## [2,] -0.5 -0.5 -0.5
## [3,] 0.5 0.5 0.5
## [4,] 1.5 1.5 1.5
# Return the result
dataPoints_Trans1
## [,1] [,2] [,3]
## [1,] -1.5 -1.5 -1.5
## [2,] -0.5 -0.5 -0.5
## [3,] 0.5 0.5 0.5
## [4,] 1.5 1.5 1.5
# Normalize
dataPoints_Trans2 <- sweep(dataPoints_Trans1, 2, dataPoints_sdev, "/")
# Return the result
dataPoints_Trans2
## [,1] [,2] [,3]
## [1,] -1.1618950 -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983 -0.3872983
## [3,] 0.3872983 0.3872983 0.3872983
## [4,] 1.1618950 1.1618950 1.1618950
Hinweis: für dieses einfache Beispiel kann das gleiche Ergebnis natürlich leichter erreicht werden, indem
apply(dataPoints, 2, scale)