Fonctions de regroupement (tapply, by, aggregate) et la famille d'application

chaque fois que je veux faire quelque chose "map"py in R, j'essaie habituellement d'utiliser une fonction dans la famille apply .

cependant, je n'ai jamais tout à fait compris les différences entre eux -- comment { sapply , lapply , etc.} appliquer la fonction à l'entrée/entrée groupée, à quoi ressemblera la sortie, ou même ce que l'entrée peut être -- donc je les passe souvent en revue jusqu'à ce que j'obtienne ce que je veux.

est-ce que quelqu'un peut expliquer comment utiliser qui un quand?

ma compréhension actuelle (probablement incorrecte/incomplète) est...

  1. sapply(vec, f) : l'entrée est un vecteur. la sortie est un vecteur / matrice, où l'élément i est f(vec[i]) , vous donnant une matrice si f a une sortie multi-éléments

  2. lapply(vec, f) : comme sapply , mais la sortie est une liste?

  3. apply(matrix, 1/2, f) : en entrée une matrice. la sortie est un vecteur, où l'élément i est f (ligne/colonne i de la matrice)
  4. tapply(vector, grouping, f) : sortie est une matrice / tableau, où un élément dans la matrice / tableau est la valeur de f à un groupement g du vecteur, et g est poussé aux noms de ligne / colonne
  5. by(dataframe, grouping, f) : que g soit un groupement. appliquer f à chaque colonne du groupe/dataframe. jolie impression de la le regroupement et la valeur de f à chaque colonne.
  6. aggregate(matrix, grouping, f) : semblable à by , mais au lieu d'Imprimer la sortie, aggregate colle tout dans une base 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 entièrement?

934
demandé sur zx8754 2010-08-17 22:31:12

9 réponses

r a beaucoup de fonctions *apply qui sont habilement décrites dans les fichiers d'aide (par exemple ?apply ). Il y en a assez, cependant, pour que les utilisateurs débutants puissent avoir de la difficulté à décider lequel est approprié à leur situation ou même à se les rappeler tous. 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) que la plupart des la fonctionnalité de la famille * apply est couverte par le très populaire paquet plyr , les fonctions de base restent utiles et valent la peine d'être connues.

cette réponse est destinée à agir comme une sorte de signpost pour les nouveaux utilisateurs pour les aider à les diriger vers la fonction correcte *appliquer pour leur problème particulier. Notez que c'est et non destiné à simplement régurgiter ou remplacer la documentation r! L'espoir est que cette réponse aide vous décidez quelle fonction * apply convient à votre situation et c'est à vous de la rechercher davantage. À une exception près, les écarts de rendement ne seront pas abordés.

  • appliquer - quand vous voulez appliquer une fonction aux lignes ou aux colonnes d'une matrice (et des analogues de dimensions plus élevées); généralement déconseillé pour les bases de données car il sera d'abord contraint à 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 ligne/colonne ou sommes pour une matrice 2D, assurez-vous de enquêtez sur le très optimisé, lightning-quick colMeans , rowMeans , colSums , rowSums .

  • lapply - quand vous voulez appliquer une fonction à chaque élément d'un la liste tour et d'obtenir une liste de retour.

    C'est la bête de somme de beaucoup d'autres * apply fonction. Peel retournez leur code et vous trouverez souvent lapply ci-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 - quand vous voulez appliquer une fonction à chaque élément d'un liste à son tour, mais vous voulez un vecteur plutôt que d'une liste.

    si vous tapez unlist(lapply(...)) , arrêtez 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 suite à 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:

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

    si notre fonction retourne une matrice bidimensionnelle, sapply fera essentiellement la même chose, 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" , auquel cas il utilisera les matrices individuelles pour construire un tableau multidimensionnel:

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

    chacun de ces comportements dépend évidemment de notre fonction retournant des vecteurs ou des matrices de la même longueur ou dimension.

  • vapply - quand vous voulez utiliser sapply mais peut-être besoin de sors un peu plus de vitesse de ton code.

    pour vapply , vous donnez essentiellement un exemple de ce genre de chose votre fonction reviendra, ce qui peut vous faire gagner du temps. valeurs à ajuster dans un vecteur atomique unique.

    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 - pour quand vous avez plusieurs structures de données (par ex. vecteurs, listes) et vous voulez appliquer une fonction aux 1er éléments de chacun, et puis la 2ème éléments de chacun, etc., le fait de contraindre le résultat à un vecteur / tableau comme dans sapply .

    c'est MultiVariable dans le sens où votre fonction doit accepter arguments multiples.

    #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 - "1519550920 Un" wrapper mapply avec SIMPLIFY = FALSE , de sorte qu'il est garanti pour revenir 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 - Pour lorsque vous souhaitez appliquer une fonction à chaque élément d'un liste imbriquée de la structure, de manière récursive.

    pour vous donner une idée à quel point rapply est rare, je l'ai oublié lors de la première affichage de cette réponse! Évidemment, je suis sûr que beaucoup de gens l'utilisent, mais YMMV. rapply est le 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 lorsque vous souhaitez appliquer une fonction de sous-ensembles d'un le vecteur et les sous-ensembles sont définis par un autre vecteur, généralement un facteur.

    le mouton noir de la *appliquer la famille, en quelque sorte. Le fichier d'aide de l'utilisation de l'expression "Ragged array" peut être un peu déroutant , mais il est en fait tout à fait simple.

    d'Un vecteur:

    x <- 1:20
    

    Un facteur (de la même longueur!) définition des groupes:

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

    additionner les valeurs de 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 des combinaisons uniques d'une liste de plusieurs facteurs. tapply est semblable dans l'esprit à la split-apply-combinent des fonctions qui être fréquent ( aggregate , by , ave , ddply , etc.) D'où son statut de mouton noir.

