4 Boucles et calculs vectoriels

Il existe deux sortes de boucles dans R. Celles pour lesquelles les itérations continuent tant qu’une condition n’est pas invalidée (while()), et celles pour lesquelles le nombre d’itérations est défini au moment de lancer la boucle (for()).

Avant de présenter chacune de ces fonctions, il est nécessaire de préciser que les boucles ne sont pas le point fort de R. Dès que l’on souhaite appliquer une fonction à chaque élément d’un vecteur, et/ou que le résultat de chaque itération ne dépend pas de l’itération précédente, il est préférable de vectoriser les calculs (voir Section 4.2).

4.1 Les boucles

4.1.1 Les boucles avec while()

Quand on souhaite répéter un calcul tant qu’une condition est satisfaite, on utilise la fonction while(), avec la syntaxte suivante :

while(condition) instruction

avec condition un logique, comme vu dans la Section @ref(manip_operateurs_logiques), et instruction du code, qui peut être entouré d’accolades si on souhaite évaluer plusieurs instructions.

x <- 100
while(x/3 > 1){
  x <- x/3
}
x/3 > 1
## [1] FALSE
x
## [1] 1.234568

4.1.2 Les boucles avec for()

Quand on connaît le nombre d’itérations à l’avance, on peut utiliser la fonction for(). La syntaxe est la suivante :

for(variable in vector) instruction

avec variable le nom d’une variable locale à la fonction for(), vector un vecteur à n éléments définissant les valeurs que prendra variable pour chacun des n tours, et instruction le code à exécuter à chaque itération.

for(nom in  c("Sonia", "Anne-Hélène", "Julien-Yacine")) print(nom)
## [1] "Sonia"
## [1] "Anne-Hélène"
## [1] "Julien-Yacine"

On peut utiliser la fonction for() pour remplir les éléments d’une liste, ou d’un vecteur. à chaque itération, R doit trouver le vecteur de destination en mémoire, créer un nouveau vecteur qui permettra de contenir plus de données, copier données depuis l’ancien vecteur pour les insérer dans le nouveau, et enfin supprimer l’ancien vecteur (Ross 2014). C’est une opération coûteuse en temps. Un moyen de rendre cette allocation plus efficace est de créer a priori le vecteur ou la liste en le remplissant avec des données manquantes. Ainsi, R n’aura pas besoin de ré-allouer la mémoire à chaque itération.

# Mauvaise manière
resultat <- NULL
for(i in seq_len(3)) {
  resultat[i] <- i
}
resultat
## [1] 1 2 3
# Manière plus économique
resultat <- rep(NA, 3)
for(i in seq_len(3)) {
  resultat[i] <- i
}
resultat
## [1] 1 2 3

4.1.3 Les conditions

On peut soumettre l’exécution de codes en R à conditions que certaines conditions soient honorées.

4.1.3.1 Les instructions ifelse

Les instructions if et else fournissent un moyen d’exécuter du code si une condition est respectée ou non. La syntaxe prend deux formes :

# Première forme (pas de code si condition == FALSE)
if (condition) instruction

# Seconde forme
if (condition) instruction si vrai else instruction si faux

avec condition un logique et instruction du code à évaluer si la condition est satisfaite. à nouveau, on peut avoir recours aux accolades pour créer des regroupements.

# Simple condition
x <- 2
if(x == 2) print("Hello")
## [1] "Hello"
x <- 3
if(x == 2) print("Hello")

# Avec des instructions dans le cas contraire
if(x == 2) print("Hello") else print("x est différent de 2")
## [1] "x est différent de 2"
if(x == 2){
  print("Hello")
} else {# x != 2
  x <- x-1
  print(paste0("La nouvelle valeur de x : ", x))
}
## [1] "La nouvelle valeur de x : 2"
Attention, lorsque l’on fait des regroupements et qu’on utilise la structure if et else, il est nécessaire d’écrire le mot else sur la même ligne que la parenthèse fermante du groupe d’instructions à réaliser dans le de la condition du if vérifiée.

4.1.3.2 La fonction switch()

Avec la fonction switch(), on peut indiquer à R d’exécuter un code en fonction du résultat obtenu lors d’un test. La syntaxe est la suivante :

switch(valeur_test,
       cas_1 = {
         instruction_cas_1
       },
       cas_2 = {
         instruction_cas_2
       },
       ...
  )

avec valeur_test un nombre ou une chaîne de caractères. Si valeur_test vaut cas_1, alors uniquement instruction_cas_1 sera évaluée, si valeur_test vaut cas_2, alors ce sera instruction_cas_2 qui le sera, et ainsi de suite. On peut rajouter une valeur par défaut en utilisant la syntaxte suivante :

switch(valeur_test,
       cas_1 = {
         instruction_cas_1
       },
       cas_2 = {
         instruction_cas_2
       },
       {
        instruction_defaut 
       }
  )

Voici un exemple d’utilisation, issu de la page d’aide de la fonction.

centre <- function(x, type) {
  switch(type,
         mean = mean(x),
         median = median(x))
}
x <- rcauchy(10)
centre(x, "mean")
## [1] -5.926731
centre(x, "median")
## [1] 0.02370687

4.1.4 L’instruction repeatbreak

L’instruction repeat, … break permet de répéter une expression. Il est nécessaire d’ajouter un test d’arrêt, à l’aide de l’instruction break.

i <- 1
repeat {
  i <- i + 1
  if(i == 3) break
}
i
## [1] 3

4.1.5 L’instruction nextbreak

L’instruction nextbreak autorise de passer immédiatement à l’itération suivante d’une boucle for, while ou repeat

resul <- rep(NA, 10)
for(i in 1:10) {
  if(i == 5) next
  resul[i] <- i
}
# Le 5e élément de resul est resté non-disponible
resul
##  [1]  1  2  3  4 NA  6  7  8  9 10

4.1.6 Barre de progression

Lorsque l’exécution d’une boucle prend du temps, il peut être intéressant d’avoir une idée de l’état d’avancement des itérations. Pour cela, il est bien sûr possible d’afficher une valeur dans la console à chaque tour, chaque 10 tours, etc.

La fonction txtProgressBar() du package {utils} permet un affichage d’une barre de progression dans la console. Il suffit de lui fournir une valeur minimale et maximale, et de la mettre à jour à chaque itération. l’argument style autorise de surcroît à choisir un “style” pour la barre. Le style numéro 3 affiche un pourcentage de progression, et est utile lorsque d’autres résultats sont affichés dans la console lors de l’exécution de la boucle, dans la mesure où la barre est de nouveau affichée au complet dans la console si nécessaire.

