Da quando ho capito che (l'eccellente) risposte a questo post mancano bye aggregatespiegazioni. Ecco il mio contributo.
DI
La byfunzione, come indicato nella documentazione, può essere però un "wrapper" per tapply. Il potere di bysorge quando vogliamo calcolare un compito che tapplynon è in grado di 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 cte cb"essenzialmente" otteniamo gli stessi risultati e le uniche differenze sono nel modo in cui sono mostrati e nei diversi classattributi, rispettivamente byper cbe arrayperct .
Come ho già detto, il potere di bysorge quando non possiamo usarlotapply ; 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, ad esempio "vogliamo calcolare il summary di tutte le variabili irislungo il fattoreSpecies ": ma R semplicemente non può farlo perché non sa come gestirlo.
Con la byfunzione R invia un metodo specifico per la data frameclasse e poi lascia chesummary funzione funzioni anche se la lunghezza del primo argomento (e anche il tipo) sono diverse.
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 oggetto di classe byche insiemeSpecies (diciamo, per ciascuno di essi) calcola il valore summarydi ciascuna variabile.
Notare che se il primo argomento è a data frame, la funzione spedita deve avere un metodo per quella classe di oggetti. Ad esempio, usiamo questo codice con la meanfunzione che 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
aggregatepuò essere visto come un altro modo diverso di utilizzarlo tapplyse lo utilizziamo 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 ditapply è unarray .
Il potere di aggregateè che può gestire facilmente sottoinsiemi di dati con subsetargomento e che ha metodi per tsoggetti eformula pure.
Questi elementi aggregatefacilitano il lavoro tapplyin 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 con tapplyma la sintassi è 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 byo tapplye 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 tapplyin una chiamata, ma dobbiamo calcolare la media insieme Monthper ogni elemento e quindi combinarli (nota anche che dobbiamo chiamare il na.rm = TRUE, perché i formulametodi della aggregatefunzione hanno di default 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 bynon possiamo proprio conseguire che in effetti la seguente chiamata di funzione restituisce un errore (ma molto probabilmente è correlata 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 nell'oggetto class (e quindi come viene mostrato / stampato e non solo - esempio, come sottoinsediarlo):
byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)
Il codice precedente raggiunge lo stesso obiettivo e gli stessi risultati, in alcuni punti quale strumento usare è solo una questione di gusti e bisogni personali; i due oggetti precedenti hanno esigenze molto diverse in termini di subsetting.
*apply()eby. plyr (almeno per me) sembra molto più coerente in quanto so sempre esattamente quale formato di dati si aspetta e esattamente cosa sputerà. Questo mi risparmia molta seccatura.