Formation R perfectionnement Martin Chevalier (Insee)


Cette page comporte l’ensemble des cas pratiques de la formation R perfectionnement donnée à la Drees les 16 et 17 avril 2018 accompagnés de leur correction.

Les supports de cette formation ont été conçus sous RStudio avec R Markdown et compilés le 10/04/2018. Certains éléments de mise en forme du site compagnon sont repris de l’ouvrage R packages de Hadley Wickham.

Ces supports seront durablement disponibles à l’adresse http:// t.slmc.fr/perf et leur code source sur github. L’ensemble est librement réutilisable sous © 2016-2018 Martin Chevalier CC BY-NC-SA 3.0.

Savoir utiliser les fonctions *apply(), do.call() et Reduce()

Le package microbenchmark est souvent utilisé dans cette partie et les suivantes pour mesurer la performance des des solutions testées :

install.packages("microbenchmark")
library(microbenchmark)

 

Cas pratique 1 Fonctions et environnements

Tout ce qui se passe dans R correspond à un appel de fonction. Comprendre le fonctionnement des fonctions et savoir en créer soi-même est donc crucial.

  1. Utilisez les guillemets simples inversés (AltGr + 7 sur le clavier azerty) pour afficher le code associé au signe +. Utilisez-le comme une fonction classique avec la syntaxe nomFonction(parametre1, parametre2).

     

  2. Définissez la fonction monCalcul(x, puissance) qui pour un vecteur numérique x quelconque :

    1. calcule sa somme ;

    2. met la somme à la puissance puissance.

    Dans un second temps, donnez à puissance la valeur 2 par défaut et faites en sorte que la fonction prenne en charge les vecteurs x présentant des valeurs manquantes NA.

     

  3. Modifier la fonction pour ajouter une étape de vérification du type de x : prévoyez un message d’erreur si x est de type character ou factor.

     

  4. Quelle est la valeur de l’objet T ? Comment expliquez-vous que cet objet soit défini alors que vous ne l’avez pas vous-même créé (vous pouvez utiliser la fonction getAnywhere() pour répondre) ?

     

  5. Soumettez le code T <- 3. Que vaut désormais l’objet T ? Pourquoi R n’accède-t-il plus à la valeur stockée par défaut (vous pouvez utiliser la fonction search() pour répondre) ? Comment accéder désormais à la valeur par défaut ? Que retenez-vous quant à l’utilisation de T et de F en lieu et place de TRUE et FALSE dans un code ?

     

 

Cas pratique 2 apply() : Appliquer une fonction selon les dimensions d’une matrice

  1. Créez une matrice e1 de 3 lignes et de 5 colonnes en utilisant la fonction runif(). Ses valeurs sont-elles identiques si vous la générez une seconde fois ? Comment faire pour que cela soit le cas ?

     

  2. Utilisez la fonction apply() pour calculer la somme des termes de chaque ligne de e1. Comment auriez-vous pu faire autrement ? Utilisez la fonction microbenchmark() pour comparer ces deux solutions.

     

  3. Utilisez la fonction apply() pour centrer-réduire toutes les colonnes de e1 (i.e. leur soustraire leur moyenne puis les diviser par leur écart-type).

     

 

Cas pratique 3 lapply() et sapply() : Appliquer une fonction aux éléments d’un vecteur, d’une liste ou aux colonnes d’un data.frame

  1. On définit le vecteur f1 par f1 <- 5:15. Utilisez la fonction sapply() pour calculer la somme cumulée des éléments de f1. Quelle(s) alternative(s) envisageriez-vous ? Utilisez la fonction microbenchmark() pour comparer ces solutions.

     

  2. On définit la liste f2 par

    f2 <- list(
      sample.int(26, 10, replace = TRUE)
      , sample.int(26, 100, replace = TRUE)
      , sample.int(26, 1000, replace = TRUE)
    )

Utilisez les fonctions lapply() ou sapply() pour :

  • retrouver la longueur de chacun des éléments de f2 ;
  • extraire les 5 premiers éléments de chacun des éléments de f2 ;
  • remplacer chaque élément de f2 par le vecteur de lettres (en minuscules) dont il représente les positions dans l’alphabet.

     

  1. On définit le data.frame f3 par

    set.seed(1)
    f3 <- data.frame(
      id = letters[1:20]
      , by = rep(letters[1:5], times = 4)
      , matrix(runif(100), ncol = 5)
      , stringsAsFactors = FALSE
    )

    Utilisez les fonctions lapply() ou sapply() pour :

  • déterminer le type de chacune des variables de f3;
  • calculer la moyenne de toutes les variables numériques de f3;
  • convertir toutes les variables de type character en facteurs.
     

 

Cas pratique 4 tapply() : Appliquer une fonction selon les modalités d’un vecteur

  1. On définit le vecteur g1 <- sample(20) et le vecteur g2 <- rep(c("H", "F"), times = 10). Utilisez tapply() pour calculer la moyenne de g1 selon les groupes définis par g2.

     

  2. On repart du data.frame f3 du cas pratique précédent. Utilisez tapply() pour calculer le total de la variable X1 selon les modalités de la variable by.

     

  3. Combinez la fonction split() avec sapply() pour obtenir le même résultat. Comment calculeriez-vous le total de toutes les variables numériques de f3 selon les modalités de la variable by ?

     

 

Cas pratique 5 do.call() : Appliquer une fonction simultanément à l’ensemble des éléments d’une liste