Dans l’exemple qui suit, à chacun des dix tours, une pause \(0.1\) seconde est effectuée, puis la barre de progression est mise à jour.

nb_tours <- 10
p_b <- txtProgressBar(min = 1, max = nb_tours, style = 3)
for(i in 1:nb_tours){
  Sys.sleep(0.1)
  setTxtProgressBar(p_b, i)
}

Si l’exécution est vraiment longue, et qu’on est impatient de connaître les résultats, il existe de plus une fonction amusante dans le package beepr, qui porte le nom de beep(). Plusieurs sons peuvent être utilisés (voir la page d’aide de la fonction).

library(beepr)
beep("mario")

4.2 La vectorisation

Comme indiqué plus haut, les boucles sont des opérations lentes en R. Il est cependant possible, dans de nombreux cas, d’éviter de les employer, en ayant recours à la vectorisation : au lieu d’appliquer une fonction à un scalaire, on l’applique à un vecteur. En fait, nous avons déjà eu recours à maintes reprises aux calculs vectoriels. En effet, lorsque nous avons procédé à des additions, des multiplications, etc. sur des vecteurs, nous avons effectué des calculs vectoriels.

Empruntons un exemple à Burns (2011) : dans des langages comme le C, pour effectuer la somme des logarithmes naturels des \(n\) premiers entiers, voici une manière de faire :

# Somme des logarithmes des 10 premiers entiers
somme_log <- 0
for(i in seq_len(10)){
  somme_log <- somme_log + log(i)
}
somme_log
## [1] 15.10441

Il est possible d’obtenir le même résultat, à la fois d’une manière plus élégante, mais surtout plus efficace en vectorisant le calcul :

sum(log(seq_len(10)))
## [1] 15.10441

Ou bien, en utilisant l’opérateur Pipe, afin d’éviter la lecture pénible de cette composée de fonctions :

seq_len(10) %>% log() %>% sum()
## [1] 15.10441

Derrière ce code, la fonction log() applique la fonction logarithme sur toutes les valeurs du vecteur donné en argument. La fonction sum(), quant à elle, se charge d’additionner tous les éléments du vecteur qui lui est donné en argument. Ces deux fonctions utilisent la vectorisation, mais d’une manière différente : la fonction log() applique une opération à chaque élément d’un vecteur, tandis que la fonction sum() produit un résultat basé sur la totalité du vecteur. L’avantage d’utiliser des fonctions vectorielles plutôt que d’écrire une boucle pour effectuer le calcul, est que ces premières font appel à des fonctions rédigées en C ou FORTRAN, qui utilisent aussi des boucles, mais comme ce sont des langages compilés et non pas interprétés, les itérations sont réalisées dans un temps réduit.

Il existe des fonctions, rédigées en C qui effectuent des boucles for. On leur donne souvent le nom de "fonctions de la famille apply’‘. Il ne s’agit pas de la vectorisation, mais ces fonctions sont souvent mentionnées dès que l’on parle de ce sujet. Ce sont des fonctionnelles qui prennent une fonction en input et retournent un vecteur en output (Wickham 2014). Ces fonctions sont très utilisées, mais elles souffrent d’un manque d’uniformité. En effet, elles ont été rédigées par des personnes différentes, ayant chacune leur convention. Le package {plyr} remédie à ce problème, et ajoute par la même occasion des fonctions supplémentaires, pour couvrir plus de cas que les "fonctions de la famille apply’’. Plus récemment, le package {purrr} a également introduit des fonctions de ce type qui seront présentées ici.

Nous allons donc nous attarder dans un premier temps les fonctions du package {purr}. Puis, afin d’en conserver une trace, sur les fonctions du package {purrr} ainsi que celles du package {base}.

4.3 Avec {purrr}

Le package {purrr} propose de nombreuses fonctions permettant de manipuler les listes et les tableaux de données. Dans cette partie, nous allons nous concentrer sur les fonctions de ce package qui permettent d’appliquer des opérations sur chaque élément d’une liste ou sur chaque colonne d’un tableau de données. Le nom de ces fonctions commence par le préfixe map. Si la structure du résultat voulu est différente d’une liste, le nom de la fonction à utiliser se poursuit par un suffixe désignant la structure désirée.

Fonction Résultat
map() Liste
map_chr() Vecteur de caractères
map_dbl() Vecteur de doubles
map_int() Vecteur d’entiers
map_lgl() Vecteur de booléens
map_dfr() data frame créé en concaténant par les lignes (rows) à chaque itération
map_dfc() data frame créé en concaténant par les colonnes (columns) à chaque itération

Ces fonctions prennent les arguments suivants :

  • .x : une liste ou un vecteur ;
  • .f : une fonction, une formule (qui sera transformée automatiquement en fonction) ou un vecteur / une liste ;
  • ... : des arguments additionnels passés à la fonction appliquée à chaque élément.

4.3.1 Listes ou vecteurs en input

Lorsque l’on donne une liste ou un vecteur à l’argument .x des fonctions de type map, la fonction (ou la formule) donnée en argument .f est appliquée à chaque élément de la liste ou du vecteur d’entrée. La structure du résultat dépend de la fonction map utilisée, qui est précisée à l’aide du suffixe du nom de la fonction.

Prenons un exemple. Admettons que l’on dispose d’une liste de longueur 100, dont chaque élément contient 50 valeurs numériques. On peut faire face à ce type de données lorsqu’on souhaite réaliser des simulations par ré-échantillonnage, par exemple. Dans cet exemple, nous aurions au préalable réalisé 100 rééchantillonnage différents. Admettons que l’on souhaite connaître la moyenne de chaque ré-échantillon.

Dans un premier temps, générons une liste contenant 50 éléments dans un vecteur contenant 100 éléments. Les 50 valeurs seront tirées ici selon une loi Normale dont les arguments sont aléatoirement tirées.

set.seed(263)
simulations <- vector(mode = "list", length = 100)
for(i in 1:length(simulations)){
  simulations[[i]] <- rnorm(n = 50,
                            mean = sample(-10:10, 1),
                            sd = runif(1, min=1, max = 5))
}
length(simulations)
## [1] 100
head(simulations[[1]])
## [1] -10.233237  -8.904499 -10.737834  -7.454745  -9.909407  -8.800618

Nous souhaitons donc calculer la moyenne de chacun des 100 éléments de cette liste. Avec une boucle for, on pourrait faire comme suit :