1217
répondu joran 2017-10-03 19:54:11

sur la note latérale, voici comment les diverses fonctions plyr correspondent aux fonctions de base *apply (de l'intro au document plyr de la page 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 

l'un des objectifs de plyr est de fournir des conventions d'appellation cohérentes pour chacune des fonctions, en encodant les types de données d'entrée et de sortie dans le nom de la fonction. Il fournit également la stabilité de la puissance, qui la sortie de dlply() peut facilement passer à ldply() pour produire une sortie utile, etc.

sur le plan conceptuel, l'apprentissage plyr n'est pas plus difficile que de comprendre les fonctions de base *apply .

plyr et reshape fonctions ont remplacé presque toutes ces fonctions dans mon utilisation quotidienne. Mais, aussi de L'Intro à Plyr document:

fonctions connexes tapply et sweep n'ont pas de fonction correspondante dans plyr , et restent utiles. merge est utile pour combiner les résumés avec les données originales.

172
répondu JoFrhwld 2010-08-17 19:20:09

de la diapositive 21 de http://www.slideshare.net/hadley/plyr-one-data-analytic-strategy :

apply, sapply, lapply, by, aggregate

(J'espère qu'il est clair que apply correspond à aaply et aggregate de @Hadley correspond à ddply etc. La diapositive 20 du même slideshare clarifiera si vous ne l'obtenez pas de cette image.)

(sur la gauche de l'entrée, sur le dessus est production)

120
répondu isomorphismes 2012-02-15 23:42:48

d'Abord commencer par Joran est excellente réponse -- douteux tout peut mieux que.

alors la mnémonique suivante peut aider à se rappeler les distinctions entre chacun. Tandis que certains sont évidents, d'autres peuvent être moins --- pour ceux-ci, vous trouverez la justification dans les discussions de Joran.

Mnémoniques

  • lapply est un liste , qui agit sur une liste ou un vecteur et renvoie une liste.
  • sapply est un simple lapply (fonction par défaut de retour d'un vecteur ou d'une matrice lorsque cela est possible)
  • vapply is a verified apply (permet de préciser le type d'objet retourné)
  • rapply est un récursive s'appliquent pour les listes imbriquées, c'est à dire les listes dans les listes
  • tapply est un marqué appliquer où les étiquettes d'identifier les sous-ensembles
  • apply est générique : applique une fonction à une matrice de lignes ou de colonnes (ou, plus généralement, aux dimensions d'un tableau)

construire le fond droit

si l'utilisation de la famille apply se sent encore un peu étranger à vous, alors 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 qui sont fournis par la apply famille de fonctions.

les utilisateurs de Lisp reconnaîtront immédiatement le paradigme. Si vous n'êtes pas familier avec le Lisp, une fois que vous obtenez votre tête autour de la FP, vous aurez gagné un puissant point de vue de L'utilisation dans R -- et apply fera beaucoup plus de sens.

89
répondu Assad Ebrahim 2017-05-23 10:31:37

depuis que je me suis rendu compte que (les réponses très excellentes) de ce manque de by et aggregate explications. Voici ma contribution.

par

la fonction by , comme indiqué dans la documentation peut être cependant, 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, la puissance de by se produit quand 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, dire "nous voulons calculer le summary de toutes les variables dans iris le long du facteur Species ": mais R ne peut pas le faire parce qu'il ne sait pas comment gérer.

avec la by Fonction R envoyer une méthode spécifique pour data frame classe et puis laisser la summary fonction fonctionne même si la longueur du premier argument (et le type aussi) sont différents.

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 en effet et le résultat est très surprenant. C'est un objet de la classe by que Species (disons, pour chacun d'eux) calcule la summary de chaque variable.

notez que si le premier argument est un data frame , la fonction dispatché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

agrégat

aggregate peut être considéré comme une autre façon d'utiliser 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 sont que le deuxième argument de aggregate doit être une liste de tapply peut (pas obligatoire) être une liste et que la sortie de aggregate est un bloc de données, tandis que celui de tapply est un array .

la puissance de aggregate est qu'il peut manipuler facilement des sous-ensembles des données avec subset argument et qu'il a des méthodes pour ts objets et formula ainsi.

ces éléments rendent aggregate plus facile à utiliser que 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 obtenir 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 moments 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 en un seul appel mais nous devons calculer la moyenne le long de Month pour chaque élément et puis les combiner (notez aussi 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 qu'avec by nous ne pouvons tout simplement pas atteindre que, en fait, l'appel de fonction suivant renvoie une erreur (mais très probablement il est 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 sont juste dans la classe (et alors comment il est montré / imprimé et pas seulement -- exemple, comment le sous-traiter) objet:

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

le code précédent atteint le même but et les mêmes résultats, à certains moments ce que l'outil à utiliser est juste 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 subsetting.

41
répondu SabDeM 2015-08-28 10:03:08

Il ya beaucoup de grandes réponses qui traitent des différences dans les cas d'utilisation pour chaque fonction. Aucune des réponses ne traite des différences de rendement. C'est une cause raisonnable diverses fonctions attend divers input et produit divers output, mais la plupart d'entre eux ont un objectif commun général d'évaluer par série/groupes. Ma réponse sera axée sur la performance. En raison de ci-dessus la création d'entrée à partir des vecteurs est inclus dans le timing, aussi la fonction apply n'est pas mesurer.

j'ai testé deux fonctions différentes sum et length à la fois. Le Volume testé est de 50 m à l'entrée et de 50 K à la 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 sont certainement la peine de regarder si vous visez une bonne performance.

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
30
répondu jangorecki 2015-12-08 22:50:30

il est peut-être intéressant de mentionner ave . ave est le cousin amical de tapply . Il renvoie des résultats sous une forme que vous pouvez brancher directement dans votre base 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 paquet de base qui fonctionne comme ave pour les bases de données entières (comme by est comme tapply pour les bases de données). Mais vous pouvez le caresser:

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
## ...
21
répondu 2014-11-06 00:00:25

malgré toutes les grandes réponses ici, il y a 2 autres fonctions de base qui méritent d'être mentionnées, la fonction utile outer et l'obscure eapply fonction

extérieur

outer est une fonction très utile cachée comme une plus banale. Si vous lisez l'aide pour outer sa description dit:

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], ...).

qui donne l'impression que c'est utile seulement pour les choses de type algèbre linéaire. Cependant, il peut être utilisé comme mapply 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 élément 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é cette quand j'ai un vecteur de valeurs et d'un vectorielle des conditions et souhaitent voir quelles valeurs répondent à quelles conditions.

eapply

eapply est comme 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 voulez trouver une liste des fonctions définies par l'utilisateur dans l'environnement mondial:

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 n'utilise pas très beaucoup, mais si vous construisez beaucoup de paquets ou Créez beaucoup d'environnements, cela peut s'avérer pratique.

21
répondu John Paul 2016-06-03 13:37:53

j'ai récemment découvert la fonction plutôt utile sweep et l'ajouter ici par souci d'exhaustivité:

sweep

l'idée de base est de balayer à travers une rangée de tableaux - ou la colonne-sage et retourner un tableau modifié. Un exemple le montrera clairement (source: datacamp ):

disons que vous avez une matrice et que vous voulez standardiser it colonne-wise:

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 en

apply(dataPoints, 2, scale)

8
répondu vonjd 2017-06-16 16:03:27