De nombreuses fonctions peuvent porter sur un nombre indéterminé d’éléments : c(), sum(), rbind(), etc. Pour les appliquer à l’ensemble des éléments d’une liste sans avoir à tous les écrire un à un, il suffit d’utiliser do.call().

  1. On définit la liste h1 <- list(1:5, 6:10, 11:15). Que se passe-t-il si vous soumettez sum(h1) ? Utilisez do.call() pour sommer l’ensemble des éléments de h1. Comparez avec le résultat de sapply(h1, sum).

     

  2. Réunissez les éléments de h1 en un seul vecteur avec la fonction base::c(). Comparez avec le résultat de lapply(h1, base::c)

     

  3. On définit la liste de matrices h2

    h2 <- list(
      matrix(1:6, nrow = 2)
      , matrix(7:12, nrow = 2)
      , matrix(13:18, nrow = 2)
    )
    Utilisez do.call() avec les fonctions rbind() et cbind() pour concaténer l’ensemble des éléments de h2 en ligne ou en colonne respectivement.

     

 

Cas pratique 6 Reduce() : Appliquer une fonction successivement à l’ensemble des éléments d’une liste

De nombreuses fonctions portent sur deux éléments précisément : opérations arithmétiques, merge(), etc. Pour les appliquer successivement à l’ensemble des éléments d’une liste, il suffit d’utiliser Reduce().

  1. On repart de la liste h1 du cas pratique précédent. Observez le résultat de Reduce(`+`, h1) : comment le comprenez-vous ? Comparez avec sapply(h1, sum).

     

  2. On définit la liste de data.frame i1

    i1 <- list(
      data.frame(id = letters[1:4], var1 = 1:4, stringsAsFactors = FALSE)
      , data.frame(id = letters[2:5], var2 = 5:8, stringsAsFactors = FALSE)
      , data.frame(id = letters[3:6], var3 = 9:12, stringsAsFactors = FALSE)
    )
    Utilisez Reduce() pour fusionner l’ensemble des éléments de i1 selon la variable id. Comment ajusteriez-vous ce code pour pouvoir utiliser l’option all = TRUE de la fonction merge() ?

     

Travailler efficacement sur des données avec base R

L’ensemble des cas pratiques qui suivent portent sur les données de l’enquête Emploi en continu stockées dans le fichier eect4.rds. La fonction readRDS() permet de le charger en mémoire :

setwd("Y:/Documentation/R/R_perfectionnement/donnees")
eec <- readRDS("eect4.rds")
str(eec)
## 'data.frame':    34913 obs. of  19 variables:
##  $ IDENT    : chr  "G0A56JP6" "G0A56JP6" "G0A56JR6" "G0A56JS6" ...
##  $ TRIM     : chr  "4" "4" "4" "4" ...
##  $ NOI      : chr  "01" "02" "01" "01" ...
##  $ REG      : chr  "11" "11" "11" "11" ...
##  $ AGE      : chr  "66" "29" "27" "29" ...
##  $ SEXE     : chr  "2" "1" "2" "2" ...
##  $ CSE      : chr  "56" "81" "38" "37" ...
##  $ DIP11    : chr  "71" "42" "10" "11" ...
##  $ ACTEU    : chr  "1" "2" "1" "1" ...
##  $ SALRED   : int  596 NA 2700 2666 11967 NA 2000 2800 2333 3500 ...
##  $ STC      : chr  "2" NA "2" "2" ...
##  $ TAM1D    : chr  NA NA NA NA ...
##  $ AIDREF   : chr  NA "5" NA NA ...
##  $ TPP      : chr  "1" NA "1" "1" ...
##  $ NBAGENF  : chr  "0" "0" "0" "0" ...
##  $ DUHAB    : chr  "7" NA "7" "7" ...
##  $ PUB3FP   : chr  "4" NA "4" "4" ...
##  $ NAIA     : chr  "1946" "1983" "1985" "1983" ...
##  $ EXTRI1613: num  1777 1777 2045 1898 1754 ...

 

Cas pratique 7 Sélection d’observations

  1. Utilisez l’opérateur [ pour sélectionner l’individu appartenant au ménage dont l’IDENT est "GFO5NVUE" et dont le numéro d’ordre NOI est "02".

     

  2. Cherchez de la documentation sur la fonction subset(). Comparez ses performances avec celles de l’opérateur [ à l’aide de la fonction microbenchmark().

     

  3. Concaténez les valeurs des variables IDENT et NOI et utilisez le résultat comme noms de ligne. Retrouvez alors l’individu de la question a à l’aide de son nom de ligne. Comparez les performances de cette méthode avec celles de l’opérateur [.

     

 

Cas pratique 8 Création de variables

On souhaite recoder la variable AGE en trois modalités : 15-30 ans, 31-60 ans et plus de 60 ans. On part du code suivant :

for(i in 1:nrow(eec)) eec$trage1[i] <- if(as.numeric(eec$AGE[i]) < 31) "15-30" else if(as.numeric(eec$AGE[i]) < 61) "31-60" else "61 et +"
  1. Mesurez le temps d’exécution du code proposé. Que pensez-vous de cette syntaxe ?

     

  2. On propose une seconde version du code :

    eec$trage2 <- ifelse(as.numeric(eec$AGE) < 31, "15-30", ifelse(
      as.numeric(eec$AGE) < 61, "31-60", "61 et +"
    ))

    Vérifiez que le résultat est identique à la proposition initiale (par exemple avec identical()ou all.equal()) et mesurez le temps temps d’exécution de cette deuxième version. Êtes-vous surpris du gain de performances ?