funzioni di raggruppamento (Tapply, da, aggregato) e il * applicare famiglia
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 è ...
-
sapply(vec, f)
: ingresso è un vettore. uscita è un vettore / matrice, dove elementoi
èf(vec[i])
, dando una matrice sef
ha un'uscita più elementi -
lapply(vec, f)
:? Ugualesapply
, ma la produzione è un elenco -
apply(matrix, 1/2, f)
: input è una matrice. uscita è un vettore, quale elementoi
è f (riga / col i della matrice) -
tapply(vector, grouping, f)
: uscita è una matrice / matrice, in cui un elemento della matrice / matrice è il valore dif
ag
raggruppamento del vettore, eg
viene spinto ai nomi riga / col -
by(dataframe, grouping, f)
: lasciareg
essere un raggruppamento. applicaref
ad ogni colonna del gruppo / dataframe. abbastanza stampare il raggruppamento ed il valore dif
ad ogni colonna. -
aggregate(matrix, grouping, f)
:. Simile aby
, 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
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 consideraresapply
.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
conSIMPLIFY = FALSE
, quindi è garantito per restituire un elencoMap(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 day
: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
.
??plyr
e reshape
hanno sostituito quasi tutte queste funzioni in ogni mio uso giorno. Ma, anche dal documento Introduzione a Plyr:
funzioni correlate
tapply
esweep
hanno funzioneplyr
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 / :
(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 semplicelapply
(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
- Avanzata R: programmazione funzionale , da Hadley Wickham
- semplice programmazione funzionale in R , da Michael Barton
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 ??strong>
L'idea di base è quella di spazzata ??em> 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)