Raggruppare le funzioni (tapply, by, aggregate) e * applicare la famiglia

Ogni volta che voglio fare qualcosa “map” in R, di solito cerco di usare una funzione nella famiglia apply .

Tuttavia, non ho mai capito bene le differenze tra loro – come { sapply , lapply , ecc}} applicare la funzione all’ingresso input / grouped, a quale apparirà l’output, o persino a ciò che può essere l’input, quindi Spesso li sfoglio tutti fino a quando non ottengo ciò che voglio.

Qualcuno può spiegare come usare quale quando?

La mia comprensione attuale (probabilmente errata / incompleta) è …

  1. sapply(vec, f) : input è un vettore. l’output è un vettore / matrice, in cui l’elemento i è f(vec[i]) , fornendo una matrice se f ha un output a più elementi

  2. lapply(vec, f) : uguale a sapply , ma l’output è una lista?

  3. apply(matrix, 1/2, f) : input è una matrice. l’output è un vettore, dove l’elemento i è f (riga / colonna della matrice)
  4. tapply(vector, grouping, f) : output è una matrice / matrice, dove un elemento nella matrice / matrice è il valore di f in un raggruppamento g del vettore, e g viene spinto ai nomi di riga / colonna
  5. by(dataframe, grouping, f) : sia g un raggruppamento. applica f a ciascuna colonna del gruppo / dataframe. bella stampa il raggruppamento e il valore di f in ogni colonna.
  6. aggregate(matrix, grouping, f) : simile a, ma invece di stampare piuttosto l’output, l’aggregato inserisce tutto in un dataframe.

Domanda a margine: non ho ancora imparato il plyr o la rimodellamento – plyr rimpiazzare o reshape completamente tutto questo?

R ha molte * funzioni di applicazione che sono abilmente descritte nei file di aiuto (ad esempio ?apply ). Ce ne sono abbastanza, tuttavia, che l’inizio dell’uso può avere difficoltà nel decidere quale sia appropriato per la loro situazione o addirittura ricordandoli tutti. Possono avere una sensazione generale che “dovrei usare una funzione * apply qui”, ma può essere difficile mantenerli tutti dritti all’inizio.

Nonostante il fatto (osservato in altre risposte) che gran parte delle funzionalità della famiglia * apply sia coperta dal pacchetto plyr estremamente popolare, le funzioni di base rimangono utili e meritevoli di essere comprese.