mean_samples <- vector(mode="numeric", length = 100)
for(i in 1:length(simulations)) 
  mean_samples[[i]] <- mean(simulations[[i]])

head(mean_samples)
## [1] -10.220707   9.918496  -8.248388  -6.879007  -7.120338  -7.047220

De manière équivalente, et avec un temps d’exécution comparable, on peut utiliser une des fonctions du package {purrr}. Ici, nous voulons appliquer à une liste en entrée, une fonction (mean()) sur chacun des éléments, puis obtenir le résultat final sous la forme d’un vecteur de numériques. Nous pouvons de fait utiliser la fonction map_dbl() comme suit :

mean_samples_2 <- map_dbl(simulations, mean)
head(mean_samples_2)
## [1] -10.220707   9.918496  -8.248388  -6.879007  -7.120338  -7.047220

Cette méthode a l’avantage de produire un code plus élégant et plus rapide à lire.

On peut également faire appel à l’opérateur Pipe pour rendre la lecture encore plus aisée :

mean_samples_2 <- 
  simulations %>% 
  map_dbl(mean)

Admettons maintenant que nous voulons calculer le premier quartile empirique de chaque élément de simulations. Pour ce faire, il faut appliquer la fonction quantile(), en précisant à l’aide de l’argument probs que nous voulons le quantile d’ordre 0.25. Si on désire utiliser la fonction map_dbl(), il faut ajouter un argument à cette dernière pour que celui-ci soit transmis à la fonction quantile() (il s’agit de fait d’appeler l’argument ... de la fonction map_dbl()).

simulations %>% 
  map_dbl(quantile, probs = .25) %>% 
  head()
## [1] -11.110399   8.129051 -11.295354  -8.150094  -9.325573  -8.541390

Si à présent, nous souhaitons appliquer plusieurs fonctions à chaque élément de notre liste et récupérer le résultat sous la forme d’un tableau de données, on peut utiliser la fonction map_df(). Admettons que l’on souhaite obtenir, pour chaque élément, la moyenne des valeurs, le premier et le troisième quartiles. On peut procéder comme suit :

simulations %>% 
  map_df(function(x){
    tibble(mean = mean(x),
           q1 = quantile(x, probs = .25),
           q3 = quantile(x, probs = .75))
  })
## # A tibble: 100 x 3
##      mean      q1    q3
##     <dbl>   <dbl> <dbl>
##  1 -10.2  -11.1   -9.37
##  2   9.92   8.13  12.0 
##  3  -8.25 -11.3   -5.24
##  4  -6.88  -8.15  -5.33
##  5  -7.12  -9.33  -5.17
##  6  -7.05  -8.54  -5.38
##  7   4.93   3.44   6.22
##  8   1.74   0.946  2.52
##  9  -4.57  -6.14  -2.94
## 10   9.12   5.77  12.1 
## # … with 90 more rows

Dans l’exemple précédent, à chaque itération, les fonctions mean() et quantile() ont été évaluées sur l’élément courant que nous avons décidé d’appeler x. Le résultat de l’évaluation de ces fonctions a été, à chaque itération, consigné dans un tableau, à l’aide de la fonction tibble().

Afin d’éviter de devoir écrire function(x), il est possible d’utiliser un raccourci : ~. L’élément courant auquel on applique une fonction est alors désigné par le symbole . :

simulations %>% 
  map_df(~tibble(
    mean = mean(.),
           q1 = quantile(., probs = .25),
           q3 = quantile(., probs = .75)
  ))
## # A tibble: 100 x 3
##      mean      q1    q3
##     <dbl>   <dbl> <dbl>
##  1 -10.2  -11.1   -9.37
##  2   9.92   8.13  12.0 
##  3  -8.25 -11.3   -5.24
##  4  -6.88  -8.15  -5.33
##  5  -7.12  -9.33  -5.17
##  6  -7.05  -8.54  -5.38
##  7   4.93   3.44   6.22
##  8   1.74   0.946  2.52
##  9  -4.57  -6.14  -2.94
## 10   9.12   5.77  12.1 
## # … with 90 more rows

On peut noter que dans le résultat final, les tableaux créés à chaque itération ont été collés par lignes. C’est le cas puisque nous avons fait appel à la fonction map_df(). Si en revanche on supprime le suffixe _df() de cette fonction, et que nous utilions uniquement map(), alors le résultat final sera rétribué dans une liste de la même longueur que celle donnée en input à l’argument .x (c’est-à-dire simulations dans notre exemple) :

tmp <- 
  simulations %>% 
  map(~tibble(
    mean = mean(.),
    q1 = quantile(., probs = .25),
    q3 = quantile(., probs = .75))
  )
length(tmp)
## [1] 100
tmp[[1]]
## # A tibble: 1 x 3
##    mean    q1    q3
##   <dbl> <dbl> <dbl>
## 1 -10.2 -11.1 -9.37

4.3.2 Tibbles en input

À présent, considérons le cas où l’objet donné en entrée aux fonctions de type map est un tableau de données. Dans ce cas, la fonction (ou la formule) donnée à l’argument .f de la fonction de type map sera appliquée à chacune des colonnes du tableau de données.

Par exemple, pour connaître la moyenne des colonnes de type numérique dans le tableau de données iris, on peut dans un premier temps sélectionner uniquement les colonnes numériques à l’aide de la fonction select_if() en précisant que l’on souhaite conserver uniquement les colonnes pour lesquelles l’application de la fonction is.numeric() retournerala valeur TRUE. Ensuite, si on applique la fonction map_df() au tableau de données iris dont seules les colonnes numériques ont été conservées, en fournissant à l’argument .f la fonction mean(), on obtient la moyenne pour chaque colonne de ce tableau :

iris %>% 
  select_if(is.numeric) %>% 
  map_df(mean)
## # A tibble: 1 x 4
##   Sepal.Length Sepal.Width Petal.Length Petal.Width
##          <dbl>       <dbl>        <dbl>       <dbl>
## 1         5.84        3.06         3.76        1.20

Si on applique une fonction à l’aide de map() aux colonnes d’un tableau de données, et que cette fonction retourne plusieurs valeurs, il est possible de faire à nouveau appel à une fonction de type map sur le résultat pour extraire un des résultats. Admettons par exemple que l’on souhaite effectuer un test de normalité sur chacune des colonnes (numériques) du tableau de données iris, puis extraire la valeur-p associée au test (en effectuant, par exemple un test de Shapiro-Wilk). La valeur-p est stocjée dans un élément du résutat retourné par la fonction shapiro.test() nommé p.value

