Domanda

Ogni volta che voglio fare qualcosa py "mappa" in R, io di solito cerco di utilizzare una funzione nella famiglia apply.

Tuttavia, non ho mai capito le differenze tra loro - come {sapply, lapply, ecc} applicare la funzione all'ingresso di input / raggruppati, ciò che l'output sarà simile, o anche quello che l'ingresso può essere -. così spesso basta andare attraverso tutti loro fino a ottenere ciò che voglio

qualcuno può spiegare come utilizzare quale quando?

Il mio attuale (/ incompleta probabilmente errata) comprensione è ...

  1. sapply(vec, f): ingresso è un vettore. uscita è un vettore / matrice, dove elemento i è f(vec[i]), dando una matrice se f ha un'uscita più elementi

  2. lapply(vec, f):? Uguale sapply, ma la produzione è un elenco

  3. apply(matrix, 1/2, f): input è una matrice. uscita è un vettore, quale elemento i è f (riga / col i della matrice)
  4. tapply(vector, grouping, f): uscita è una matrice / matrice, in cui un elemento della matrice / matrice è il valore di f a g raggruppamento del vettore, e g viene spinto ai nomi riga / col
  5. by(dataframe, grouping, f): lasciare g essere un raggruppamento. applicare f ad ogni colonna del gruppo / dataframe. abbastanza stampare il raggruppamento ed il valore di f ad ogni colonna.
  6. aggregate(matrix, grouping, f):. Simile a by, ma invece di stampare abbastanza dell'uscita, bastoni aggregati tutto in un dataframe

domanda laterale:? Ho ancora plyr o rimodellare non imparato - avrei plyr o reshape sostituire tutti questi del tutto

È stato utile?

Soluzione

R ha molti * funzioni che vengono abilmente descritte nei file di aiuto (per esempio ?apply) si applicano. Ci sono abbastanza di loro, però, che gli utenti che iniziano possono avere difficoltà di decidere quale è appropriato per la loro situazione o anche tutti ricordare. Possono avere un senso generale che "dovrei usare un * Applicare la funzione qui", ma può essere difficile tenerli tutti dritti in un primo momento.

Nonostante il fatto (già sottolineato in altre risposte) che gran parte della funzionalità della famiglia * applicare è coperto dal pacchetto plyr estremamente popolare, le funzioni di base rimangono utile e vale la pena conoscere.

