Le package plyr (2)

Auteur.e.s
Fabrice Dessaint, Inra
Résumé

Le package plyr propose de nombreuses fonctions permettant de manipuler, à la manière des fonctions de la famille apply, des objets de type array, data.frame et list. Il permet de fractionner un ensemble de données en unités plus simples qui peuvent ensuite être traitées puis une fois les traitements effectués, de reconstruire un tout avec les résultats obtenus.

Le package est riche et toutes les fonctions ne seront pas traitées dans ce document: seules les fonctions de type **ply() seront vues. Les autres fonctions sont présentées dans un autre document (voir la fiche Le package plyr (1)).

Le package plyr est proposé par Hadley Wickham. Il offre de nombreuses fonctions permettant de manipuler et de traiter des objets R et plus particulièrement des tableaux (data.frame), des listes (list) et des matrices (array).

Les fonctions

Ces fonctions sont construites selon un même modèle de nommage: elles sont formée de 5 lettres et se terminent par ply. Un premier groupe de fonctions est construit selon la convention suivante: la première lettre indique le type de l’objet de départ et la seconde celui d’arrivée. On a :

  • a pour une matrice (array)
  • l pour une liste (list)
  • d pour un tableau (data.frame)

Ainsi la fonction adply() utilise en entrée une matrice (array) et fournie en sortie un tableau (data.frame).

Un second groupe de fonctions utilisent comme première lettre m ou r. On a par exemple mdply() ou raply(). Les fonctions r*ply() sont des analogues à la fonction replicate(). Les fonctions m*ply() permettent d’évaluer des fonctions pour des valeurs d’arguments fournies par la structure d’entrée.

Enfin, un dernier groupe de fonctions, utilisent le symbole _ en seconde position comme a_ply(). Ces fonctions opérent sur une structure R en entrée mais ne fournissent pas de structure en sortie.

La syntaxe

Le package plyr utilise plusieurs systèmes de notations pour indiquer comment fractionner un ensemble de données

  • la forme vecteur,
  • la forme formule et
  • une nouvelle notation.

Par exemple, pour fractionner les données selon les modalités de 2 variables color et cut, on utilisera respectivement:

  • c("color","cut") ou
  • ~ color + cut ou
  • .(color,cut).

Exemples d’utilisation

Avant d’utiliser ces fonctions, il faut charger le package plyr.

library("plyr")

Note: Ce package n’est pas installé par défaut et il vous faudra peut être le faire avant de pouvoir l’utiliser.

install.packages("plyr")

Les exemples qui suivent montrent comment on peut utiliser les différentes fonctions pour réaliser plus facilement (et souvent plus rapidement) des traitements. On va utiliser un tableau de données (data.frame) disponible dans le package ggplot2. Il a l’avantage de posséder un grand nombre d’observations.

data(diamonds, package = "ggplot2")

Le tableau diamonds contient 53940 lignes et 10 colonnes.

Les fonctions du premier groupe

Neuf fonctions appartiennent à ce groupe (voir le tableau de synthèse). Elles opèrent sur des matrices, des listes ou des tableaux en entrée et fournissent les résultats sous forme de matrices, de listes ou de tableaux en sortie.

La syntaxe

La syntaxe générale pour ces fonctions, illustrée pour la fonction ddply(), est la suivante:

formatR::usage(ddply)
## ddply(.data, .variables, .fun = NULL, ..., .progress = "none",
##     .inform = FALSE, .drop = TRUE, .parallel = FALSE, .paropts = NULL)

avec

  • .data: la structure d’entrée (ici un tableau)
  • .variables: la liste des variables permettant de découper le tableau.
  • .fun=NULL: la fonction à appliquer; cette fonction pouvant prendre des arguments
  • .progress="none" permet l’affichage d’une barre de progression
  • .inform=FALSE: fournit des messages d’erreur plus explicites. Utile lors d’une phase de mise au point
  • .parallel=FALSE: utilisation de la fonction foreach()
  • .paropts=NULL: arguments pour la fonction foreach()

L’argument .drop=TRUE est spécifique à la fonction ddply() et indique si toutes les combinaisons des modalités des variables servant à découper le tableau d’entrée doivent apparaitre (.drop=FALSE) dans le tableau de sortie.

formatR::usage(llply)
## llply(.data, .fun = NULL, ..., .progress = "none", .inform = FALSE,
##     .parallel = FALSE, .paropts = NULL)
formatR::usage(aaply)
## aaply(.data, .margins, .fun = NULL, ..., .expand = TRUE,
##     .progress = "none", .inform = FALSE, .drop = TRUE,
##     .parallel = FALSE, .paropts = NULL)
  • .margins: les marges sur lesquelles se fait le découpage.