iris %>% 
  select_if(is.numeric) %>% 
  map(shapiro.test) %>% 
  map_dbl("p.value")
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
## 1.018116e-02 1.011543e-01 7.412263e-10 1.680465e-08

Comme avec les listes (cf plus haut), il est possible d’appliquer plusieurs fonctions à chaque colonne du tableau de données et de retourner sous la forme d’un tibble chaque sous-résultat. On peut par ailleurs ajouter à la fonction map_df() l’argument .id auquel on indique la valeur "variable" pour faire figurer une colonne indiquant le nom de chaque colonne.

iris %>% 
  select_if(is.numeric) %>% 
  map_df(~tibble(
    mean = mean(.),
    q1 = quantile(., probs = .25),
    q3 = quantile(., probs = .75)
    ),
    .id = "variable"
  )
## # A tibble: 4 x 4
##   variable      mean    q1    q3
##   <chr>        <dbl> <dbl> <dbl>
## 1 Sepal.Length  5.84   5.1   6.4
## 2 Sepal.Width   3.06   2.8   3.3
## 3 Petal.Length  3.76   1.6   5.1
## 4 Petal.Width   1.20   0.3   1.8

Evidemment, il est possible d’appliquer certaines fonctions sur des variables factorielles. Par exemple, si on désire connaître le nombre de valeurs distinctes pour chaque variable catégorielle d’un tableau de données :

iris %>% 
  select_if(is.factor) %>% 
  map_df(~tibble(
    n_dist = n_distinct(.),
    class = class(.x)
    ),
  .id = "variable"
  )
## # A tibble: 0 x 0

Astuce : pour connaître le type de données de chaque colonne, on peut faire appel à la fonction map_chr() en appelant la fonction class() sur chaque colonne :

iris %>% map_chr(class)
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
##    "numeric"    "numeric"    "numeric"    "numeric"  "character"

4.4 Avec {plyr}

Les fonctions que nous allons aborder dans cette section possèdent des noms faciles à se remémorer : la première lettre correspond au format d’entrée des données, la seconde au format de sortie souhaité, et la fin du nom se termine par le suffixe ply . Ainsi, la fonction llapply() prend en entrée une liste, effectue une opération sur les éléments, et retourne une liste (Anderson 2012).

Les différentes fonctions que nous allons passer en revue sont consignées dans le tableau ci-dessous, où les lignes correspondent aux formats d’entrée, et les lignes aux formats de sortie. Pour y avoir accès, il est possible de charger le package. Il est également possible de faire précéder l’appel des fonctions par le nom du package (il existe de nombreux conflits de noms de fonctions entre celle de {plyr} et d’autres packages…)

Type array data.frame list
array aaply() adply() alply()
data.frame daply() ddply() dlply()
list laply() ldply() llply()
Il est possible d’avoir plusieurs arguments en input au lieu d’un seul objet. Les fonctions mlply(), mdply() et maply() s’en chargent. Si à la place du m, la première lettre est un r, il s’agit alors de fonction de réplications. Enfin, si la seconde lettre est un trait de soulignement (_), alors le résultat retourné n’est pas affiché (le code utilise la fonction invisible()).

Tous les arguments de ces fonctions commencent par un point (.), afin d’éviter des incompatibilités avec la fonction à appliquer.

4.4.1 Array en input

Les fonctions aaply(), adply() et alply() appliquent une fonction à chaque portion d’un array et ensuitent joignent le résultat sous forme d’un array, d’un data frame ou d’une list respectivement.

l’argument .margins détermine la manière de découper le tableau. Il y en a quatre pour un tableau en deux dimensions :

  1. .margins = 1 : par lignes ;
  2. .margins = 2 : par colonnes ;
  3. .margins = c(1,2) : par cellule ;
  4. .margins = c() : ne pas faire de découpement.

Pour un tableau en trois dimensions, il y a trois découpages possibles en deux dimensions, trois en une dimension et une en zéro dimension (voir Wickham (2011)) au besoin.

tableau <- array(1:24, dim = c(3, 4, 2),
                 dimnames = list(ligne = letters[1:3],
                                 colonne = LETTERS[1:4],
                                 annee = 2001:2002))
tableau
## , , annee = 2001
## 
##      colonne
## ligne A B C  D
##     a 1 4 7 10
##     b 2 5 8 11
##     c 3 6 9 12
## 
## , , annee = 2002
## 
##      colonne
## ligne  A  B  C  D
##     a 13 16 19 22
##     b 14 17 20 23
##     c 15 18 21 24
# La moyenne des valeurs pour chaque ligne
plyr::aaply(tableau, 1, mean) # résultat sous forme de tableau
##    a    b    c 
## 11.5 12.5 13.5
plyr::adply(tableau, 1, mean) # résultat sous forme de liste
##   ligne   V1
## 1     a 11.5
## 2     b 12.5
## 3     c 13.5
plyr::alply(tableau, 1, mean) # résultat sous forme de liste
## $`1`
## [1] 11.5
## 
## $`2`
## [1] 12.5
## 
## $`3`
## [1] 13.5
## 
## attr(,"split_type")
## [1] "array"
## attr(,"split_labels")
##   ligne
## 1     a
## 2     b
## 3     c
# La moyenne des valeurs pour chaque longitude
# en ne simplifiant pas le résultat
plyr::aaply(tableau, 2, mean, .drop = FALSE)
##        
## colonne  1
##       A  8
##       B 11
##       C 14
##       D 17
# Par lignes et colonnes
plyr::aaply(tableau, c(1,2), mean)
##      colonne
## ligne A  B  C  D
##     a 7 10 13 16
##     b 8 11 14 17
##     c 9 12 15 18
plyr::adply(tableau, c(1,2), mean)
##    ligne colonne V1
## 1      a       A  7
## 2      b       A  8
## 3      c       A  9
## 4      a       B 10
## 5      b       B 11
## 6      c       B 12
## 7      a       C 13
## 8      b       C 14
## 9      c       C 15
## 10     a       D 16
## 11     b       D 17
## 12     c       D 18
# L'affichage prend beaucoup de place
# alply(tableau, c(1,2), mean)