Questa risposta è pensata per fungere da una sorta di cartello per i nuovi usi per aiutarli a dirigere la corretta funzione * per il loro particolare problema. Nota, questo non è inteso semplicemente rigurgitare o sostituire la documentazione R! La speranza è che questa risposta ti aiuti a decidere quale funzione * si adatta alla tua situazione e poi spetta a te approfondirla. Con un’eccezione, le differenze di prestazioni non saranno affrontate.

  • applyQuando si desidera applicare una funzione alle righe o alle colonne di una matrice (e analoghi a più dimensioni); non è generalmente consigliabile per i frame di dati in quanto prima costringerà a una matrice.

     # 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[*, , ] - ie Sum across 2nd and 3rd dimension apply(M, 1, sum) # Result is one-dimensional [1] 120 128 136 144 # Apply sum across each M[*, *, ] - ie 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 vuoi i mezzi di riga / colonna o le somme per una matrice 2D, assicurati di investigare i colMeans , i rowMeans , i colSums , i rowSums altamente ottimizzati e rowSums .

  • lapply - Quando si desidera applicare una funzione a ciascun elemento di una lista a turno e recuperare una lista.

    Questo è il cavallo di battaglia di molte delle altre funzioni * apply. lapply loro codice e troverete 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 vuoi applicare una funzione a ciascun elemento di una lista a turno, ma vuoi un vettore indietro, piuttosto che una lista.

    Se ti ritrovi a digitare unlist(lapply(...)) , fermati e considera sapply .

     x < - list(a = 1, b = 1:3, c = 10:100) # Compare with above; a named vector, not a list sapply(x, FUN = length) abc 1 3 91 sapply(x, FUN = sum) abc 1 6 5005 

    Negli usi più avanzati di sapply tenterà di forzare il risultato in una matrice multidimensionale, se appropriato. Ad esempio, se la nostra funzione restituisce vettori della stessa lunghezza, sapply li userà come colonne di una matrice:

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

    Se la nostra funzione restituisce una matrice bidimensionale, sapply farà la stessa cosa, trattando ciascuna matrice restituita come un singolo vettore lungo:

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

    A meno che non specifichiamo simplify = "array" , nel qual caso userà le singole matrici per build un array multidimensionale:

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

    Ognuno di questi comportamenti è ovviamente subordinato alla nostra funzione di restituire vettori o matrici della stessa lunghezza o dimensione.

  • vapply - Quando vuoi usare sapply ma forse sapply bisogno di spremere un po 'più di velocità dal tuo codice.

    Per vapply , in pratica si fornisce a R un esempio del tipo di cosa restituita dalla funzione, che può far risparmiare alcuni valori restituiti che si adattano in un singolo vettore atomico.

     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) abc 1 3 91 
  • mapply - Per quando si hanno diverse strutture di dati (ad esempio vettori, elenchi) e si desidera applicare una funzione ai primi elementi di ciascuno, quindi i 2 elementi di ciascuno, ecc., forzando il risultato a un vettore / array come in sapply .

    Questo è multivariato nel senso che la tua 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 mapply SIMPLIFY = FALSE , quindi è garantito il ritorno di una lista.

     Map(sum, 1:5, 1:5, 1:5) [[1]] [1] 3 [[2]] [1] 6 [[3]] [1] 9 [[4]] [1] 12 [[5]] [1] 15 
  • rapply - Per quando si desidera applicare una funzione a ciascun elemento di una struttura di elenco nidificata , in modo ricorsivo.

    Per darti un'idea di quanto sia raro il rapply , me ne sono dimenticato quando ho postato questa risposta per la prima volta! Ovviamente, sono sicuro che molte persone lo usano, ma YMMV. rapply è meglio illustrato con una funzione definita dall'utente da 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 desidera applicare una funzione ai sottoinsiemi di un vettore e i sottoinsiemi sono definiti da un altro vettore, solitamente un fattore.

    Le pecore nere della * applicano la famiglia, di sorta. L'uso del file di aiuto della frase "ragged array" può essere un po ' confuso , ma in realtà è piuttosto semplice.

    Un vettore:

     x < - 1:20 

    Un fattore (della stessa lunghezza!) Che definisce i gruppi:

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

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

     tapply(x, y, sum) abcde 10 26 42 58 74 

    Esempi più complessi possono essere gestiti laddove i sottogruppi sono definiti dalle combinazioni univoche di un elenco di diversi fattori. tapply è simile nello spirito alle funzioni split-apply-combine che sono comuni in R ( aggregate , by , ave , ddply , ecc.). Da qui il suo status di pecora nera.