Exemples

Dans l’exemple suivant, on utilise la fonction ddply() pour calculer le nombre de lignes/observations pour les différents combinaisons de niveaux de 2 variables. On fournit en entrée un tableau et on obtient en sortie un autre tableau.

sortie <- ddply(.data = diamonds, .variables = c("color", "cut"), .fun = nrow)

La fonction commence par découper le tableau d’entrée diamonds en autant de sous tableaux qu’il y a de combinaisons des modalités des 2 variables color et cut. Puis elle calcule le nombre de lignes de chacun de ces sous tableaux et regroupe les informations sous la forme d’un tableau.

class(sortie)
## [1] "data.frame"
head(sortie)
##   color       cut   V1
## 1     D      Fair  163
## 2     D      Good  662
## 3     D Very Good 1513
## 4     D   Premium 1603
## 5     D     Ideal 2834
## 6     E      Fair  224

L’utilisation de la fonction daply() aurait fournie le même résultat mais sous la forme d’une matrice.

sortie <- daply(.data = diamonds, .variables = ~color + cut, .fun = nrow)
class(sortie)
## [1] "matrix"
sortie
##      cut
## color Fair Good Very Good Premium Ideal
##     D  163  662      1513    1603  2834
##     E  224  933      2400    2337  3903
##     F  312  909      2164    2331  3826
##     G  314  871      2299    2924  4884
##     H  303  702      1824    2360  3115
##     I  175  522      1204    1428  2093
##     J  119  307       678     808   896

Note: Pour cet exemple, on a utilisé une formule pour l’argument .variables.

Enfin, l’utilisation de la fonction dlply() fournit le même résultat sous la forme d’une liste.

sortie <- dlply(.data = diamonds, .variables = .(color, cut), .fun = nrow)
class(sortie)
## [1] "list"
head(sortie)
## $D.Fair
## [1] 163
## 
## $D.Good
## [1] 662
## 
## $`D.Very Good`
## [1] 1513
## 
## $D.Premium
## [1] 1603
## 
## $D.Ideal
## [1] 2834
## 
## $E.Fair
## [1] 224

Note: Pour cet exemple, on a utilisé la nouvelle notation proposée par plyr pour l’argument .variables.

Dans les 3 exemples précédents, la fonction utilisée s’appliquait à une propriété du tableau (nombre de lignes). Lorsque l’on veut appliquer une fonction aux colonnes d’un tableau, c’est un peu plus compliqué.