# Avec une fonction définie par l'utilisateur/rice
standardise <- function(x) (x - min(x)) / (max(x) - min(x))
# Standardiser les valeurs par colonne
plyr::aaply(tableau, 2, standardise)
## , , annee = 2001
## 
##        ligne
## colonne a          b         c
##       A 0 0.07142857 0.1428571
##       B 0 0.07142857 0.1428571
##       C 0 0.07142857 0.1428571
##       D 0 0.07142857 0.1428571
## 
## , , annee = 2002
## 
##        ligne
## colonne         a         b c
##       A 0.8571429 0.9285714 1
##       B 0.8571429 0.9285714 1
##       C 0.8571429 0.9285714 1
##       D 0.8571429 0.9285714 1

4.4.2 Data frame en input

En économétrie, les data frames sont très présents. Aussi, la connaissance des fonction daply(), ddply() et dlply() s’avère pratique. En effet, elles sont très utiles pour appliquer des fonctions à des groupes basés sur des combinaisons de variables. Actuellement, il est toutefois, selon moi, plus pratique d’avoir recours aux fonctions du package {dplyr} (group_by(), summarise(), mutate(), …, comme présentées dans la Section @ref(tableaux_de_donnees)). À titre informatif, ces notes de cours présentent toutefois les fonctions du package {plyr}.

Avec les fonctions d*ply(), il est nécessaire d’indiquer quelles variables, ou fonctions de variables on souhaite utiliser, en l’indiquant à l’argument .variables. Elles peuvent être contenue dans le data frame fourni à l’argument .data, ou bien provenir de l’environnement global. R cherchera dans un premier temps si la variable est contenue dans le data frame, et, s’il ne trouve pas, ira chercher dans l’environnement global.

Pour indiquer que l’on désire faire le regroupement selon une variable — mettons variable_1 — il faudra fournir l’expression .(variable_1) à l’argument .variables. Si on souhaite effectuer les regroupement selon les interactions de plusieurs variables — variable_1, variable_2 et variable_3, il faut alors utiliser l’expression suivante : .(variable_1, variable_2, variable_3).

chomage <- 
  tibble(region = rep(c(rep("Bretagne", 4), rep("Corse", 2)), 2),
         departement = rep(c("Cotes-d'Armor", "Finistere",
                             "Ille-et-Vilaine", "Morbihan",
                             "Corse-du-Sud", "Haute-Corse"), 2),
         annee = rep(c(2011, 2010), each = 6),
         ouvriers = c(8738, 12701, 11390, 10228, 975, 1297,
                      8113, 12258, 10897, 9617, 936, 1220),
         ingenieurs = c(1420, 2530, 3986, 2025, 259, 254,
                        1334, 2401, 3776, 1979, 253, 241))

chomage
## # A tibble: 12 x 5
##    region   departement     annee ouvriers ingenieurs
##    <chr>    <chr>           <dbl>    <dbl>      <dbl>
##  1 Bretagne Cotes-d'Armor    2011     8738       1420
##  2 Bretagne Finistere        2011    12701       2530
##  3 Bretagne Ille-et-Vilaine  2011    11390       3986
##  4 Bretagne Morbihan         2011    10228       2025
##  5 Corse    Corse-du-Sud     2011      975        259
##  6 Corse    Haute-Corse      2011     1297        254
##  7 Bretagne Cotes-d'Armor    2010     8113       1334
##  8 Bretagne Finistere        2010    12258       2401
##  9 Bretagne Ille-et-Vilaine  2010    10897       3776
## 10 Bretagne Morbihan         2010     9617       1979
## 11 Corse    Corse-du-Sud     2010      936        253
## 12 Corse    Haute-Corse      2010     1220        241
# Total chomeurs en Bretagne et en Corse pour les années 2010 et 2011
# Sous forme de data frame
plyr::ddply(chomage, plyr::.(annee), summarise, total_chomeurs = sum(ouvriers + ingenieurs))
##   annee total_chomeurs
## 1  2010          53025
## 2  2011          55803
# Sous forme de tableau
plyr::daply(chomage, plyr::.(annee), summarise, total_chomeurs = sum(ouvriers + ingenieurs))
## $`2010`
## [1] 53025
## 
## $`2011`
## [1] 55803
# Sous forme de liste
plyr::dlply(chomage, plyr::.(annee), summarise, total_chomeurs = sum(ouvriers + ingenieurs))
## $`2010`
##   total_chomeurs
## 1          53025
## 
## $`2011`
##   total_chomeurs
## 1          55803
## 
## attr(,"split_type")
## [1] "data.frame"
## attr(,"split_labels")
##   annee
## 1  2010
## 2  2011
# Total chomeurs pour les années 2010 et 2011, par région du data frame
plyr::ddply(chomage, plyr::.(annee, region), summarise,
      total_chomeurs = sum(ouvriers + ingenieurs))
##   annee   region total_chomeurs
## 1  2010 Bretagne          50375
## 2  2010    Corse           2650
## 3  2011 Bretagne          53018
## 4  2011    Corse           2785
# Nombre d'observations pour chaque groupe
plyr::ddply(chomage, plyr::.(annee, region), nrow) 
##   annee   region V1
## 1  2010 Bretagne  4
## 2  2010    Corse  2
## 3  2011 Bretagne  4
## 4  2011    Corse  2
# En utilisant une fonction définie par l'utilisateur/rice
plyr::ddply(chomage, plyr::.(annee, region), function(x){
  moy_ouvriers <- mean(x$ouvriers)
  moy_ingenieurs <- mean(x$ingenieurs)
  data.frame(moy_ouvriers = moy_ouvriers, moy_ingenieurs = moy_ingenieurs)
})
##   annee   region moy_ouvriers moy_ingenieurs
## 1  2010 Bretagne     10221.25        2372.50
## 2  2010    Corse      1078.00         247.00
## 3  2011 Bretagne     10764.25        2490.25
## 4  2011    Corse      1136.00         256.50

4.4.3 List en input

Les fonctions du type l*ply() prennent une liste en entrée. Il n’y a donc pas de paramétrage à effectuer pour choisir un découpage, il est déjà fait.

set.seed(1)
liste <-
  list(normale =rnorm(10),
       logiques =c(TRUE, TRUE, FALSE),
       x =c(0,NA, 3))

# Obtenir la longueur de chaque élément de la liste
plyr::laply(liste, length)
## [1] 10  3  3
plyr::ldply(liste, length)
##        .id V1
## 1  normale 10
## 2 logiques  3
## 3        x  3
plyr::llply(liste, length)
## $normale
## [1] 10
## 
## $logiques
## [1] 3
## 
## $x
## [1] 3
# Calculer la moyenne pour chaque élément
plyr::llply(liste, mean, na.rm = TRUE) %>% 
  unlist()