Nella nota a plyr , ecco come le varie funzioni plyr corrispondono alle funzioni di base *apply (dall’intro al documento plyr dalla pagina web plyr http://had.co.nz/plyr/ )

 Base function Input Output plyr function --------------------------------------- aggregate dd ddply + colwise apply aa/l aaply / alply by dl dlply lapply ll llply mapply aa/l maply / mlply replicate ra/l raply / rlply sapply la laply 

Uno degli obiettivi di plyr è fornire convenzioni di denominazione coerenti per ciascuna funzione, codificando i tipi di dati di input e di output nel nome della funzione. Fornisce inoltre coerenza nell’output, in quanto l’output di dlply() è facilmente passabile a ldply() per produrre output utili, ecc.

Concettualmente, apprendere plyr non è più difficile della comprensione delle funzioni di base *apply .

plyr funzioni plyr e plyr hanno sostituito quasi tutte queste funzioni nel mio uso quotidiano. Ma anche dal documento Intro to Plyr:

Le funzioni correlate tapply e sweep non hanno alcuna funzione corrispondente in plyr e restano utili. merge è utile per combinare i riepiloghi con i dati originali.

Dalla diapositiva 21 di http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy :

applicare, sapientemente, lapply, per, aggregare

(Si spera che sia chiaro che l’ apply corrisponde a @ Hadley e che l’ aggregate corrisponde al aaply di @ Hadley ecc. La diapositiva 20 della stessa diapositiva chiarirà se non si ottiene da questa immagine.)

(a sinistra viene inserito, in alto viene visualizzato)

Per prima cosa inizia con l’eccellente risposta di Joran – dubitare che tutto possa migliorare.

Quindi i seguenti mnemonici possono aiutare a ricordare le distinzioni tra ciascuno. Mentre alcuni sono ovvi, altri potrebbero esserlo di meno — per questi troverai una giustificazione nelle discussioni di Joran.

mnemonics

  • lapply è una lista che si applica che agisce su una lista o vettore e restituisce una lista.
  • sapply è un semplice lapply (la funzione predefinita è restituire un vettore o una matrice quando ansible)
  • vapply è un’applicazione verificata (consente di prespecificare il tipo di object restituito)
  • rapply è una domanda ricorsiva per liste annidate, cioè liste all’interno di elenchi
  • tapply è un tag applicato dove i tag identificano i sottoinsiemi
  • apply is generic : applica una funzione alle righe o alle colonne di una matrice (o, più in generale, alle dimensioni di un array)

Costruire lo sfondo giusto

Se usare la famiglia apply sembra ancora un po ‘alieno, potrebbe darsi che ti manchi un punto di vista chiave.

Questi due articoli possono aiutare. Forniscono il background necessario per motivare le tecniche di programmazione funzionale fornite dalla famiglia di funzioni apply .

Gli utenti di Lisp riconosceranno immediatamente il paradigma. Se non hai familiarità con Lisp, una volta che hai capito la FP, avrai acquisito un potente punto di vista per l’uso in R – e apply avrà molto più senso.

  • Advanced R: Functional Programming , di Hadley Wickham
  • Semplice programmazione funzionale in R , di Michael Barton

Da quando ho capito che (le eccellenti) risposte di questo post mancano e spiegazioni aggregate . Ecco il mio contributo.

DI

La funzione per, come indicato nella documentazione può essere, come un “wrapper” per tapply . Il potere di deriva quando vogliamo calcolare un compito che tapply non può gestire. Un esempio è 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 stampiamo questi due oggetti, ct e cb , "essenzialmente" abbiamo gli stessi risultati e le uniche differenze sono nel modo in cui sono mostrate e nei diversi attributi di class , rispettivamente per cb e array per ct .

Come ho detto, il potere di sorgere 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 il summary di tutte le variabili in iris lungo il fattore Species ": ma R non può farlo perché non sa come gestire.

Con la funzione by R si invia un metodo specifico per data frame class data frame e si lascia che la funzione di summary funzioni anche se la lunghezza del primo argomento (e anche il tipo) sono diversi.

 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. È un object di class by quello lungo Species (diciamo, per ciascuno di essi) calcola il summary di ogni variabile.

Si noti che se il primo argomento è un data frame , la funzione inviata deve avere un metodo per quella class di oggetti. Per esempio, usiamo questo codice con la funzione mean , avremo questo codice che non ha alcun senso:

  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 

AGGREGATO

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

 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 immediate sono che il secondo argomento di aggregate deve essere un elenco mentre tapply può (non obbligatorio) essere un elenco e che l'output di aggregate è un frame di dati mentre quello di tapply è un array .

Il potere aggregate è che può gestire facilmente sottoinsiemi dei dati con argomento subset e che ha anche metodi per oggetti ts e formula .

Questi elementi rendono l' aggregate più facile da utilizzare con quel 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 

Possiamo ottenere lo stesso risultato con tapply ma la syntax è leggermente più difficile e l'output (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 altre volte in cui non possiamo usare o tapply e dobbiamo usare l' 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 chiamata, ma dobbiamo calcolare la media lungo il Month per ogni elemento e quindi combinarli (notare anche che dobbiamo chiamare na.rm = TRUE , perché i metodi della formula della funzione di aggregate ha come impostazione predefinita 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 non riusciamo ad ottenere ciò, infatti la seguente chiamata di funzione restituisce un errore (ma molto probabilmente è legato alla funzione fornita, 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 class (e quindi come viene mostrato / stampato e non solo - esempio, come suddividerlo) object:

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

Il codice precedente raggiunge lo stesso objective e i risultati, ad alcuni punti quale strumento da utilizzare è solo una questione di gusti e bisogni personali; i due oggetti precedenti hanno esigenze molto diverse in termini di subsetting.

Ci sono molte ottime risposte che discutono le differenze nei casi d’uso per ciascuna funzione. Nessuna delle risposte discute le differenze nelle prestazioni. Questo è ragionevole perché varie funzioni si aspettano vari input e producono vari output, tuttavia molti di essi hanno un objective generale comune da valutare per serie / gruppi. La mia risposta si concentrerà sulle prestazioni. A causa di quanto sopra la creazione di input dai vettori è inclusa nei tempi, anche la funzione apply non viene misurata.

Ho testato due diverse funzioni sum e length contemporaneamente. Il volume testato è 50M in ingresso e 50K in uscita. Ho anche incluso due pacchetti attualmente popolari che non erano molto usati al momento della domanda, data.table e dplyr . Entrambi valgono sicuramente la pena di guardare se si sta puntando a una buona prestazione.

 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 

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

esterno

outer è una funzione molto utile nascosta come una più banale. Se leggi l’aiuto per 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], ...). 

il che fa sembrare che questo sia utile solo per cose di tipo algebra lineare. Tuttavia, può essere usato molto come mapply per applicare una funzione a due vettori di input. La differenza è che mapply applicherà la funzione ai primi due elementi e quindi ai secondi due ecc, mentre l’ outer applicherà la funzione a ogni combinazione di un elemento dal primo vettore e uno dal secondo. Per 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 

L’ho usato personalmente quando ho un vettore di valori e un vettore di condizioni e desidero vedere quali valori soddisfano le condizioni.

eapply

eapply è come lapply eccetto che piuttosto che applicare una funzione a ogni elemento di una lista, applica una funzione a ogni elemento di un ambiente. Ad esempio se si desidera trovare un elenco di funzioni definite dall’utente nell’ambiente 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 non lo uso molto, ma se stai costruendo molti pacchetti o crei molti ambienti potrebbe tornare utile.

Vale forse la pena menzionare ave . tapply è il cugino amichevole di tapply . Restituisce i risultati in un modulo che puoi ricolbind direttamente al tuo frame dei dati.

 dfr < - data.frame(a=1:20, f=rep(LETTERS[1:5], each=4)) means <- tapply(dfr$a, dfr$f, mean) ## ABCDE ## 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 ## afm 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 funzioni come ave per i frame di dati interi (dato che è come tapply per i frame di dati). Ma puoi fudge:

 dfr$foo < - ave(1:nrow(dfr), dfr$f, FUN=function(x) { x <- dfr[x,] sum(x$m*x$m2) }) dfr ## afm 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 ## ... 

Recentemente ho scoperto la funzione di sweep piuttosto utile e la aggiungo qui per motivi di completezza:

spazzare

L’idea di base è di passare attraverso un array di righe o colonne e restituire un array modificato. Un esempio lo renderà chiaro (fonte: datacamp ):

Diciamo che hai una matrice e vuoi standardizzarla in base alla colonna:

 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)