Imaginons que l’on veuille calculer la moyenne de chacune des variables quantitatives des différents sous tableaux générés par les modalité de la variable `clarity. On pourrait utiliser le code suivant:

moyenne <- numcolwise(mean)
sortie <- daply(.data = diamonds, .variables = .(clarity), .fun = moyenne)
sortie
##        
## clarity carat     depth    table    price    x        y       
##    I1   1.283846  62.73428 58.30378 3924.169 6.761093 6.709379
##    SI2  1.077648  61.77217 57.92718 5063.029 6.40137  6.397826
##    SI1  0.8504822 61.85304 57.66254 3996.001 5.888383 5.888256
##    VS2  0.7639346 61.72442 57.4174  3924.989 5.657709 5.658859
##    VS1  0.7271582 61.66746 57.31515 3839.455 5.572178 5.581828
##    VVS2 0.5962021 61.66378 57.02499 3283.737 5.218454 5.232118
##    VVS1 0.5033215 61.62465 56.88446 2523.115 4.960364 4.975075
##    IF   0.5051229 61.51061 56.50721 2864.839 4.968402 4.989827
##        
## clarity z       
##    I1   4.207908
##    SI2  3.948478
##    SI1  3.639845
##    VS2  3.491478
##    VS1  3.441007
##    VVS2 3.221465
##    VVS1 3.061294
##    IF   3.061659

La fonction numcolwise() est une fonction proposée par le package plyr qui permet l’utilisation d’une fonction prévue pour traiter une variable (ici mean()), à l’ensemble des variables numériques d’un tableau.

L’utilisation de la fonction colMeans() conduit à une erreur.

sortie <- daply(.data = diamonds, .variables = .(clarity), .fun = colMeans)
Error in .fun(piece, ...) : 'x' doit être numérique

Un autre exemple plus compliqué. On souhaite ajouter 2 variables: la première volume, est le produit de 3 variables du tableau et la seconde, volume.max, est le rapport de cette variable sur la maximum qu’elle peut prendre pour la combinaison de niveaux considérée.

sortie <- ddply(.data = diamonds[1:100, -c(1, 5:7)], .variables = .(cut, 
    color), .fun = mutate, volume = x * y * z, volume.max = volume/max(volume))
head(sortie, n = 10)
##     cut color clarity    x    y    z    volume volume.max
## 1  Fair     E     VS2 3.87 3.78 2.49  36.42521  0.2534522
## 2  Fair     E     SI2 6.45 6.33 3.52 143.71632  1.0000000
## 3  Fair     F     SI2 6.27 5.95 4.07 151.83746  1.0000000
## 4  Good     D     VS2 3.99 4.02 2.61  41.86388  0.9579095
## 5  Good     D     VS1 4.19 4.24 2.46  43.70338  1.0000000
## 6  Good     E     VS1 4.05 4.07 2.31  38.07688  0.3263900
## 7  Good     E     VS1 3.83 3.85 2.46  36.27393  0.3109353
## 8  Good     E    VVS1 4.22 4.25 2.45  43.94075  0.3766543
## 9  Good     E     VS2 5.85 5.90 3.38 116.66070  1.0000000
## 10 Good     F     VS1 4.06 4.08 2.37  39.25858  0.3510727

La fonction mutate() disponible dans le package plyr est une fonction du même type que transform().

Les fonctions du deuxième groupe

Six fonctions appartiennent à ce groupe (voir le tableau de synthèse). Elles opèrent sur des matrices ou des tableaux en entrée et fournissent les résultats sous forme de matrices, de listes ou de tableaux en sortie.

Les fonctions r*ply() sont des analogues à la fonction replicate(). Les fonctions m*ply() permettent d’évaluer des fonctions pour des valeurs d’arguments fournies par la structure d’entrée.

La syntaxe

La syntaxe générale pour les m*ply(), illustrée pour la fonction mdply(), est la même que pour les fonctions précédentes.

formatR::usage(mdply)
## mdply(.data, .fun = NULL, ..., .expand = TRUE, .progress = "none",
##     .inform = FALSE, .parallel = FALSE, .paropts = NULL)

avec

  • .data: la structure d’entrée (ici un tableau)
  • .fun=NULL: la fonction à appliquer; cette fonction pouvant prendre des arguments
  • .progress="none" permet l’affichage d’une barre de progression
  • .inform=FALSE: fournit des messages d’erreur plus explicites. Utile lors d’une phase de mise au point
  • .parallel=FALSE: utilisation de la fonction foreach()
  • .paropts=NULL: arguments pour la fonction foreach()

Pour les fonctions de type r*ply() la syntaxe, illustrée pour la fonction rdply(), est la suivante:

formatR::usage(rdply)
## rdply(.n, .expr, .progress = "none", .id = NA)

avec

  • .n: le nombre de fois où l’expression doit être évaluer
  • .expr: l’expression à évaluer
  • .progress="none" permet l’affichage d’une barre de progression

L’argument .drop=TRUE est spécifique à la fonction raply(), et indique si toutes les combinaisons des modalités des variables servant à découper le tableau d’entrée doivent apparaitre (.drop=FALSE) dans le tableau de sortie.

Exemples d’utilisation des fonctions r*ply()

Création d’une table de nombres aléatoires ayant 10 lignes et 5 colonnes.

set.seed(1347)
raply(.n = 10, .expr = runif(5))
##               1          2          3         4         5
##  [1,] 0.6951353 0.08545947 0.71560196 0.1332820 0.1582842
##  [2,] 0.6332076 0.62175332 0.38420903 0.6924909 0.2462433
##  [3,] 0.3068186 0.37746400 0.06586577 0.3153150 0.6843955
##  [4,] 0.7046497 0.75727852 0.43420726 0.8817528 0.4325755
##  [5,] 0.5674876 0.27487493 0.93073893 0.6054027 0.5731911
##  [6,] 0.9310923 0.87719340 0.16183269 0.8780731 0.7289471
##  [7,] 0.4221001 0.01932751 0.69429572 0.6179032 0.4295595
##  [8,] 0.6066641 0.37793131 0.12667435 0.3699783 0.7265005
##  [9,] 0.3692220 0.42154858 0.18080658 0.6326618 0.8654309
## [10,] 0.3188769 0.29479482 0.70428533 0.6244405 0.4781271

Un exemple de simulation. La fonction simul() calcule la moyenne d’un échantillon de 10 valeurs tirées dans une distribution normale de moyenne=100 et d’écart type=15. On utilise ensuite cette fonction pour réaliser une simulation de taille 1000.

simul <- function() mean(rnorm(n = 10, mean = 100, sd = 15))
sortie <- rdply(.n = 1000, .expr = simul())
colnames(sortie) <- c("Rep", "Moyenne")
head(sortie)
##   Rep   Moyenne
## 1   1  95.88420
## 2   2  92.16942
## 3   3  95.50919
## 4   4 104.64103
## 5   5 104.21931
## 6   6  97.19170

Exemples d’utilisation des fonctions m*ply()

Un premier exemple simple de l’utilisation de la fonction mdply(). Il s’agit de calculer la valeur de \(t\) pour différents degrés de liberté. On utilise la fonction qt() qui possède 2 arguments obligatoires: p, la probabilité et df, le nombre de degrés de liberté.

entree <- data.frame(p = rep(0.975, 11), df = c(seq(10, 100, 10), 1000))
mdply(.data = entree, .fun = qt)
##        p   df       V1
## 1  0.975   10 2.228139
## 2  0.975   20 2.085963
## 3  0.975   30 2.042272
## 4  0.975   40 2.021075
## 5  0.975   50 2.008559
## 6  0.975   60 2.000298
## 7  0.975   70 1.994437
## 8  0.975   80 1.990063
## 9  0.975   90 1.986675
## 10 0.975  100 1.983972
## 11 0.975 1000 1.962339

Un deuxième exemple illustre le paradoxe des anniversaires. Dû à Richard von Mises, il est à l’origine une estimation probabiliste du nombre de personnes que l’on doit réunir pour avoir une chance sur deux que deux personnes de ce groupe aient leur anniversaire le même jour de l’année. R propose la fonction qbirthday() pour calculer cette probabilité:

qbirthday(prob = 0.5, classes = 365, coincident = 2)
## [1] 23

Il se trouve que ce nombre est 23, ce qui est finalement assez peu.

On va chercher qu’elle devrait être la taille du groupe pour différentes valeurs de probabilités et différentes valeurs de coincidence.

entree <- expand.grid(prob = c(5:9/10, 91:99/100), coincident = 2:6)
maply(.data = entree, .fun = qbirthday)
##       coincident
## prob    2   3   4   5   6
##   0.5  23  88 187 313 459
##   0.6  27  97 202 334 486
##   0.7  30 107 218 356 514
##   0.8  35 118 237 382 546
##   0.9  41 135 263 416 589
##   0.91 42 137 266 421 594
##   0.92 43 139 270 426 600
##   0.93 44 142 274 431 607
##   0.94 45 145 278 437 615
##   0.95 47 148 283 444 623
##   0.96 48 152 290 452 633
##   0.97 50 157 297 462 645
##   0.98 53 164 307 474 660
##   0.99 57 174 322 494 685

À partir d’un groupe de 57 personnes, la probabilité que 2 personnes ait leur anniversaire le même jour est supérieure à 99%. Pour cette même probabilité, il faut un groupe d’au moins 685 personnes pour en avoir 6 ayant leur anniversaire le même jour.

Les fonctions du troisième groupe

Cing fonctions appartiennent à ce groupe (voir le tableau de synthèse). Elles opèrent sur des matrices, des listes ou des tableaux en entrée mais ne produisent pas de sorties. Ce sont les fonction a_ply(), d_ply(), l_ply(), m_ply(), r_ply(). Elles sont utilisées, par exemples, lors de la constructions de graphiques.

Les fonctions

Vingt fonctions permettent d’appliquer un traitement à des sous ensembles appartenant à un objet puis de reconstruire un objet unique contenant l’ensemble des résultats. Les objets de départ et d’arrivée peuvent être d’un même type ou de types différents.

Le tableau suivant indique les fonction disponibles avec en lignes, le type de structures d’entrée et en colonnes, le type de structures en sortie.

  Matrice Tableau Liste Rien
Matrice aaply() adply() alply() a_ply()
Tableau daply() ddply() dlply() d_ply()
Liste laply() ldply() llply() l_ply()
  maply() mdply() mlply() m_ply()
  raply() rdply() rlply() r_ply()

Pour aller plus loin

H. Wickham (2011). The Split-Apply-Combine Strategy for Data Analysis. Journal of Statistical Software, 40(1).

Versions des outils utilisés
R version 3.4.2 (2017-09-28), plyr version 1.8.4
Thèmes de la fiche
Thèmes