Questa risposta è destinato ad agire come una sorta di cartello per i nuovi utenti per aiutarli direttamente al corretto funzionamento * applicare per la loro particolare problema. Nota, questo è non destinato semplicemente regurgitate o sostituire la documentazione R! La speranza è che questa risposta vi aiuta a decidere quale * applicare tute funzione la vostra situazione e quindi spetta a voi per la ricerca ulteriormente. Con una sola eccezione, non saranno affrontate differenze di prestazioni.

  • applicare - Quando si desidera applicare una funzione per le righe o le colonne di una matrice (e analoghi di dimensione superiore); generalmente non consigliabile per frame di dati in quanto sarà costringere ad una matrice prima.

    # 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
    

    Se si desidera riga / medie di colonna o le somme per una matrice 2D, essere sicuri di indagare la, colMeans ottimizzato fulmine-rapido, rowMeans, colSums, rowSums.

  • lapply - Quando si desidera applicare una funzione a ogni elemento di un Lista a sua volta e ottenere una lista di nuovo.

    Questo è il cavallo di battaglia di molti degli altri * si applicano le funzioni. Sbucciare eseguire il proprio codice e si trovano spesso lapply sotto.

    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 - Quando si desidera applicare una funzione a ogni elemento di un lista a sua volta, ma si vuole un vector di nuovo, piuttosto che una lista.

    Se vi trovate digitando unlist(lapply(...)), fermarsi e considerare 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 usi più avanzati di sapply tenterà di costringere il portare a una matrice multidimensionale, se del caso. Ad esempio, se i nostri restituisce funzione vettori della stessa lunghezza, sapply li usa come colonne di una matrice:

    sapply(1:5,function(x) rnorm(3,x))
    

    Se la nostra funzione restituisce una matrice bidimensionale 2, sapply farà essenzialmente la stessa cosa, trattando ogni matrice restituita come un unico lungo vettore:

    sapply(1:5,function(x) matrix(x,2,2))
    

    A meno che non si precisa simplify = "array", nel qual caso utilizzerà le singole matrici per costruire un array multi-dimensionale:

    sapply(1:5,function(x) matrix(x,2,2), simplify = "array")
    

    Ognuno di questi comportamenti è naturalmente subordinata alla nostra funzione che restituisce vettori o matrici della stessa lunghezza o dimensione.

  • vapply - Quando si desidera utilizzare sapply ma forse ha bisogno di spremere un po 'più velocità dal vostro codice.

    Per vapply, che, fondamentalmente, dai R un esempio di che tipo di cose la funzione restituirà, che può risparmiare un po 'di tempo coercizione restituito I valori per adattarsi in un singolo vettore atomica.

    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 - Per quando si dispone di diverse strutture di dati (per esempio vettori, liste) e si desidera applicare una funzione per il 1 ° elementi di ciascuno, e poi il 2 ° elementi di ciascuno, ecc, vi costringono il risultato ad un vettore / matrice come in sapply.

    Questa è multivariata, nel senso che la funzione deve accettare più argomenti.

    #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
    
  • Mappa -. Un wrapper per mapply con SIMPLIFY = FALSE, quindi è garantito per restituire un elenco

    Map(sum, 1:5, 1:5, 1:5)
    [[1]]
    [1] 3
    
    [[2]]
    [1] 6
    
    [[3]]
    [1] 9
    
    [[4]]
    [1] 12
    
    [[5]]
    [1] 15
    
  • rapply - Perquando si vuole applicare una funzione ad ogni elemento di un elenco nidificato la struttura, in modo ricorsivo.

    Per darvi un'idea di come rapply raro è, ho dimenticato a questo proposito quando prima pubblicazione di questa risposta! Ovviamente, sono sicuro che molte persone lo usano, ma YMMV. rapply è meglio illustrato con una funzione definita dall'utente per applicare:

    # 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 - Per quando si vuole applicare una funzione a sottoinsiemi di un vettore ei sottoinsiemi sono definiti da un altro vettore, solitamente fattore.

    La pecora nera della famiglia * applicare, di sorta. l'uso del file di aiuto di la frase "Jagged array" può essere un po ' confusione , ma in realtà è abbastanza semplice.

    Un vettore:

    x <- 1:20
    

    Un fattore (della stessa lunghezza!) Gruppi che definiscono:

    y <- factor(rep(letters[1:5], each = 4))
    

    Aggiungi i valori in x all'interno di ciascun sottogruppo definito da y:

    tapply(x, y, sum)  
     a  b  c  d  e  
    10 26 42 58 74 
    

    Altri esempi complessi possono essere gestiti in cui sono definiti i sottogruppi dalle combinazioni uniche di un elenco di diversi fattori. tapply è simile nello spirito alla scissione-apply-combinare funzioni che sono comune in R (aggregate, by, ave, ddply, ecc) qui il suo stato di pecora nera.

Altri suggerimenti

