Chaque fois que je veux faire quelque chose "map" py dans R, j'essaie généralement d'utiliser une fonction de la famille apply
.
Cependant, je n'ai jamais bien compris les différences qui les séparent - comment {sapply
, lapply
, etc.} appliquent la fonction à l'entrée/à l'entrée groupée, à quoi ressemblera la sortie, ou même ce que l’entrée peut être - alors souvent, je les passe en revue jusqu’à ce que j’obtienne ce que je veux.
Quelqu'un peut-il expliquer comment utiliser lequel quand?
Ma compréhension actuelle (probablement incorrecte/incomplète) est ...
sapply(vec, f)
: input est un vecteur. la sortie est un vecteur/matrice, où l'élément i
est f(vec[i])
, ce qui vous donne une matrice si f
a une sortie multi-éléments
lapply(vec, f)
: identique à sapply
, mais la sortie est une liste?
apply(matrix, 1/2, f)
: input est une matrice. la sortie est un vecteur, où l'élément i
est f (ligne/colonne i de la matrice)tapply(vector, grouping, f)
: output est une matrice/un tableau, où un élément de la matrice/du tableau a la valeur de f
dans un groupe g
du vecteur et où g
est poussé vers la ligne/noms de colby(dataframe, grouping, f)
: Soit g
un groupe. applique f
à chaque colonne du groupe/cadre de données. joli imprimer le groupement et la valeur de f
à chaque colonne.aggregate(matrix, grouping, f)
: semblable à by
, mais au lieu d’imprimer assez la sortie, l’agrégat colle tout dans une trame de données.Question secondaire: je n’ai toujours pas appris le plyr ou le remodelage - est-ce que plyr
ou reshape
remplacerait tout cela?
R possède de nombreuses fonctions * apply qui sont décrites de manière pertinente dans les fichiers d’aide (par exemple, ?apply
). Cependant, ils sont assez nombreux pour que les utilisateurs débutants aient du mal à décider laquelle convient le mieux à leur situation ou même à se souvenir de toutes. Ils peuvent avoir le sentiment général que "je devrais utiliser une fonction * apply ici", mais il peut être difficile de les garder tous droits au début.
Malgré le fait (noté dans d'autres réponses) qu'une grande partie des fonctionnalités de la famille * apply sont couvertes par le package extrêmement populaire plyr
, les fonctions de base restent utiles et valent la peine d'être connues.
Cette réponse est censée agir comme une sorte de indicateur pour les nouveaux utilisateurs afin de les orienter vers la fonction * appropriée appropriée pour leur problème particulier. Notez que ceci n'est pas destiné à simplement régurgiter ou remplacer la documentation R! L'espoir est que cette réponse vous aide à décider quelle fonction * apply convient à votre situation et qu'il vous appartient ensuite de la rechercher plus en profondeur. À une exception près, les différences de performances ne seront pas traitées.
apply - Lorsque vous souhaitez appliquer une fonction aux lignes ou aux colonnes d'une matrice (et d'analogues de dimension supérieure); généralement déconseillé pour les trames de données, car il forcera d’abord à former une 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[*, , ] - 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
Si vous voulez des moyennes ligne/colonne ou des sommes pour une matrice 2D, assurez-vous d’examiner les options colMeans
, rowMeans
, colSums
, rowSums
hautement optimisées.
lapply - Lorsque vous souhaitez appliquer une fonction à chaque élément d'une liste, vous obtenez une liste.
C’est le cheval de bataille de beaucoup des autres fonctions * apply. Détachez leur code et vous trouverez souvent lapply
en dessous.
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 - Lorsque vous souhaitez appliquer une fonction à chaque élément d'une liste, mais que vous souhaitez un vector retour, plutôt qu'une liste.
Si vous vous trouvez en train de taper unlist(lapply(...))
, arrêtez-vous et considérez 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
Dans les utilisations plus avancées de sapply
, il tentera de contraindre le résultat à un tableau multidimensionnel, le cas échéant. Par exemple, si notre fonction retourne des vecteurs de même longueur, sapply
les utilisera comme colonnes d'une matrice:
sapply(1:5,function(x) rnorm(3,x))
Si notre fonction retourne une matrice à 2 dimensions, sapply
fera essentiellement la même chose, en traitant chaque matrice retournée comme un seul vecteur long:
sapply(1:5,function(x) matrix(x,2,2))
Sauf si nous spécifions simplify = "array"
, dans ce cas, il utilisera les matrices individuelles pour créer un tableau multidimensionnel:
sapply(1:5,function(x) matrix(x,2,2), simplify = "array")
Chacun de ces comportements dépend évidemment de notre fonction qui renvoie des vecteurs ou des matrices de même longueur ou de même dimension.
vapply - Lorsque vous voulez utiliser sapply
mais que vous avez peut-être besoin de tirer un peu plus de vitesse de votre code.
Pour vapply
, vous donnez à R un exemple de ce que votre fonction retournera, ce qui peut permettre de gagner du temps en forçant les valeurs renvoyées à tenir dans un seul vecteur atomique.
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 - Lorsque vous avez plusieurs structures de données (par exemple, des vecteurs, des listes) et que vous souhaitez appliquer une fonction aux premiers éléments de chaque , puis les deuxièmes éléments de chacun, etc., contraignant le résultat à un vecteur/tableau comme dans sapply
.
Ceci est multivarié dans le sens où votre fonction doit accepter plusieurs arguments.
#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
Carte - n wrapper to mapply
avec SIMPLIFY = FALSE
, il est donc garanti de renvoyer une liste. =
Map(sum, 1:5, 1:5, 1:5)
[[1]]
[1] 3
[[2]]
[1] 6
[[3]]
[1] 9
[[4]]
[1] 12
[[5]]
[1] 15
rapply - Lorsque vous souhaitez appliquer une fonction à chaque élément d'une liste imbriquée structure, récursivement.
Pour vous donner une idée de la rareté de rapply
, je l'ai oublié lorsque j'ai posté cette réponse pour la première fois! Évidemment, je suis sûr que beaucoup de gens l'utilisent, mais le YMMV. rapply
est mieux illustré par une fonction définie par l'utilisateur à appliquer:
# 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 - Pour quand appliquer une fonction à des sous-ensembles d'un vecteur et les sous-ensembles sont définis par un autre vecteur, généralement un facteur.
Le mouton noir de la * appliquer famille, en quelque sorte. L'utilisation par le fichier d'aide de l'expression "ragged array" peut être un peu déroutant , mais c'est en fait assez simple.
Un vecteur:
x <- 1:20
Un facteur (de même longueur!) Définissant les groupes:
y <- factor(rep(letters[1:5], each = 4))
Additionnez les valeurs dans x
dans chaque sous-groupe défini par y
:
tapply(x, y, sum)
a b c d e
10 26 42 58 74
Des exemples plus complexes peuvent être traités lorsque les sous-groupes sont définis par les combinaisons uniques d’une liste de plusieurs facteurs. tapply
est similaire dans l'esprit aux fonctions split-apply-combine communes à R (aggregate
, by
, ave
, ddply
, etc.) D'où son statut de mouton noir.
Sur la note latérale, voici comment les diverses fonctions plyr
correspondent aux fonctions de base *apply
(de l’introduction au document plyr de la page Web plyr 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
L'un des objectifs de plyr
est de fournir des conventions de dénomination cohérentes pour chacune des fonctions, en codant les types de données d'entrée et de sortie dans le nom de la fonction. Il fournit également une cohérence dans la sortie, en ce sens que la sortie de dlply()
est facilement transmissible à ldply()
pour produire une sortie utile, etc.
Conceptuellement, apprendre plyr
n'est pas plus difficile que de comprendre les fonctions de base *apply
.
Les fonctions plyr
et reshape
ont remplacé la quasi-totalité de ces fonctions dans mon utilisation quotidienne. Mais aussi du document Intro to Plyr:
Les fonctions associées
tapply
etsweep
n'ont pas de fonction correspondante dansplyr
et restent utiles.merge
est utile pour combiner des résumés avec les données d'origine.
De la diapositive 21 de http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy :
(Espérons qu'il soit clair que apply
correspond à @ [Hadry's aaply
et aggregate
correspond à @ Hadley's ddply
etc. La diapositive 20 du même diaporama vous expliquera si vous n'obtenez pas à partir de cette image.)
(à gauche est entrée, en haut est sortie)
Commençons par excellente réponse de Joran - rien ne permet de douter de l'amélioration.
Ensuite, les mnémoniques suivants peuvent aider à rappeler les distinctions entre chacun. Alors que certains sont évidents, d'autres le sont peut-être moins - vous trouverez une justification dans les discussions de Joran.
Mnémoniques
lapply
est une liste qui agit sur une liste ou un vecteur et renvoie une liste.sapply
est un simple lapply
(la fonction retourne par défaut un vecteur ou une matrice)vapply
est un vérifié applique (permet de spécifier le type d'objet renvoyé)rapply
est un récursif s'applique aux listes imbriquées, c'est-à-dire aux listes au sein de listestapply
est un étiqueté s'applique là où les étiquettes identifient les sous-ensemblesapply
est générique : applique une fonction aux lignes ou aux colonnes d'une matrice (ou plus généralement aux dimensions d'un tableau).Construire le bon fond
Si vous utilisez toujours la famille apply
pour vous sentir un peu étranger, il se peut que vous manquiez d'un point de vue essentiel.
Ces deux articles peuvent aider. Elles fournissent les bases nécessaires pour motiver les techniques de programmation fonctionnelle fournies par la famille apply
.
Les utilisateurs de LISP reconnaîtront immédiatement le paradigme. Si vous n'êtes pas familier avec LISP, une fois que vous aurez compris l'esprit de FP, vous aurez acquis un puissant point de vue pour une utilisation dans R - et apply
aura plus de sens.
Depuis que j’ai réalisé que (les très excellentes) réponses de cet article manquaient de by
et aggregate
explications. Voici ma contribution.
La fonction by
, comme indiqué dans la documentation, peut toutefois être utilisée comme un "wrapper" pour tapply
. La puissance de by
apparaît lorsque nous voulons calculer une tâche que tapply
ne peut pas gérer. Un exemple est ce 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
Si nous imprimons ces deux objets, ct
et cb
, nous avons "essentiellement" les mêmes résultats et les seules différences sont dans la façon dont ils sont affichés et les différents attributs class
, respectivement by
pour cb
et array
pour ct
.
Comme je l'ai dit, le pouvoir de by
apparaît lorsque nous ne pouvons pas utiliser tapply
; le code suivant est un exemple:
tapply(iris, iris$Species, summary )
Error in tapply(iris, iris$Species, summary) :
arguments must have same length
R dit que les arguments doivent avoir les mêmes longueurs, disons "nous voulons calculer la summary
de toutes les variables dans iris
le long du facteur Species
": mais R ne peut tout simplement pas le faire car il ne sait pas comment gérer.
Avec la fonction by
R, envoyez une méthode spécifique pour la classe data frame
puis laissez la fonction summary
fonctionner même si la longueur du premier argument (et du type) est différente.
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
cela fonctionne vraiment et le résultat est très surprenant. C'est un objet de la classe by
que le long de Species
(par exemple, pour chacun d'eux) calcule le summary
de chaque variable.
Notez que si le premier argument est un data frame
, la fonction distribuée doit avoir une méthode pour cette classe d'objets. Par exemple si nous utilisons ce code avec la fonction mean
, nous aurons ce code qui n’a aucun sens:
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
aggregate
peut être vu comme un autre mode d'utilisation tapply
si nous l'utilisons de cette manière.
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
Les deux différences immédiates sont que le deuxième argument de aggregate
doit est une liste alors que tapply
peut (non obligatoire) est une liste et que le la sortie de aggregate
est une trame de données alors que celle de tapply
est une array
.
La puissance de aggregate
réside dans le fait qu’il peut gérer facilement des sous-ensembles de données avec l’argument subset
et qu’il dispose de méthodes pour les objets ts
et formula
.
Ces éléments rendent aggregate
plus facile à utiliser avec tapply
dans certaines situations. Voici quelques exemples (disponibles dans la documentation):
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
Nous pouvons réaliser la même chose avec tapply
mais la syntaxe est légèrement plus dure et la sortie (dans certaines circonstances) moins lisible:
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
Il y a d'autres fois où nous ne pouvons pas utiliser by
ou tapply
et nous devons utiliser 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
Nous ne pouvons pas obtenir le résultat précédent avec tapply
dans un appel, mais nous devons calculer la moyenne le long de Month
pour chaque élément, puis les combiner (notez également que nous devons appeler le na.rm = TRUE
, parce que les méthodes formula
de la fonction aggregate
ont par défaut le 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
alors que avec by
nous ne pouvons tout simplement pas y parvenir. En fait, l'appel de fonction suivant renvoie une erreur (mais il est très probablement lié à la fonction fournie, mean
):
by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE)
D'autres fois, les résultats sont les mêmes et les différences ne sont que dans la classe (et ensuite, comment cela est montré/imprimé et pas seulement - par exemple, comment le sous-créer):
byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)
Le code précédent atteignait le même objectif et les mêmes résultats, à certains moments, quel outil utiliser était simplement une question de goûts et de besoins personnels; les deux objets précédents ont des besoins très différents en termes de sous-ensemble.
Il y a beaucoup de bonnes réponses qui traitent des différences dans les cas d'utilisation pour chaque fonction. Aucune des réponses ne discute des différences de performance. C’est raisonnable, car diverses fonctions s’attendent à diverses entrées et produisent diverses sorties, mais la plupart d’entre elles ont un objectif général commun d’évaluer par séries/groupes. Ma réponse va se concentrer sur la performance. En raison de ce qui précède, la création d’entrée à partir des vecteurs est incluse dans la synchronisation, de même que la fonction apply
n’est pas mesurée.
J'ai testé deux fonctions différentes sum
et length
à la fois. Le volume testé est 50M en entrée et 50K en sortie. J'ai également inclus deux paquets actuellement populaires qui n'étaient pas largement utilisés au moment où la question a été posée, data.table
et dplyr
. Les deux valent vraiment la peine d'être regardés si vous visez de bonnes performances.
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
Malgré toutes les bonnes réponses données ici, il reste 2 fonctions de base qui méritent d’être mentionnées, la fonction utile outer
et la fonction obscure eapply
.
externe
outer
est une fonction très utile cachée de manière plus banale. Si vous lisez l'aide pour outer
sa description indique:
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], ...).
ce qui donne l’impression que cela n’est utile que pour les choses de type algèbre linéaire. Cependant, il peut être utilisé comme mapply
pour appliquer une fonction à deux vecteurs d'entrées. La différence est que mapply
appliquera la fonction aux deux premiers éléments, puis aux deux autres, etc., tandis que outer
appliquera la fonction à chaque combinaison d'un élément du premier vecteur et d'un du second. . Par exemple:
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
J'ai personnellement utilisé cela lorsque j'ai un vecteur de valeurs et un vecteur de conditions et que je souhaite voir quelles valeurs correspondent à quelles conditions.
en avance
eapply
ressemble à lapply
sauf que plutôt que d'appliquer une fonction à chaque élément d'une liste, il applique une fonction à chaque élément d'un environnement. Par exemple, si vous souhaitez rechercher une liste de fonctions définies par l'utilisateur dans l'environnement global:
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
Franchement, je ne l'utilise pas beaucoup, mais si vous construisez beaucoup de paquets ou créez beaucoup d'environnements, cela peut être utile.
Il est peut-être intéressant de mentionner ave
. ave
est le cousin amical de tapply
. Il renvoie les résultats dans un formulaire que vous pouvez réintégrer directement dans votre cadre de données.
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
## ...
Il n'y a rien dans le package de base qui fonctionne comme ave
pour des trames de données entières (car by
est semblable à tapply
pour des trames de données). Mais vous pouvez le tromper:
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
## ...
J'ai récemment découvert la fonction plutôt utile sweep
et l'ajoute ici par souci d'exhaustivité:
balayage
L'idée de base est de balayer dans un tableau, ligne par colonne ou par colonne, et de renvoyer un tableau modifié. Un exemple le clarifiera (source: datacamp ):
Disons que vous avez une matrice et que vous voulez standardiser la colonne par colonne:
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: pour cet exemple simple, le même résultat peut bien sûr être obtenu plus facilement enapply(dataPoints, 2, scale)