##   normale  logiques         x 
## 0.1322028 0.6666667 1.5000000
# Appliquer une fonction définie par l'utilisateur/rice
plyr::llply(liste, function(x, y) x /mean(x, na.rm = TRUE) + y, y = 2)
## $normale
##  [1] -2.7385827  3.3891033 -4.3208096 14.0669232  4.4924421 -4.2061356
##  [7]  5.6869803  7.5847895  6.3552892 -0.3099997
## 
## $logiques
## [1] 3.5 3.5 2.0
## 
## $x
## [1]  2 NA  4

4.4.4 Calcul parallèle

En utilisant plusieurs processeurs, on peut effectuer des calculs parallèles, ce qui accélère les calculs dans certains cas. En effet, quand il est possible de fractionner les opérations à effectuer en morceaux, on peut en réaliser une partie sur un processeur, une autre sur un second processeur, et ainsi de suite. Les résultats obtenus sont ensuite rassemblés avant d’être retournés. Le package {doMC} (ou {doSMP} sur Windows) peut être chargé pour utiliser la fonction de calcul parallèle proposé par les fonctions **ply(). Il suffit de préciser le nombre de cœurs souhaité en faisant appel à la fonction registerDoMC(), et de fixer la valeur TRUE à l’argument .parallel de la fonction **ply().

df <- 
  data.frame(valeur_1 = rnorm(12000),
         id = rep(month.abb, each = 1000))

# Fonction lente (et inutile)
f <- function(x){
  res <- rep(NA, nrow(x))
  for(i in 1:length(res)){
    res[i] <- mean(x[1:i, "valeur_1"],)
  }
  mean(res)
}

library(doMC)
# Définir le parallel backend avec le package foreach
registerDoMC(cores=4)

system.time(plyr::ddply(df, plyr::.(id), f))
##    user  system elapsed 
##   0.222   0.024   0.257
system.time(plyr::ddply(df, plyr::.(id), f, .parallel = TRUE))
##    user  system elapsed 
##   0.300   0.183   0.138

4.5 Avec {base}

Le tableau ci-dessous recense les fonctions principales de la famille apply du package {base}.

Fonction Input Output
apply() Matrice ou tableau Vecteur ou tableau ou liste
lapply() Liste ou vecteur Liste
sapply() Liste ou vecteur Vecteur ou matrice ou liste
vapply() Liste ou vecteur Vecteur ou matrice ou liste
tapply() Tableau et facteurs Tableau ou liste
mapply() Listes et/ou vecteurs Vecteur ou matrice ou liste

4.5.1 La fonction lapply

La fonction lapply() applique à chaque élément du premier argument qui lui est donné une fonction indiquée en second argument et retourne le résultat sous forme de liste. La syntaxe est la suivante :

lapply(X, FUN, ...)

avec X la liste ou le vecteur donné en argument sur lequel on désire appliquer la fonction FUN. L’argument ... permet comme expliqué dans une précédente remarque, de fournir des arguments à une fonction imbriquée, en l’occurance à celle que l’on souhaite appliquer à tous les éléments de X.

liste <- 
  list(normale = rnorm(10),
       logiques = c(TRUE,  TRUE, FALSE),
       x = c(0, NA, 3))

# Obtenir la liste des longueurs de chaque élément
lapply(liste, length)
## $normale
## [1] 10
## 
## $logiques
## [1] 3
## 
## $x
## [1] 3
# Calculer la moyenne pour chaque élément
lapply(liste, mean, na.rm = TRUE)
## $normale
## [1] 0.04319732
## 
## $logiques
## [1] 0.6666667
## 
## $x
## [1] 1.5

On peut créer une fonction à l’intérieur de l’appel à la fonction lapply(). Le premier argument est nécessairement un élément du vecteur auquel on souhaite appliquer la fonction.

lapply(liste, function(x) x / mean(x, na.rm = TRUE))
## $normale
##  [1] -17.375456  -6.647068  19.251983 -12.199524 -42.424943  24.224455
##  [7] -12.030570  24.388223  31.518789   1.294111
## 
## $logiques
## [1] 1.5 1.5 0.0
## 
## $x
## [1]  0 NA  2
# Si la fonction doit posséder plusieurs arguments
lapply(liste, function(x, y) x / mean(x, na.rm = TRUE) + y, y = 2)
## $normale
##  [1] -15.375456  -4.647068  21.251983 -10.199524 -40.424943  26.224455
##  [7] -10.030570  26.388223  33.518789   3.294111
## 
## $logiques
## [1] 3.5 3.5 2.0
## 
## $x
## [1]  2 NA  4

On peut appliquer la fonction lapply() sur des tableaux de données, dans la mesure où ces derniers sont des listes. Cela s’avère pratique pour réaliser des opérations pour chaque colonne d’un tableau de données. Afin de prendre moins de place dans l’affichage, l’exemple suivant utilise la fonction unlist() pour aplatir la liste.

data(cars)

# Afficher le type de chaque colonne de la data frame "cars"
unlist(lapply(cars, class))
##     speed      dist 
## "numeric" "numeric"
# Calculer la moyenne pour chaque colonne
unlist(lapply(cars, mean))
## speed  dist 
## 15.40 42.98

Attention, ce qui suit relève plus d’un tour de passe-passe que de la programmation élégante.

Si la fonction que l’on souhaite appliquer aux éléments de notre vecteur retourne un vecteur ligne de même longueur pour chaque élément, la fonction do.call() peut devenir un outil très pratique pour créer une data frame. Par exemple, définissons une liste à l’aide de lapply(): l <- lapply(1:3, function(x) cbind(valeur = x, lettre = LETTERS[x])). Ensuite, pour coller les lignes entre-elles : do.call("rbind", l) %>% data.frame().

L’appel de do.call("rbind", x) revient à faire rbind(x[1], x[2], ..., x[n]) avec x un vecteur de taile \(n\).

4.5.2 La fonction sapply()

La fonction sapply() applique une fonction aux éléments d’un vecteur ou d’une liste et peut retourner un vecteur, une liste ou une matrice. Elle possède la syntaxe suivante :

sapply(X, FUN, simplify, USE.NAMES)