Sul lato nota, ecco come le varie funzioni plyr corrispondono alle funzioni di base *apply (da intro di documento plyr dal plyr pagina web 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 

Uno degli obiettivi di plyr è quello di realizzare convenzioni di denominazione coerenti per ciascuna delle funzioni, codificare i tipi di dati di ingresso e di uscita nel nome della funzione. Esso prevede inoltre la coerenza della produzione, in quanto uscita dal dlply() è facilmente percorribile per ldply() per produrre output utile, etc.

Concettualmente, l'apprendimento plyr non è più difficile di quanto la comprensione delle funzioni di base *apply.

funzioni

??plyr e reshape hanno sostituito quasi tutte queste funzioni in ogni mio uso giorno. Ma, anche dal documento Introduzione a Plyr:

  

funzioni correlate tapply e sweep hanno funzione plyr non corrispondenti, e rimane utile. merge è utile per combinare sintesi con i dati originali.

Da slitta 21 di http://www.slideshare.net/ -analitico-strategia plyr-uno-dati Hadley / :

si applicano, sapply, lapply, da, aggregato

(Speriamo che sia chiaro che apply corrisponde a @ di Hadley aaply e aggregate corrisponde a @ ddply ecc Slide Hadley 20 dello stesso Slideshare chiarirà se non si ottiene da questa immagine.)

(a sinistra in ingresso, in alto viene emesso)

Per prima cosa iniziare con risposta eccellente di Joran -. Dubbioso tutto può meglio che

Poi i seguenti mnemonici possono aiutare a ricordare le distinzioni tra ciascuno. Mentre alcuni sono evidenti, altri possono essere meno così --- per questi si trovano giustificazione nelle discussioni di Joran.

Mnemonics

  • lapply è un si applicano che agisce su una lista o vettore e restituisce una lista.
  • sapply è un semplice lapply (default funzione per restituire un vettore o matrice quando possibile)
  • vapply è un verificate applicare (consente al tipo di oggetto ritorno da pre-specificata)
  • rapply è un ricorsiva invia candidatura per le liste nidificate, ossia elenchi all'interno di elenchi
  • tapply è un tag applica quando i tag identificano i sottoinsiemi
  • apply è generici : applica una funzione di righe o colonne di una matrice (o, più in generale, alle dimensioni di un array)

Costruire sullo sfondo a destra

Se si utilizza la famiglia apply sente ancora un po 'estraneo a voi, allora potrebbe essere che vi state perdendo un punto chiave di vista.

Questi due articoli possono aiutare. Essi forniscono il background necessario per motivare i tecniche funzionali di programmazione che vengono forniti dalla famiglia apply di funzioni.

Gli utenti di Lisp riconoscerà immediatamente il paradigma. Se non hai familiarità con Lisp, una volta che si ottiene la testa intorno FP, avrete guadagnato un punto di vista potente per l'uso in R -. E apply farà molto più senso

Da quando ho capito che (i) molto eccellenti risposte di questo post mancanza di by e aggregate spiegazioni. Qui è il mio contributo.

DA

La funzione by, come indicato nella documentazione può essere però, come un "wrapper" per tapply. Il potere di by si pone quando si vuole calcolare un compito che tapply non può gestire. Ne è un esempio di questo codice:

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 

Se il risultato della stampa di questi due oggetti, ct e cb, noi "essenzialmente" hanno gli stessi risultati e le uniche differenze sono nel modo in cui vengono visualizzati ed i diversi attributi class, rispettivamente by per cb e array per ct.

Come ho detto, il potere di by sorge quando non possiamo usare tapply; il seguente codice è un esempio:

 tapply(iris, iris$Species, summary )
Error in tapply(iris, iris$Species, summary) : 
  arguments must have same length

R dice che gli argomenti devono avere le stesse lunghezze, dire "vogliamo calcolare la summary di tutte le variabili in iris lungo il fattore Species":., Ma R solo non può farlo perché non sa come gestire

Con la funzione by R inviano un metodo specifico per la classe data frame e poi lasciare la funzione summary funziona anche se la lunghezza del primo argomento (e il tipo troppo) sono differenti.

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     

funziona davvero e il risultato è molto sorprendente. E 'un oggetto della classe by che insieme Species (ad esempio, per ciascuna di esse) calcola la summary di ciascuna variabile.

Si noti che se il primo argomento è un data frame, la funzione di inviato deve avere un metodo per quella classe di oggetti. Per esempio, è che abbiamo utilizzare questo codice con la funzione mean avremo questo codice che non ha alcun senso a tutti:

 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

COMPLESSO

aggregate può essere visto come un altro un modo diverso di utilizzo tapply se lo usiamo in tal modo un.

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

Le due differenze immediati sono che il secondo argomento della aggregate deve essere un elenco mentre tapply possono (non obbligatorio) una lista e che l'uscita del aggregate è un frame di dati mentre quella di tapply è un array.

Il potere di aggregate è che può gestire facilmente sottoinsiemi di dati con tesi subset e che ha metodi per gli oggetti ts e formula pure.

Questi elementi rendono aggregate più facile lavorare con che tapply in alcune situazioni. Ecco alcuni esempi (disponibili nella documentazione):

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

Si può ottenere lo stesso con tapply ma la sintassi è leggermente più difficile e l'uscita (in alcune circostanze) meno leggibile:

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

Ci sono altri momenti in cui non possiamo usare by o tapply e dobbiamo usare aggregate.

 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

Non possiamo ottenere il risultato precedente, con tapply in una sola chiamata, ma dobbiamo calcolare la media lungo Month per ogni elemento e poi combinarle (da notare, inoltre, che dobbiamo chiamare il na.rm = TRUE, perché i metodi formula della funzione aggregate ha per impostazione predefinita, il 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

mentre con by noi non possiamo ottenere che in realtà la seguente chiamata di funzione restituisce un errore (ma molto probabilmente è legato alla funzione in dotazione, mean):

by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE)

Altre volte i risultati sono gli stessi e le differenze sono solo nella classe (e quindi come si mostra / stampata e non solo - ad esempio, come sottoinsieme di esso) oggetto:

byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)

