Question

Chaque fois que je veux faire quelque chose py « carte » dans R, j'essaie généralement d'utiliser une fonction dans la famille de apply.

Cependant, je l'ai compris jamais tout à fait 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 simplement passer en revue tous jusqu'à ce que je ce que je veux

Quelqu'un peut-il expliquer comment utiliser un quand qui?

Ma compréhension actuelle (probablement incorrect / incomplet) est ...

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

  2. lapply(vec, f): même que sapply, mais la sortie est une liste

  3. apply(matrix, 1/2, f): entrée est une matrice. sortie est un vecteur, où i élément est f (ligne / colonne i de la matrice)
  4. tapply(vector, grouping, f): la sortie est une matrice / matrice, où un élément de la matrice / matrice est la valeur de f à un groupement g du vecteur, et g est poussé vers les noms de ligne / col
  5. by(dataframe, grouping, f): laisser g être un groupement. appliquer f à chaque colonne du groupe / de trame de données. enluminer le regroupement et la valeur de f à chaque colonne.
  6. aggregate(matrix, grouping, f):. Semblable à by, mais au lieu de l'impression assez la sortie, des bâtons globaux tout en dataframe

question Côté: Je n'ai pas encore appris plyr ou Reshape - serait plyr ou reshape remplacer tous ces entièrement

Était-ce utile?

La solution

R a beaucoup * appliquer des fonctions qui sont habilement décrites dans les fichiers d'aide (par exemple ?apply). Il y a assez d'entre eux, cependant, que commençant peut avoir des difficultés uTIlIsaTeurs de décider qui est le plus approprié à leur situation ou même les rappeler tous. Ils peuvent avoir un sens général que « je devrais utiliser un * appliquer la fonction ici », mais il peut être difficile de les garder tout d'abord droit.

