Chaque fois que je veux faire quelque chose de "map" py dans R, j'essaie généralement d'utiliser une fonction dans la apply
famille.
Cependant, je ne l' ai jamais très bien compris les différences entre eux - comment { sapply
, lapply
etc.} appliquer la fonction à l'entrée / entrée groupée, ce que la sortie ressemblera, ou même ce que l'entrée peut être - donc je souvent il suffit de les parcourir 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)
: l'entrée est un vecteur. la sortie est un vecteur / matrice, où l'élémenti
estf(vec[i])
, vous donnant une matrice sif
a une sortie multi-élémentslapply(vec, f)
: identique àsapply
, mais la sortie est une liste?apply(matrix, 1/2, f)
: l'entrée est une matrice. la sortie est un vecteur, où l'élémenti
est f (ligne / col i de la matrice)tapply(vector, grouping, f)
: la sortie est une matrice / tableau, où un élément de la matrice / tableau est la valeur def
lors d'un regroupementg
du vecteur, etg
est poussé vers les noms de ligne / colby(dataframe, grouping, f)
:g
soit un groupement. s'appliquentf
à chaque colonne du groupe / trame de données. joli imprimer le regroupement et la valeur def
à chaque colonne.aggregate(matrix, grouping, f)
: similaire àby
, mais au lieu d'imprimer joliment la sortie, l'agrégat colle tout dans une trame de données.
Question Côté: Je n'ai pas encore appris plyr ou Reshape - serait plyr
ou reshape
remplacer tous ces entièrement?
*apply()
etby
. plyr (du moins pour moi) semble beaucoup plus cohérent dans la mesure où je sais toujours exactement quel format de données il attend et exactement ce qu'il va cracher. Cela m'évite beaucoup de tracas.doBy
et les capacités de sélection et d'application dedata.table
.sapply
est justelapply
avec l'ajout desimplify2array
sur la sortie.apply
contraint au vecteur atomique, mais la sortie peut être un vecteur ou une liste.by
divise les cadres de données en sous-cadres de données, mais il n'utilise pasf
les colonnes séparément. Ce n'est que s'il existe une méthode pour la classe 'data.frame'f
que la colonne peut être appliquéeby
.aggregate
est générique, donc différentes méthodes existent pour différentes classes du premier argument.Réponses:
R a de nombreuses fonctions * apply qui sont décrites de manière efficace dans les fichiers d'aide (par exemple
?apply
). Cependant, il y en a suffisamment pour que les utilisateurs débutants aient du mal à décider lequel convient à leur situation ou même à s'en souvenir. Ils peuvent avoir le sentiment général que «je devrais utiliser une fonction * appliquer ici», mais il peut être difficile de les garder tous droits au début.Malgré le fait (noté dans d'autres réponses) que la plupart des fonctionnalités de la famille * apply sont couvertes par le
plyr
package extrêmement populaire , les fonctions de base restent utiles et méritent d'être connues.Cette réponse est destinée à agir comme une sorte de panneau pour les nouveaux utilisateurs afin de les diriger vers la fonction * appliquer correcte pour leur problème particulier. Remarque, 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 * appliquer convient à votre situation, puis c'est à vous de la rechercher davantage. À une exception près, les différences de performances ne seront pas traitées.
appliquer - Lorsque vous souhaitez appliquer une fonction aux lignes ou colonnes d'une matrice (et aux analogues de dimension supérieure); généralement déconseillé pour les trames de données car il contraindra d'abord une matrice.
Si vous voulez ligne / colonne ou des moyens de sommes pour une matrice 2D, assurez - vous d'enquêter sur le hautement optimisé, rapide comme l' éclair
colMeans
,rowMeans
,colSums
,rowSums
.lapply - Lorsque vous souhaitez appliquer une fonction à chaque élément d'une liste tour à tour et récupérer une liste.
C'est le cheval de bataille de la plupart des autres fonctions * apply. Décollez leur code et vous le trouverez souvent en
lapply
dessous.sapply - Lorsque vous voulez appliquer une fonction à tour de rôle à chaque élément d'une liste, mais que vous voulez un vecteur de retour, plutôt qu'une liste.
Si vous vous retrouvez à taper
unlist(lapply(...))
, arrêtez-vous et réfléchissezsapply
.Dans des utilisations plus avancées,
sapply
il tentera de contraindre le résultat à un tableau multidimensionnel, le cas échéant. Par exemple, si notre fonction renvoie des vecteurs de même longueur,sapply
les utilisera comme colonnes d'une matrice:Si notre fonction renvoie une matrice bidimensionnelle,
sapply
fera essentiellement la même chose, en traitant chaque matrice retournée comme un seul vecteur long:Sauf indication contraire
simplify = "array"
, auquel cas il utilisera les matrices individuelles pour construire un tableau multidimensionnel:Chacun de ces comportements est bien sûr tributaire de notre fonction renvoyant des vecteurs ou des matrices de même longueur ou dimension.
vapply - Lorsque vous souhaitez utiliser
sapply
mais que vous avez peut-être besoin de réduire la vitesse de votre code.Pour
vapply
, vous donnez essentiellement à R un exemple de ce que votre fonction renverra, ce qui peut gagner du temps en contraignant les valeurs renvoyées à tenir dans un seul vecteur atomique.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 chacun, puis aux seconds éléments de chacun, etc., en contraignant le résultat à un vecteur / tableau comme dans
sapply
.Ceci est multivarié dans le sens où votre fonction doit accepter plusieurs arguments.
Map - Un wrapper
mapply
avecSIMPLIFY = FALSE
, il est donc garanti de renvoyer une liste.rapply - Pour quand vous voulez appliquer une fonction à chaque élément d'une structure de liste imbriquée , récursivement.
Pour vous donner une idée de la rareté
rapply
, je l'ai oublié lors de la première publication de cette réponse! Évidemment, je suis sûr que beaucoup de gens l'utilisent, mais YMMV.rapply
est mieux illustré avec une fonction définie par l'utilisateur à appliquer:tapply - Lorsque vous souhaitez appliquer une fonction à des sous - ensembles d'un vecteur et que les sous-ensembles sont définis par un autre vecteur, généralement un facteur.
Les moutons noirs de la famille * s'appliquent, en quelque sorte. L'utilisation par le fichier d'aide de l'expression «tableau irrégulier» peut être un peu déroutante , mais elle est en fait assez simple.
Un vecteur:
Un facteur (de même longueur!) Définissant les groupes:
Additionnez les valeurs
x
dans chaque sous-groupe défini pary
:Des exemples plus complexes peuvent être traités où les sous-groupes sont définis par les combinaisons uniques d'une liste de plusieurs facteurs.
tapply
est similaire dans l' esprit à la scission-apply-combiner des fonctions qui sont communes dans R (aggregate
,by
,ave
,ddply
, etc.) D' où son statut de mouton noir.la source
by
c'est purement fendu et queaggregate
c'esttapply
à leur cœur . Je pense que le mouton noir fait un excellent tissu.aggregate
etby
aussi? (Je les comprends enfin après votre description!, Mais ils sont assez courants, il pourrait donc être utile de les séparer et d'avoir des exemples spécifiques pour ces deux fonctions.)aggregate
,by
, etc. , sont basés sur * appliquer des fonctions, la façon dont vous abordez les utiliser est assez différent du point de vue des utilisateurs qu'ils doivent être résumés dans une réponse distincte. Je peux essayer cela si j'ai le temps, ou peut-être que quelqu'un d'autre me battra et gagnera mon vote positif.?Map
tant que parent demapply
data.frame
s sont une partie absolument centrale de R et en tantlist
qu'objet sont fréquemment manipulés en utilisant enlapply
particulier. Ils agissent également comme des conteneurs pour regrouper des vecteurs / facteurs de nombreux types dans un ensemble de données rectangulaire traditionnel. Bien quedata.table
etplyr
puissent ajouter un certain type de syntaxe que certains pourraient trouver plus confortables, ils étendent et agissentdata.frame
respectivement sur s.En marge , voici comment les différentes
plyr
fonctions correspondent aux*apply
fonctions de base (de l'intro au document plyr de la page web plyr http://had.co.nz/plyr/ )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 dedlply()
est facilement passableldply()
pour produire une sortie utile, etc.Conceptuellement, l'apprentissage
plyr
n'est pas plus difficile que la compréhension des*apply
fonctions de base .plyr
et lesreshape
fonctions ont remplacé presque toutes ces fonctions dans mon utilisation quotidienne. Mais, également du document Intro to Plyr:la source
*apply()
famille de fonctions. Pour moi,ddply()
c'était très intuitif car je connaissais les fonctions d'agrégation SQL.ddply()
est devenu mon marteau pour résoudre de nombreux problèmes, dont certains auraient pu être mieux résolus avec d'autres commandes.plyr
fonctions est similaire aux*apply
fonctions, donc si vous pouvez en faire une, vous pouvez faire l'autre, mais lesplyr
fonctions sont plus faciles à retenir. Mais je suis totalement d'accord sur leddply()
marteau!join()
fonction qui effectue des tâches similaires à la fusion. C'est peut-être plus au point de le mentionner dans le contexte de la plyr.eapply
vapply
et les inconvénients desapply
. Un avantage majeurvapply
est qu'il applique le type et la longueur de sortie, vous vous retrouverez donc avec la sortie attendue exacte ou une erreur informative. D'un autre côté,sapply
essaiera de simplifier la sortie en suivant des règles qui ne sont pas toujours évidentes, et retombera dans une liste sinon. Par exemple, essayez de prédire le type de sortie cela produira:sapply(list(1:5, 6:10, matrix(1:4, 2)), function(x) head(x, 1))
. Et alorssapply(list(matrix(1:4, 2), matrix(1:4, 2)), ...)
?De la diapositive 21 de http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy :
(J'espère qu'il est clair que cela
apply
correspond à @ Hadleyaaply
etaggregate
correspond à @ Hadley,ddply
etc. La diapositive 20 du même diaporama clarifiera si vous ne l'obtenez pas de cette image.)(à gauche est entrée, en haut est sortie)
la source
Commençons d'abord par l'excellente réponse de Joran - tout ce qui est douteux peut améliorer cela.
Ensuite, les mnémoniques suivantes peuvent aider à se souvenir des distinctions entre chacune. Alors que certains sont évidents, d'autres peuvent l'être moins --- pour ceux-ci, vous trouverez une justification dans les discussions de Joran.
Mnémotechnique
lapply
est une application de liste qui agit sur une liste ou un vecteur et renvoie une liste.sapply
est simplelapply
(la fonction renvoie par défaut un vecteur ou une matrice lorsque cela est possible)vapply
est une application vérifiée (permet de spécifier le type d'objet de retour)rapply
est une application récursive pour les listes imbriquées, c'est-à-dire les listes dans les listestapply
est une application balisée où les balises identifient les sous-ensemblesapply
est générique : applique une fonction aux lignes ou colonnes d'une matrice (ou, plus généralement, aux dimensions d'un tableau)Construire le bon arrière-plan
Si l'utilisation de la
apply
famille vous semble encore un peu étrangère, il se peut que vous manquiez un point de vue clé.Ces deux articles peuvent vous aider. Ils fournissent le contexte nécessaire pour motiver les techniques de programmation fonctionnelle fournies par la
apply
famille de fonctions.Les utilisateurs de Lisp reconnaîtront immédiatement le paradigme. Si vous n'êtes pas familier avec Lisp, une fois que vous vous familiariserez avec FP, vous aurez acquis un point de vue puissant pour une utilisation dans R - et cela
apply
aura beaucoup plus de sens.la source
Depuis que je me suis rendu compte que (les très excellentes) réponses de ce post manquent
by
et d'aggregate
explications. Voici ma contribution.PAR
La
by
fonction, comme indiqué dans la documentation, peut cependant être un "wrapper" pourtapply
. La puissance deby
surgit lorsque nous voulons calculer une tâche quitapply
ne peut pas être gérée. Un exemple est ce code:Si nous imprimons ces deux objets,
ct
etcb
, 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érentsclass
attributs, respectivementby
pourcb
etarray
pourct
.Comme je l'ai dit, le pouvoir de
by
surgit lorsque nous ne pouvons pas utilisertapply
; le code suivant en est un exemple:R dit que les arguments doivent avoir les mêmes longueurs, disons "nous voulons calculer la
summary
variable deiris
tout le long du facteurSpecies
": mais R ne peut tout simplement pas faire cela car il ne sait pas comment gérer.Avec la
by
fonction R, distribuez une méthode spécifique pour ladata frame
classe, puis laissez lasummary
fonction fonctionner même si la longueur du premier argument (et le type aussi) est différente.cela fonctionne en effet et le résultat est très surprenant. C'est un objet de classe
by
qui le longSpecies
(disons, pour chacun d'eux) calcule lesummary
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 lamean
fonction, nous aurons ce code qui n'a aucun sens:AGRÉGAT
aggregate
peut être considéré comme un autre mode d'utilisation différenttapply
si nous l'utilisons de cette manière.Les deux différences immédiates sont que le deuxième argument de
aggregate
doit être une liste tandis quetapply
peut (non obligatoire) être une liste et que la sortie deaggregate
est une trame de données tandis que celle detapply
est unarray
.La puissance de
aggregate
est qu'il peut gérer facilement des sous-ensembles de données avecsubset
argument et qu'il a des méthodes pour lests
objets etformula
aussi.Ces éléments
aggregate
facilitent le travail avec celatapply
dans certaines situations. Voici quelques exemples (disponibles dans la documentation):Nous pouvons obtenir la même chose avec
tapply
mais la syntaxe est légèrement plus difficile et la sortie (dans certaines circonstances) moins lisible:Il y a d'autres moments où nous ne pouvons pas utiliser
by
outapply
et nous devons utiliseraggregate
.Nous ne pouvons pas obtenir le résultat précédent avec
tapply
en un seul appel mais nous devons calculer la moyenneMonth
pour chaque élément et ensuite les combiner (notez également que nous devons appeler lena.rm = TRUE
, car lesformula
méthodes de laaggregate
fonction ont par défaut lena.action = na.omit
):alors qu'avec
by
nous, 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 fourniemean
):D'autres fois, les résultats sont les mêmes et les différences ne concernent que la classe (et ensuite comment elle est affichée / imprimée et pas seulement - par exemple, comment la sous-définir).
Le code précédent atteint le même objectif et les mêmes résultats, à certains moments, quel outil utiliser n'est qu'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.
la source
data.frame(tapply(unlist(iris[,-5]),list(rep(iris[,5],ncol(iris[-5])),col(iris[-5])),summary))
c'est une utilisation de tapply. With the right splitting there is nothing you cant do with
tapply. The only thing is it returns a matrix. Please be careful by saying we cant use
tapply`Il y a beaucoup de bonnes réponses qui discutent des différences dans les cas d'utilisation pour chaque fonction. Aucune des réponses ne traite des différences de performances. C'est raisonnable car diverses fonctions attendent diverses entrées et produisent différentes sorties, mais la plupart d'entre elles ont un objectif général commun à é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, la
apply
fonction n'est pas non plus mesurée.J'ai testé deux fonctions différentes
sum
etlength
à la fois. Le volume testé est de 50M en entrée et de 50K en sortie. J'ai également inclus deux packages actuellement populaires qui n'étaient pas largement utilisés au moment où la question a été posée,data.table
etdplyr
. Les deux valent vraiment la peine d'être recherchés si vous visez de bonnes performances.la source
Malgré toutes les bonnes réponses ici, il y a 2 autres fonctions de base qui méritent d'être mentionnées, la
outer
fonction utile et laeapply
fonction obscureextérieur
outer
est une fonction très utile cachée comme plus banale. Si vous lisez l'aide pourouter
sa description dit: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 quemapply
la fonction sera appliquée aux deux premiers éléments, puis les deux autres, etc., tandis queouter
la fonction sera appliquée à chaque combinaison d'un élément du premier vecteur et d'un du second. Par exemple:Je l'ai personnellement utilisé lorsque j'ai un vecteur de valeurs et un vecteur de conditions et que je souhaite voir quelles valeurs répondent à quelles conditions.
empressé
eapply
est similaire,lapply
sauf qu'au lieu 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:Franchement, je ne l'utilise pas beaucoup, mais si vous créez de nombreux packages ou créez de nombreux environnements, cela peut être utile.
la source
Cela vaut peut-être la peine d'être mentionné
ave
.ave
esttapply
le cousin amical. Il renvoie les résultats sous une forme que vous pouvez directement reconnecter à votre bloc de données.Il n'y a rien dans le package de base qui fonctionne comme
ave
pour les trames de données entières (by
commetapply
pour les trames de données). Mais vous pouvez le truquer:la source
J'ai récemment découvert la
sweep
fonction plutôt utile et l'ajoute ici par souci d'exhaustivité:balayage
L'idée de base est de balayer par un row- de tableau ou en colonne et retourner un tableau modifié. Un exemple rendra cela clair (source: datacamp ):
Supposons que vous ayez une matrice et que vous souhaitiez la normaliser en colonnes:
NB: pour cet exemple simple, le même résultat peut bien sûr être obtenu plus facilement en
apply(dataPoints, 2, scale)
la source
sweep
est une fonction d'ordre supérieur comme tous les autres mentionnés ici, par exempleapply
,sapply
,lapply
donc pourrait se poser la même question au sujet de la réponse acceptée avec plus de 1000 upvotes et les exemples qui y sont données. Jetez un œil à l'exemple donnéapply
ici.sweep(matrix(1:6,nrow=2),2,7:9,list)
. C'est généralement plus efficace queapply
parce que là où lesapply
bouclessweep
sont capables d'utiliser des fonctions vectorisées.Dans le package d' effondrement récemment publié sur CRAN, j'ai tenté de compresser la plupart des fonctionnalités d'application courantes en seulement 2 fonctions:
dapply
(Data-Apply) applique des fonctions aux lignes ou aux colonnes (par défaut) des matrices et data.frames et (par défaut) renvoie un objet du même type et avec les mêmes attributs (sauf si le résultat de chaque calcul est atomique etdrop = TRUE
). Les performances sont comparables àlapply
celles des colonnes data.frame et environ 2 fois plus rapides que cellesapply
des lignes ou colonnes de matrice. Le parallélisme est disponible viamclapply
(uniquement pour MAC).Syntaxe:
Exemples:
BY
est un générique S3 pour le calcul d'application de combinaison et de fractionnement avec méthode vectorielle, matrice et data.frame. Il est nettement plus rapide quetapply
,by
etaggregate
( et aussi plus rapide queplyr
, sur de grandes donnéesdplyr
est plus rapide cependant).Syntaxe:
Exemples:
Des listes de variables de regroupement peuvent également être fournies
g
.Parler de performance: Un objectif principal de l' effondrement est de favoriser la programmation haute performance en R et d'aller au-delà de la combinaison d'application-séparation-combinaison. A cet effet , le paquet a un ensemble complet de fonctions C ++ à base rapide génériques:
fmean
,fmedian
,fmode
,fsum
,fprod
,fsd
,fvar
,fmin
,fmax
,ffirst
,flast
,fNobs
,fNdistinct
,fscale
,fbetween
,fwithin
,fHDbetween
,fHDwithin
,flag
,fdiff
etfgrowth
. Ils effectuent des calculs groupés en un seul passage à travers les données (c'est-à-dire sans fractionnement ni recombinaison).Syntaxe:
Exemples:
Dans les vignettes de package, je fournis des repères. La programmation avec les fonctions rapides est beaucoup plus rapide que la programmation avec dplyr ou data.table , en particulier sur des données plus petites, mais aussi sur des données volumineuses.
la source