Il codice precedente raggiungere lo stesso obiettivo e risultati, in alcuni punti quanto strumento da usare è solo una questione di gusti ed esigenze personali; i precedenti due oggetti hanno esigenze molto diverse in termini di sottoinsiemi.

Ci sono un sacco di grandi risposte che discutono differenze nei casi di utilizzo per ogni funzione. Nessuna delle risposte discutere le differenze in termini di prestazioni. Cioè ragionevole causano varie funzioni prevede diversi ingressi e produce vari uscita, ma la maggior parte di loro hanno un comune obiettivo generale di valutare per serie / gruppi. La mia risposta sta andando a concentrarsi sulle prestazioni. A causa di sopra della creazione ingresso dai vettori è incluso nel temporizzazione, anche la funzione apply non è misurata.

Ho provato due diverse funzioni sum e length in una sola volta. Volume testato è 50M in ingresso e in uscita 50K. Ho incluso anche due pacchetti attualmente popolari che non sono stati ampiamente utilizzati nel momento in cui domanda è stato chiesto, data.table e dplyr. Entrambi sono sicuramente vale la pena di guardare se si sono intesi per buone prestazioni.

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

E 'forse la pena ave menzione. ave è cugino amichevole tapply. Esso restituisce i risultati in una forma che è possibile collegare schiena dritta nella cornice di dati.

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

Non c'è nulla nel pacchetto base che opere come ave per frame di dati interi (come by è come tapply per frame di dati). Ma si può fudge che:

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

Nonostante tutte le grandi risposte qui, ci sono 2 più funzioni di base che meritano di essere menzionati, la funzione outer utile e la funzione eapply oscura

esterno

outer è una funzione molto utile nascosto come una più banale. Se andate a leggere l'aiuto per la outer sua descrizione dice:

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], ...).

che fa sembrare come questo è utile solo per le cose di tipo algebra lineare. Tuttavia, può essere utilizzato tanto come mapply per applicare una funzione di due vettori di input. La differenza è che mapply applicherà la funzione per i primi due elementi e quindi la seconda due ecc, che outer applicherà la funzione di ogni combinazione di un elemento del primo vettore e uno dal secondo. Ad esempio:

 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

Personalmente ho usato questo quando ho un vettore di valori e un vettore di condizioni e di desiderio di vedere che si incontrano, che le condizioni di valori.

eapply

eapply è come lapply differenza che invece di applicare una funzione ad ogni elemento in una lista, si applica una funzione per ogni elemento in un ambiente. Per esempio, se si vuole trovare un elenco delle funzioni definite dall'utente nel contesto globale:

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 

Francamente io non uso questo molto, ma se si sta creando un sacco di pacchetti o di creare un sacco di ambienti, può tornare utile.

Recentemente ho scoperto la funzione sweep piuttosto utile e inserirlo qui per ragioni di completezza:

spazzata

L'idea di base è quella di spazzata attraverso una matrice di riga o colonna-saggio e restituire una matrice modificata. Un esempio renderà questa chiara (fonte: datacamp ) :

Diciamo che si dispone di una matrice e volete standardizzare esso colonna-saggio:

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

NB: per questo semplice esempio lo stesso risultato può naturalmente essere raggiunto più facilmente
apply(dataPoints, 2, scale)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top