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 ...

  1. sapply(vec, f): Eingang ist ein Vektor. Ausgang ist ein Vektor / Matrix, wo Element i ist f(vec[i]), so dass Sie eine Matrix, wenn f einen Multi-Element-Ausgang

  2. lapply(vec, f): gleiche wie sapply, aber Ausgang ist eine Liste

  3. apply(matrix, 1/2, f): Eingang ist eine Matrix. Ausgang ist ein Vektor, in dem Elemente i ist f (Zeile / Spalte der Matrix i)
  4. tapply(vector, grouping, f): Ausgang ist eine Matrix / Anordnung, wobei ein Element in der Matrix / Anordnung ist der Wert von f bei einer Gruppierungs g des Vektors und g wird auf die Zeile / Spalte-Namen geschoben
  5. by(dataframe, grouping, f): Lassen Sie g eine Gruppierung sein. gilt f zu jeder Spalte der Gruppe / Datenrahmen. drucken ziemlich die Gruppierung und den Wert von f bei jeder Spalte.
  6. aggregate(matrix, grouping, f). Ähnlich wie by, 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

War es hilfreich?

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 mit SIMPLIFY = FALSE, so gewährleistet ist, eine Liste zurück

    Map(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 von y 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 und sweep haben keine in plyr 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 :

gilt, sapply, lapply, durch Aggregat

(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 einfach lapply (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

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)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top