X est le vecteur ou la liste auquel on souhaite appliquer la fonction FUN. Lorsque simplify vaut FALSE, le résultat est retourné sous forme de liste, exactement comme lapply() (la fonction sapply() s’appuie sur la fonction lapply()). Lorsque simplify vaut TRUE (par défaut), le résultat est retourné dans une forme simplifiée, si cela est possible. Si tous les éléments retournés par la fonction FUN sont des scalaires, alors sapply() retourne un vecteur ; sinon, si les éléments retournés ont la même taille, sapply() retourne une matrice avec une colonne pour chaque élément de X auquel la fonction FUN est appliquée. l’argument USE.NAMES, quand il vaut TRUE (par défaut), et si X est de type character, utilise X comme nom pour le résultat, à moins que le résultat possède déjà des noms.

(x <- list(a = 1:10, beta = exp(-3:3), logic = c(TRUE,FALSE,FALSE,TRUE)))

# Application de la fonction quantile() à chaque élément
# pour obtenir la médiane et les quartiles

# Avec lapply()
lapply(x, quantile)

# Avec sapply
sapply(x, quantile)

# Exemple avec USE.NAMES
sapply(LETTERS[1:3], nchar)
sapply(LETTERS[1:3], nchar, USE.NAMES = FALSE)

4.5.3 La fonction vapply

La fonction vapply est similaire à sapply(), mais elle possède un type de valeurs spécifié, ce qui peut rendre l’utilisation plus sûre (et parfois plus rapide). Lorsqu’on lui fournit un data frame, vapply retourne le même résultat que sapply(). Cependant, quand on lui fournit une liste vide, vapply retourne un vecteur logique de longueur nulle (ce qui est plus sensé que la liste vide que returne sapply()).

vapply(X, FUN, FUN.VALUE, ..., USE.NAMES)

avec X, FUN, ... et USE.NAMES les mêmes arguments que pour sapply(). l’argument FUN.VALUE doit être un vecteur, un masque pour la valeur retournée par la fonction de FUN.

# Retourner le vecteur 
sapply(cars, is.numeric)
## speed  dist 
##  TRUE  TRUE
vapply(cars, is.numeric, FUN.VALUE = logical(1))
## speed  dist 
##  TRUE  TRUE
# Avec la liste vide
sapply(list(), is.numeric)
## list()
vapply(list(), is.numeric, FUN.VALUE = logical(1))
## logical(0)

4.5.4 La fonction apply()

La fonction apply() possède la syntaxe suivante : <<boucles_vectorisation_apply_apply_syntaxe, eval = FALSE>>= apply(X, MARGIN, FUN, …) @ avec X une matrice ou un tableau, MARGIN indiquant si on souhaite appliquer la fonction FUN aux lignes (MARGIN = 1) ou aux colonnes (MARGIN = 2), et ... des arguments supplémentaires éventuels à passer à la fonction FUN.

(X <- matrix(1:9, ncol = 3))
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9
# Somme par ligne
apply(X, MARGIN = 1, sum)
## [1] 12 15 18
# Somme par colonne
apply(X, MARGIN = 2, sum)
## [1]  6 15 24
# Fonction définie par l'utilisateur/rice
apply(X, MARGIN = 1, function(x) sum(x) / sum(X))
## [1] 0.2666667 0.3333333 0.4000000

4.5.5 La fonction tapply()

La fonction tapply() s’applique à chaque cellule d’un tableau, sur des regroupements définis par les variables catégorielles fournies. La syntaxe est la suivante :

tapply(X,INDEX, FUN, ..., simplify)

avec X le taleau de données, INDEX une liste d’un ou plusieurs facteurs, chacun de même taille que X. l’argument FUN renseigne la fonction que l’on souhaite appliquer. Si SIMPLIFY vaut FALSE, le résultat est un tableau de mode list. Sinon (par défaut), le résultat est un tableau de scalaires.

data(iris)
head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa
# Moyenne de la longueur des sépales par espèce
tapply(iris$Sepal.Length, iris$Species, mean)
##     setosa versicolor  virginica 
##      5.006      5.936      6.588
# Pour retourner le résultat sous forme de liste
tapply(iris$Sepal.Length, iris$Species, mean, simplify = FALSE)
## $setosa
## [1] 5.006
## 
## $versicolor
## [1] 5.936
## 
## $virginica
## [1] 6.588

4.5.6 La fonction mapply()

La fonction mapply() applique une fonction à plusieurs listes ou vecteurs. La syntaxe est la suivante :

mapply(FUN, ..., MoreArgs, SIMPLIFY, USE.NAMES)

avec FUN la fonction à appliquer aux vecteurs ou listes fournies (grâce à ...), MoreArgs une liste d’arguments supplémentaires à fournir à la fonction à appliquer. Les arguments SIMPLIFY et USE.NAMES ont le même usage que pour la fonction sapply().

(l1 <- list(a = c(1:5), b = c(6:10)))
## $a
## [1] 1 2 3 4 5
## 
## $b
## [1]  6  7  8  9 10
(l2 <- list(c = c(11:15), d = c(16:20)))
## $c
## [1] 11 12 13 14 15
## 
## $d
## [1] 16 17 18 19 20
# La somme des éléments correspondants de l1 et l2
mapply(sum, l1$a, l1$b, l2$c, l2$d)
## [1] 34 38 42 46 50
# Attention au recyclage silencieux !
(l1 <- list(a = c(1:5), b = c(6:20)))
## $a
## [1] 1 2 3 4 5
## 
## $b
##  [1]  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
mapply(sum, l1$a, l1$b, l2$c, l2$d)
##  [1] 34 38 42 46 50 39 43 47 51 55 44 48 52 56 60

4.5.7 La fonction Vectorize()

La fonction Vectorize() permet de convertir une fonction scalaire en une fonction vectorielle. Attention, cela ne permet pas d’améliorer la rapidité d’exécution du code. Par contre, son utilisation assez intuitive permet de gagner du temps. Il s’agit donc de faire l’arbitrage entre le temps passé à trouver un moyen élégant et efficace pour effectuer une opération en passant par de réels calculs vectoriels et le gain d’exécution que ce calcul vectoriel apporte vis-à-vis d’une boucle. La syntaxe de la fonction Vectorize() est la suivante :

Vectorize(FUN, vectorize.args, SIMPLIFY, USE.NAMES)

avec FUN une fonction à appliquer, vectorize.args un vecteur d’arguments (de type caractère) qui devraient être vectorisés (par défaut, tous les arguments de FUN). Les arguments SIMPLIFY et USE.NAMES on le même emploi que dans la fonction sapply().