Malgré le fait (noté dans d'autres réponses) que la plupart des fonctionnalités de la * appliquer la famille est couvert par le forfait plyr extrêmement populaire, les fonctions de base demeurent utiles et qu'il faut savoir.

Cette réponse est destinée à agir comme une sorte de poteau indicateur pour les nouveaux résidents pour les aider directement à la bonne * appliquer la fonction pour leur problème particulier. Notez que ceci est pas destiné à simplement régurgiter ou remplacer la documentation R! L'espoir est que cette réponse vous aide à décider quels * appliquer des costumes de fonction de votre situation et il vous appartient de davantage de recherche. À une exception près, les différences de performance ne seront pas pris en compte.

  • appliquer - Lorsque vous souhaitez appliquer une fonction aux lignes ou colonnes d'une matrice (et des analogues de dimensions supérieures); généralement pas recommandé pour les trames de données car il forcera à une matrice d'abord.

    # 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 moyens de ligne / colonne ou des sommes pour une matrice 2D, assurez-vous étudier la très optimisée, colMeans rapide comme l'éclair, rowMeans, colSums, rowSums.

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

    Ceci est le cheval de bataille de la plupart des autres * appliquer des fonctions. Peler sauvegarder leur code et vous trouverez souvent lapply 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 voulez appliquer une fonction à chaque élément d'un liste à son tour, mais vous voulez un vecteur retour, plutôt que d'une liste.

    Si vous vous trouvez en tapant unlist(lapply(...)), arrêter et de considérer 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 
    

    En plus des utilisations avancées de sapply il va tenter de contraindre le aboutir à un tableau multidimensionnel, le cas échéant. Par exemple, si nos retours de fonction des vecteurs de la même longueur, sapply les utiliseront comme des colonnes d'une matrice:

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

    Si notre fonction renvoie une matrice 2 dimensions, sapply fera essentiellement la même chose, traiter chaque matrice retournée comme un seul vecteur à long:

    sapply(1:5,function(x) matrix(x,2,2))
    

    À moins que nous précisons 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 est contingent de cours sur notre fonction des vecteurs ou des matrices de retour de la même longueur ou dimension.

  • vapply - Lorsque vous souhaitez utiliser sapply mais il faut peut-être presser un peu plus de vitesse de votre code.

    Pour vapply, vous donnez essentiellement R un exemple de ce genre de chose votre fonction sera de retour, ce qui peut économiser du temps revenu qui exerce la contrainte les valeurs pour 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 - Pour quand vous avez plusieurs structures de données (par exemple vecteurs, listes) et que vous souhaitez appliquer une fonction au 1er éléments de chacun, puis les 2e éléments de chacun, etc., le résultat contraindre des à un vecteur / tableau comme dans sapply.

    est à plusieurs variables dans le sens que votre fonction doit accepter de multiples 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 -. Une enveloppe à mapply avec SIMPLIFY = FALSE, il est garanti de retourner 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 - Pourlorsque vous souhaitez appliquer une fonction à chaque élément d'une liste imbriqué structure récursive.

    Pour vous donner une idée de la façon dont rapply est rare, je l'avais oublié lors de la première publication de cette réponse! De toute évidence, je suis sûr que beaucoup de gens l'utilisent, mais YMMV. rapply est mieux illustrée avec une fonction définie par l'utilisateur d'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 vous voulez appliquer une fonction à sous-ensembles d'un vecteur et les sous-ensembles sont définis par un autre vecteur, généralement facteur.

    Le mouton noir de la famille * appliquer, en quelque sorte. L'utilisation du fichier d'aide l'expression « tableau en haillons » peut être un peu confusion , mais il est en fait tout à fait simple.

    Vecteur

    x <- 1:20
    

    Un facteur (de la même longueur!) Définissant des groupes:

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

    Ajoutez les valeurs dans chaque sous-groupe au sein x 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ées où les sous-groupes sont définis par les combinaisons uniques d'une liste de plusieurs facteurs. tapply est dans le même esprit à la scission-apply-combiner les fonctions qui sont commun à R (aggregate, by, ave, ddply, etc.) D'où son le statut de mouton noir.

Autres conseils

Sur la note de côté, voici comment les différentes fonctions de plyr correspondent aux fonctions de *apply de base (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 conventions de nommage cohérentes pour chacune des fonctions, codant pour les types de données d'entrée et de sortie dans le nom de fonction. Il assure aussi la cohérence de la production, dans cette sortie de dlply() est facilement praticable à ldply() pour produire une sortie utile, etc.

Conceptuellement, l'apprentissage plyr est plus difficile que la compréhension des fonctions de *apply de base.

Fonctions plyr et reshape ont remplacé presque toutes ces fonctions dans mon utilisation quotidienne. Mais, aussi de l'introduction du document plyr:

  

Fonctions connexes tapply et sweep ont aucune fonction correspondante dans plyr et restent utile. merge est utile pour combiner des résumés avec les données d'origine.

De coulisseau 21 de http://www.slideshare.net/ hadley / plyr-one-données-stratégie analytique :

appliquer, sapply, lapply, par, agrégat

(un peu de chance il est clair que correspond de apply à @ Hadley s correspond à aaply et aggregate à @ ddply Hadley etc. Toboggan 20 du même SlideShare précisera si vous ne l'obtenez pas de cette image.)

(sur la gauche est entré, sur la partie supérieure est sortie)

d'abord commencer par excellente réponse de Joran -. Quoi que ce soit douteux peut mieux que

Ensuite, les mnémoniques suivants peuvent aider à se rappeler les distinctions entre chaque. Alors 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 appliquer qui agit sur une liste ou d'un vecteur et retourne une liste.
  • sapply est simple, lapply (par défaut de fonction pour retourner un vecteur ou une matrice si possible)
  • vapply est verified appliquer (permet le type d'objet de retour à cela était spécifié)
  • rapply est récursive pour appliquer les listes imbriquées, à savoir dans les listes listes
  • tapply est taguée appliquer lorsque les balises identifient les sous-ensembles
  • apply est générique : applique une fonction pour les lignes d'une matrice ou colonnes (ou, plus généralement, aux dimensions d'un tableau)

Construire l'arrière-plan droit

Si vous utilisez la famille apply se sent encore un étranger peu pour vous, alors il se pourrait que vous manque un point de vue clé.

Ces deux articles peuvent aider. Ils fournissent les bases nécessaires pour motiver les techniques de programmation fonctionnelle qui sont fournis par la famille apply des fonctions.

Les utilisateurs de Lisp reconnaîtra immédiatement le paradigme. Si vous n'êtes pas familier avec Lisp, une fois que vous obtenez votre tête autour de FP, vous aurez gagné un point de vue puissant pour une utilisation en R -. Et apply ferez beaucoup plus de sens

Depuis que je pris conscience que (les très excellents) réponses de ce post manque d'explications by et aggregate. Voici ma contribution.

PAR

La fonction by, comme indiqué dans la documentation peut être bien, comme un « emballage » pour tapply. La puissance de by se pose quand on veut calculer une tâche qui 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 « essentiellement » ont les mêmes résultats et les seules différences sont dans la façon dont ils sont présentés et les différents attributs de class, respectivement by pour cb et array pour ct.

Comme je l'ai dit, le pouvoir de by se pose 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, dire « nous voulons calculer la summary de toute variable iris le long du facteur Species ». Mais R ne peuvent pas le faire parce qu'il ne sait pas comment gérer

Avec la fonction by R expédier une méthode spécifique pour la classe de data frame puis laissez la fonction summary 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     

il fonctionne bien et le résultat est très surprenant. Il est un objet de by de classe le long Species (disons, pour chacun d'eux) calcule la summary de chaque variable.

Notez que si le premier argument est un data frame, la fonction envoyée doit avoir une méthode pour cette classe d'objets. Par exemple est-nous utiliser ce code avec la fonction mean nous aurons ce code qui n'a pas de sens du tout:

 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

aggregate peut être considérée comme une autre d'une manière différente d'utilisation tapply si nous l'utilisons de manière d'un tel.

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 une liste alors que tapply peut (non obligatoire) une liste et que la sortie de aggregate est trame de données tandis que l'un des tapply est un array.

Le pouvoir de aggregate est qu'il peut gérer facilement des sous-ensembles de données avec l'argument de subset et qu'il a des méthodes pour les objets de ts et formula ainsi.

Ces éléments rendent aggregate plus facile à travailler 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 réaliser la même chose avec tapply mais la syntaxe est un peu plus difficile 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 Month pour chacun des éléments, puis les combiner (note aussi que nous devons appeler le na.rm = TRUE, parce que les méthodes de formula de la fonction aggregate a 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 pas réaliser que, en fait, l'appel de fonction suivante renvoie une erreur (mais très probablement elle est liée à 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 comment il est montré / imprimé et non seulement - par exemple, comment sous-ensemble il) objet:

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

Le code précédent atteindre le même objectif et les résultats, à quelques points ce que l'outil à utiliser est juste une question de goûts et besoins personnels; les deux précédents objets ont des besoins très différents en termes de sous-ensembles.

Il y a beaucoup de grandes réponses qui traitent des différences dans les cas d'utilisation pour chaque fonction. Aucun de la réponse discuter des différences de performance. C'est une cause raisonnable diverses fonctions diverses entrées et attend produit plusieurs sorties, mais la plupart d'entre eux ont un objectif commun général d'évaluer par série / groupes. Ma réponse va se concentrer sur la performance. En raison de la création au-dessus d'entrée à partir des vecteurs est inclus dans le temps, aussi la fonction apply ne se mesure pas.

J'ai testé deux fonctions différentes sum et length à la fois. Volume testé est 50M sur l'entrée et sur la sortie 50K. J'ai également inclus deux actuellement populaires qui ne sont pas largement utilisés au moment où la question a été posée, et data.table dplyr. Les deux valent vraiment 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

Il est peut-être convient de mentionner ave. ave est le cousin amical tapply. Il renvoie les résultats sous une forme que vous pouvez brancher le dos droit dans votre bloc 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 (comme by est comme tapply pour des trames de données). Mais vous pouvez truquer il:

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
## ...

En dépit de toutes les grandes réponses ici, il y a 2 fonctions plus base qui méritent d'être mentionnés, la fonction outer utile et la fonction eapply obscure

externe

outer est une fonction très utile cachée comme un plus banal. 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], ...).

ce qui fait croire que ce n'est utile pour des choses de type algèbre linéaire. Cependant, il peut être utilisé un peu comme mapply pour appliquer une fonction à deux vecteurs d'entrées. La différence est que mapply va appliquer la fonction aux deux premiers éléments, puis la seconde deux etc, alors que outer applique la fonction à chaque combinaison d'un élément du premier vecteur et un de la seconde. 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

Je l'ai personnellement utilisé quand j'ai un vecteur de valeurs et un vecteur de conditions et souhait de voir quelles valeurs se rencontrent quelles conditions.

eapply

eapply est comme lapply, sauf que, plutôt que d'appliquer une fonction à chaque élément dans une liste, il applique une fonction à chaque élément dans un environnement. Par exemple, si vous voulez trouver 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 un grand nombre de paquets ou de créer un grand nombre d'environnements, il peut être utile.

J'ai récemment découvert la fonction sweep assez utile et l'ajouter ici par souci d'exhaustivité:

balayage

L'idée de base est de balayage par un row- tableau ou sage colonne et retourner un tableau modifié. Un exemple que ce soit clair (source: datacamp ) :

Disons que vous avez une matrice et que vous voulez Normaliser il en 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 réalisé plus facilement
apply(dataPoints, 2, scale)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top