f <- function(x = 1:3, y) c(x, y)
# On "vectorise" la fonction f
vf <- Vectorize(f, SIMPLIFY = FALSE)
f(1:3, 1:3)
## [1] 1 2 3 1 2 3
vf(1:3, 1:3)
## [[1]]
## [1] 1 1
## 
## [[2]]
## [1] 2 2
## 
## [[3]]
## [1] 3 3
# Vectorise seulement y, pas x
vf(y = 1:3)
## [[1]]
## [1] 1 2 3 1
## 
## [[2]]
## [1] 1 2 3 2
## 
## [[3]]
## [1] 1 2 3 3

4.6 Exercices

Exercice 1. Boucle while()

  1. à l’aide de la fonction while(), créer une boucle qui permet de calculer la factorielle d’un nombre ;
  2. Réutiliser le code de la question précédente pour en faire une fonction qui, lorsqu’on lui donne un nombre, retourne sa factorielle. Comparer le résultat avec la fonction factorial().

Exercice 2. Boucles while() et for()

  1. Choisir un nombre mystère entre 1 et 100, et le stocker dans un objet que l’on nommera nombre_mystere. Ensuite, créer une boucle qui à chaque itération effectue un tirage aléatoire d’un entier compris entre 1 et 100. Tant que le nombre tiré est différent du nombre mystère, la boucle doit continuer. à la sortie de la boucle, une variable que l’on appellera nb_tirages contiendra le nombre de tirages réalisés pour obtenir le nombre mystère ;
  2. Utiliser le code de la question précédente pour réaliser la fonction trouver_nombre, qui, lorsqu’on lui donne un nombre compris entre 1 et 100, retourne le nombre de tirages aléatoires d’entiers compris entre 1 et 100 nécessaires avant de tirer le nombre mystère ;
  3. En utilisant une boucle for, faire appel 1000 fois à la fonction trouver_nombre() qui vient d’être créée. à chaque itération, stocker le résultat dans un élément d’un vecteur que l’on appellera nb_essais_rep. Enfin, afficher la moyenne du nombre de tirages nécessaires pour retrouver le nombre magique.

nb_essais_rep <- rep(NA, 1000)

Exercice 3. Boucles for()

  1. Parcourir les entiers de 1 à 20 à l’aide d’une boucle for en affichant dans la console à chaque itération si le nombre courant est pair ;
  2. L’objet month.name est un vecteur contenant les noms des mois du calendrier, en anglais. Parcourir chacun des éléments de ce vecteur, et afficher dans la console pour chacun des mois si le nombre de caractères composant le nom du mois est pair ou impair.

Exercice 4. Suite de Fibonacci

Utiliser une boucle for pour reproduire la suite de Fibonacci jusqu’à son dixième terme (la séquence \(F_n\) est définie par la relation de récurrence suivante : \(F_{n} = F_{n-1} + F_{n-2}\) ; les valeurs initiales sont : \(F_0 = 0\) et \(F_{1} = 1\)).

Exercice 5. Barre de progression

Considérons le vecteur de chaînes de caractères ids ainsi que la liste res :
library(magrittr)
n <- 1000
ids <- 
  str_c(sample(LETTERS, n, replace = TRUE),
        sample(letters, n, replace = TRUE)) %>% 
  unique()
res <- vector("list", length(ids))

Parcourir les éléments du vecteur à l’aide d’une boucle for(). à chaque itération, stocker dans l’élément de la liste res dont la position correspond à celle de l’identifiant courant dans ids les informations suivantes : (i) l’identifiant courant et (ii) la somme de \(50 000\) tirages aléatoires selon une \(\mathcal{N}(0,1)\).

Afficher l’état d’avancement de la boucle à l’aide d’une barre de progression.

Exercice 6. Fonctions appliquées aux éléments d’une liste

Soit une liste nommée twittos, disponible à l’adresse suivante : http://egallic.fr/Enseignement/R/Exercices/donnees/twittos.rda. Elle contient des informations fictives sur des utilisateur•rice•s de Twitter ; chaque élément de cette liste est une liste dont les éléments sont les suivants :

  • screen_name : nom d’utilisateur•rice ;
  • nb_tweets : nombre de tweets ;
  • nb_followers : nombre de followers ;
  • nb_friends : nombre de followings ;
  • created_at : date de création du compte ;
  • location : ville renseignée.
  1. Importer le contenu du fichier dans la session R ;
  2. Utiliser la fonction map appropriée sur twittos pour récupérer une liste contenant uniquement les noms d’utilisateur•rice•s. Faire de même pour le nombre de followers, puis appliquer unlist() au résultat ;
  3. Créer une fonction qui, quand on lui fournit un élément de la liste twittos, c’est-à-dire les informations sous forme de liste d’un•e seul•e utilisateur•rice, retourne ces informations sous forme de tableau de données. Nommer cette fonction twittos_to_df ;
  4. Appliquer la fonction twittos_to_df() au premier élément de la liste twittos, puis utiliser la fonction map appropriée pour appliquer la fonction twittos_to_df() à tous les éléments de la liste. Stocker ce dernier résultat dans un objet appelé res ;
  5. Quelle est la structure de l’objet res obtenu à la question précédente ?
  6. Importer le fichier disponible à cette adresse dans la session R : http://egallic.fr/Enseignement/R/Exercices/donnees/dates_tw.rda. Il s’agit d’une liste donc chaque élément contient une liste indiquant le nom d’un•e utilisateuret la date de chacun de ses tweets.
  7. Appliquer la fonction map appropriée à la liste dates_tw qui vient d’être importée dans R, pour afficher l’heure moyenne des tweets pour chaque utilisateur•rice, puis faire de même pour l’écart-type.

Exercice 7. Fonctions appliquées aux éléments d’une matrice

  1. Créer une matrice de dimension \(100\times 5\), donc chaque vecteur colonne est composé de tirages issus d’une loi Normale centrée réduite ;
  2. Utiliser la fonction apply() pour calculer la moyenne des valeurs de chaque colonne ;
  3. Utiliser la fonction apply() pour calculer l’écart-type des valeurs de chaque colonne.

References

Anderson, Sean. 2012. “A Quick Introduction to Plyr.” http://seananderson.ca/courses/12-plyr/plyr_2012.pdf.

Burns, Patrick. 2011. “The R Inferno.” http://www.burns-stat.com/pages/Tutor/R_inferno.pdf.

Ross, Noam. 2014. “Vectorization in R: Why?” http://www.noamross.net/blog/2014/4/16/vectorization-in-r--why.html.

Wickham, Hadley. 2011. “The Split-Apply-Combine Strategy for Data Analysis.”

Wickham, Hadley. 2014. “Functionals.” In Advanced R. http://adv-r.had.co.nz/Functionals.html.