L’objectif de ce troisième et dernier module est de réutiliser dans un cadre métier les briques élémentaires du langage introduites dans le module précédent:

  • présentation du type data.frame et de ses relations avec les vecteurs, les matrices et les listes;
  • opérations courantes sur les tables de données statistiques: sélection d’observations et de variables, création et modification de variables, tris, fusions, etc.;
  • utilisation de R pour la statistique descriptive et la production de graphiques

En dernière partie, des liens complémentaires sont fournis vers le support de la formation R perfectionnement ainsi que vers des exemples d’utilisation plus spécifiques du logiciel (analyse de données multidimensionnelle, régression).

 

Manipuler les data.frame

Dans R, la majeure partie des données statistiques se présente sous la forme de data.frame : ces objets permettent en effet de représenter sous la forme d’une table (i.e. d’un objet à deux dimensions) des données de nature tant quantitative (variables numériques) que qualitative (variables de type caractère ou facteur).

Créer des data.frame et y sélectionner des éléments

Pour créer un objet de type data.frame, il suffit d’utiliser la fonction data.frame().

# Création du data.frame df1
df1 <- data.frame(
  var1 = 1:10
  , var2 = letters[1:10]
  , var3 = rep(c(TRUE, FALSE), times = 5)
)

# Caractéristiques de df1
str(df1)
  ## 'data.frame':  10 obs. of  3 variables:
  ##  $ var1: int  1 2 3 4 5 6 7 8 9 10
  ##  $ var2: Factor w/ 10 levels "a","b","c","d",..: 1 2 3 4 5 6 7 8 9 10
  ##  $ var3: logi  TRUE FALSE TRUE FALSE TRUE FALSE ...

# Premières lignes de df1
head(df1)
  ##   var1 var2  var3
  ## 1    1    a  TRUE
  ## 2    2    b FALSE
  ## 3    3    c  TRUE
  ## 4    4    d FALSE
  ## 5    5    e  TRUE
  ## 6    6    f FALSE

Il est impératif que tous les éléments qui composent un data.frame soient de même longueur.

# Création du data.frame df3
df3 <- data.frame(
  var1 = 1:10
  , var2 = 1:15
)
  ## Error in data.frame(var1 = 1:10, var2 = 1:15): les arguments impliquent des nombres de lignes différents : 10, 15

 


Remarque Par défaut, la fonction data.frame() convertit les variables caractères en facteurs (cf. module 2). Pour éviter ce comportement (pas toujours souhaitable), il suffit d’utiliser l’argument stringsAsFactors = FALSE.

# Création du data.frame df2
df2 <- data.frame(
  var1 = 1:10
  , var2 = letters[1:10]
  , var3 = rep(c(TRUE, FALSE), times = 5)
  , stringsAsFactors = FALSE
)

# Caractéristiques de df2
str(df2)
  ## 'data.frame':  10 obs. of  3 variables:
  ##  $ var1: int  1 2 3 4 5 6 7 8 9 10
  ##  $ var2: chr  "a" "b" "c" "d" ...
  ##  $ var3: logi  TRUE FALSE TRUE FALSE TRUE FALSE ...
# Note : dans df2, var2 est de type caractère alors que dans 
# df1 elle a été automatiquement convertie en factor. 

Pour empêcher la conversion de caractères en facteurs pour toute une session, il suffit de modifier l’option globale stringsAsFactors.

# Modification de l'option globale stringsAsFactors 
options(stringsAsFactors = FALSE)

# Désormais l'option stringsAsFactors n'est plus nécessaire 
# dans chaque appel de fonction
df3 <- data.frame(
  var1 = 1:10
  , var2 = letters[1:10]
  , var3 = rep(c(TRUE, FALSE), times = 5)
)
str(df3)
  ## 'data.frame':  10 obs. of  3 variables:
  ##  $ var1: int  1 2 3 4 5 6 7 8 9 10
  ##  $ var2: chr  "a" "b" "c" "d" ...
  ##  $ var3: logi  TRUE FALSE TRUE FALSE TRUE FALSE ...

 

Du point de vue de sa structure, un data.frame est en réalité une liste dont tous les éléments ont la même longueur : c’est ce qui permet de le représenter sous la forme d’un tableau à deux dimensions.

# Un data.frame est une liste...
is.list(df1)
  ## [1] TRUE

# ... dont tous les éléments sont de même longueur
lapply(df1, length)
  ## $var1
  ## [1] 10
  ## 
  ## $var2
  ## [1] 10
  ## 
  ## $var3
  ## [1] 10

De ce fait, les data.frame empruntent leurs caractéristiques tantôt aux listes, tantôt aux matrices :

  • Comme une matrice, un data.frame a deux dimensions (fonction dim()) ; mais comme une liste, sa longueur (fonction length()) correspond à son nombre d’éléments (son nombre de variables).

    # Dimensions de df1 : comme une matrice
    dim(df1)
      ## [1] 10  3
    nrow(df1)
      ## [1] 10
    ncol(df1)
      ## [1] 3
    
    # Longueur de df1 : comme une liste
    length(df1)
      ## [1] 3

 

  • Comme avec une matrice, on accède aux noms de lignes et de colonne d’un data.frame avec les fonctions rownames() et colnames() ; mais comme avec une liste, les noms de colonnes sont aussi directement accessibles avec names().

    # rownames() et colnames() : comme avec une matrice
    rownames(df1)
      ##  [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10"
    colnames(df1)
      ## [1] "var1" "var2" "var3"
    
    # names() : comme avec une liste
    names(df1)
      ## [1] "var1" "var2" "var3"

 

  • Comme avec une matrice, il est possible d’accéder aux éléments d’un data.frame en indiquant leurs deux positions dans un opérateur [ ; mais comme avec une liste, il est également possible d’utiliser les opérateurs [[ et $.

    df1
      ##    var1 var2  var3
      ## 1     1    a  TRUE
      ## 2     2    b FALSE
      ## 3     3    c  TRUE
      ## 4     4    d FALSE
      ## 5     5    e  TRUE
      ## 6     6    f FALSE
      ## 7     7    g  TRUE
      ## 8     8    h FALSE
      ## 9     9    i  TRUE
      ## 10   10    j FALSE
    # On cherche à accéder à l'élément en ligne 8, colonne 2 de df1
    
    # - comme une matrice : avec `[` et deux positions 
    df1[8, 2]
      ## [1] h
      ## Levels: a b c d e f g h i j
    df1[8, "var2"]
      ## [1] h
      ## Levels: a b c d e f g h i j
    
    # - comme une liste : avec `[[` pour sélectionner la colonne,
    # puis [ pour sélectionner la ligne
    df1[[2]][8]
      ## [1] h
      ## Levels: a b c d e f g h i j
    df1[["var2"]][8]
      ## [1] h
      ## Levels: a b c d e f g h i j
    
    # - comme une liste : avec `$` pour sélectionner la colonne,
    # puis [ pour sélectionner la ligne
    df1$var2[8]
      ## [1] h
      ## Levels: a b c d e f g h i j

 

Les fonctions as.matrix(), as.list() et as.data.frame() permettent de convertir un data.frame en liste ou en matrice, et inversement.

# Conversion de df1 en matrice
as.matrix(df1)
  ##       var1 var2 var3   
  ##  [1,] " 1" "a"  " TRUE"
  ##  [2,] " 2" "b"  "FALSE"
  ##  [3,] " 3" "c"  " TRUE"
  ##  [4,] " 4" "d"  "FALSE"
  ##  [5,] " 5" "e"  " TRUE"
  ##  [6,] " 6" "f"  "FALSE"
  ##  [7,] " 7" "g"  " TRUE"
  ##  [8,] " 8" "h"  "FALSE"
  ##  [9,] " 9" "i"  " TRUE"
  ## [10,] "10" "j"  "FALSE"
# Note : au passage les variables ont toutes été converties
# en caractères, car une matrice ne peut avoir qu'un seul
# et unique type

# Conversion de df1 en liste
as.list(df1)
  ## $var1
  ##  [1]  1  2  3  4  5  6  7  8  9 10
  ## 
  ## $var2
  ##  [1] a b c d e f g h i j
  ## Levels: a b c d e f g h i j
  ## 
  ## $var3
  ##  [1]  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE
# Note : on n'a pas à proprement parler affaire ici à une
# "conversion" (un data.frame est une liste) mais plutôt
# à la suppression de certains attributs spécifiques aux
# data.frame (noms de ligne notamment)
rownames(df1)
  ##  [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10"
rownames(as.list(df1))
  ## NULL

# Conversion d'une matrice en data.frame
as.data.frame(matrix(1:10, ncol = 5))
  ##   V1 V2 V3 V4 V5
  ## 1  1  3  5  7  9
  ## 2  2  4  6  8 10

# Conversion d'une liste en data.frame
as.data.frame(list(a = 1:5, b = letters[5:1]))
  ##   a b
  ## 1 1 e
  ## 2 2 d
  ## 3 3 c
  ## 4 4 b
  ## 5 5 a
# Note : dans ce cas il est également impératif
# que tous les éléments de la liste aient bien la même
# longueur. 

 

Cas pratique 3.1 Sélectionner des variables et des observations dans une table

Ce cas pratique aborde plusieurs manipulations courantes de sélection de variables et d’observations dans une table. Comme la plupart des cas pratiques de ce module, il repose sur l’utilisation des données de l’enquête Emploi en continu 2012 restreinte au quatrième trimestre et aux individus en première ou sixième interrogation. Ces données correspondent au fichier eect4.rds contenu dans le fichier donnees.zip.

  1. Après avoir modifié le répertoire de travail avec setwd(), utilisez la fonction readRDS() pour charger le fichier eect4.rds dans l’objet eec (cf. la remarque finale du module 1 pour l’utilisation de la fonction readRDS()).

     

  2. Pour simplifier le travail sur cette table, on souhaite normaliser la casse des noms de variable. Proposez une méthode pour passer l’ensemble des noms de variable en minuscules et appliquez-la.

     

     

  3. On souhaite créer deux nouvelles tables ne contenant que les variables sur lesquelles portent différents aspects de l’étude :

    1. eec2 qui ne contienne que les variables ident, noi, acteu et extri1613.

       

    2. eec3 qui contienne toutes les variables de eec à l’exception de cse.

       

       

  4. On souhaite désormais créer une nouvelle table eec4 contenant toutes les variables mais uniquement pour les individus appartenant à la population active (acteu vaut "1" ou "2"). Comment procéderiez-vous ?

     

Créer ou modifier des variables dans un data.frame

Pour créer une nouvelle variable dans un data.frame, le plus simple est d’utiliser l’opérateur $.

# Création du data.frame df5
df5 <- data.frame(
  var1 = letters[1:4]
  , var2 = rep(c(FALSE, TRUE), times = 2)
  , stringsAsFactors = FALSE
)
df5
  ##   var1  var2
  ## 1    a FALSE
  ## 2    b  TRUE
  ## 3    c FALSE
  ## 4    d  TRUE

# Ajout de la variable var3 avec $
df5$var3 <- (1:4)^2
df5
  ##   var1  var2 var3
  ## 1    a FALSE    1
  ## 2    b  TRUE    4
  ## 3    c FALSE    9
  ## 4    d  TRUE   16

Pour créer une variable à partir d’une ou plusieurs autres de la table, il suffit d’utiliser l’opérateur $ plusieurs fois.

# Création de la variable var4 à partir de var3
df5$var4 <- df5$var3 * 2
df5
  ##   var1  var2 var3 var4
  ## 1    a FALSE    1    2
  ## 2    b  TRUE    4    8
  ## 3    c FALSE    9   18
  ## 4    d  TRUE   16   32

# Conversion de var2 de logique vers numérique
df5$var2 <- as.numeric(df5$var2)
df5
  ##   var1 var2 var3 var4
  ## 1    a    0    1    2
  ## 2    b    1    4    8
  ## 3    c    0    9   18
  ## 4    d    1   16   32
# Note : modifier à la volée une variable existante ne pose 
# aucun problème

Pour effectuer un recodage manuel selon une ou plusieurs conditions (comme un IF THEN ELSE dans SAS), trois méthodes sont disponibles :

  1. Pour les variables dichotomiques uniquement, utiliser des opérateurs logiques pour créer un nouveau vecteur.

    # Création de la variable var5 valant TRUE si var4 > 10 et var2 = 1
    df5$var5 <- df5$var4 > 10 & df5$var2 == 1
    df5
      ##   var1 var2 var3 var4  var5
      ## 1    a    0    1    2 FALSE
      ## 2    b    1    4    8 FALSE
      ## 3    c    0    9   18 FALSE
      ## 4    d    1   16   32  TRUE
  2. Créer la variable recodée progressivement en utilisant l’opérateur [.

    # Création de la variable var6 identique à var5
    df5$var6 <- "Non"
    df5$var6[df5$var4 > 10 & df5$var2 == 1] <- "Oui"
    df5
      ##   var1 var2 var3 var4  var5 var6
      ## 1    a    0    1    2 FALSE  Non
      ## 2    b    1    4    8 FALSE  Non
      ## 3    c    0    9   18 FALSE  Non
      ## 4    d    1   16   32  TRUE  Oui
  3. Utiliser la fonction ifelse().

    # Création de la variable var7 identique à var5 et var6
    df5$var7 <- ifelse(df5$var4 > 10 & df5$var2 == 1, "Oui", "Non")
    df5
      ##   var1 var2 var3 var4  var5 var6 var7
      ## 1    a    0    1    2 FALSE  Non  Non
      ## 2    b    1    4    8 FALSE  Non  Non
      ## 3    c    0    9   18 FALSE  Non  Non
      ## 4    d    1   16   32  TRUE  Oui  Oui

    La fonction ifelse() prend trois arguments : l’expression logique à évaluer, la valeur à renvoyer si l’expression est vraie, la valeur à renvoyer si l’expression est fausse. Il est possible d’imbriquer des fonctions ifelse() pour effectuer des recodages complexes.

 


Remarque Savoir tirer parti de la fonction within()

Quand on met en oeuvre un recodage, on est fréquemment amené à répéter le nom du data.frame sur lequel on travaille. La fonction within() permet d’alléger l’écriture d’un recodage et de faciliter la compréhension d’un code en évitant cette répétition.

# Concaténation manuelle des variables var1 à var4
df5$var7 <- paste0(df5$var1, df5$var2, df5$var3, df5$var4)

# Syntaxe allégée avec la fonction within()
# Création de la variable var5, concaténation de 
# toutes les autres variables de la table df5
df5 <- within(df5, {
  var8 <- paste0(var1, var2, var3, var4)
})
df5[, c("var7", "var8")]
  ##     var7   var8
  ## 1   a012   a012
  ## 2   b148   b148
  ## 3  c0918  c0918
  ## 4 d11632 d11632

Le premier argument de within() est le nom du data.frame sur lequel porte le recodage, le second est la série d’instructions à appliquer (les accolades sont obligatoires s’il y a plus d’une instruction).


 

 

Cas pratique 3.2 Recoder des variables

Ce cas pratique vise à appliquer les opérations de création et de modification de variables présentées dans cette partie à des données statistiques classiques. Comme le précédent, il porte sur les données de l’enquête Emploi en continu au 2015T4.

  1. La variable cse code la Profession et catégorie socioprofessionnelle (PCS) des individus en 42 postes (cf. cette page pour plus de détails). On souhaite créer la variable agrégée cs qui ne conserve que la première position de la nomenclature.

    1. Proposez une première méthode (un peu fastidieuse) s’appuyant sur des recodages manuels avec l’opérateur [ .

       

    2. Effectuez le même recodage en utilisant la fonction substr().

       

  2. La variable de position sur le marché du travail (acteu) comporte des valeurs manquantes dans le fichier eec à votre disposition. On souhaite imputer cette variable de façon déterministe :

    • si la personne est âgée de moins de 67 ans, on considère qu’elle est active occupée (acteu vaut "1") ;
    • si la personne est âgée de 67 ans ou plus, on considère qu’elle est inactive (acteu vaut "3").

    Remarque Le fichier original de l’enquête Emploi en continu ne comporte aucune valeur manquante pour la variable acteu, celles-ci ont été ajoutées pour l’exercice.


    1. Utilisez la fonction table() pour affichez le nombre de valeurs NA dans la variable acteu. Créez la table eec_pb ne comportant que les individus pour lesquels la variable acteu vaut NA.

       

       

    2. Dans la table eec, créez la variable redressée acteu_red en mettant en oeuvre la procédure d’imputation (très frustre) décrite ci-dessus.

       

    3. Recréez la table eec_pb et contrôlez que l’imputation s’est déroulée correctement (en vérifiant que les valeurs imputées sont cohérentes avec l’âge des individus).

       

  3. Le vecteur de poids de l’enquête (variable extri1613) présente des valeurs extrêmes relativement élevées. Afin d’éviter que les estimations ne soient trop affectées par quelques individus atypiques, on souhaite limiter le poids des individus en les “rabotant” à la valeur du 99ème percentile.

    1. Utilisez la fonction quantile() pour calculer le 99ème percentile de la distribution de extri1613.

       

    2. Récupérer la valeur du 99ème percentile et utilisez-la pour créer une nouvelle pondération (newpond) dans laquelle les poids ont été “rabotés” à son niveau.

       

Modifier la structure d’un data.frame

Comme pour les vecteurs ou les matrices, plusieurs opérations permettent de modifier la structure d’un data.frame :

  • trier un data.frame avec order() : contrairement aux vecteurs, il n’est pas possible d’utiliser la fonction sort() pour trier un data.frame. En revanche, la fonction order() renvoie la permutation permettant de trier une table selon une ou plusieurs variables.
# Création de la table df6
df6 <- data.frame(
  var1 = letters[c(3, 4, 2, 5, 1, 5, 2, 4, 3, 1)]
  , var2 = rnorm(10))
df6
  ##    var1       var2
  ## 1     c -0.6264538
  ## 2     d  0.1836433
  ## 3     b -0.8356286
  ## 4     e  1.5952808
  ## 5     a  0.3295078
  ## 6     e -0.8204684
  ## 7     b  0.4874291
  ## 8     d  0.7383247
  ## 9     c  0.5757814
  ## 10    a -0.3053884

# Tri selon la variable var1
# - Etape 1 : obtention de la permutation correspondante
order(df6$var1)
  ##  [1]  5 10  3  7  1  9  2  8  4  6
# Utilisée sur le vecteur df6$var1, cette permutation
# renvoie un vecteur trié
df6$var1
  ##  [1] "c" "d" "b" "e" "a" "e" "b" "d" "c" "a"
order(df6$var1)
  ##  [1]  5 10  3  7  1  9  2  8  4  6
df6$var1[order(df6$var1)]
  ##  [1] "a" "a" "b" "b" "c" "c" "d" "d" "e" "e"

# - Etape 2 : utilisation de la permutation pour trier df6
df6[order(df6$var1), ]
  ##    var1       var2
  ## 5     a  0.3295078
  ## 10    a -0.3053884
  ## 3     b -0.8356286
  ## 7     b  0.4874291
  ## 1     c -0.6264538
  ## 9     c  0.5757814
  ## 2     d  0.1836433
  ## 8     d  0.7383247
  ## 4     e  1.5952808
  ## 6     e -0.8204684

# Tri selon la variable var1 puis la variable var2
# - Etape 1 : obtention de la permutation correspondante
order(df6$var1, df6$var2)
  ##  [1] 10  5  3  7  1  9  2  8  6  4
# - Etape 2 : utilisation de la permutation pour trier
df6[order(df6$var1, df6$var2), ]
  ##    var1       var2
  ## 10    a -0.3053884
  ## 5     a  0.3295078
  ## 3     b -0.8356286
  ## 7     b  0.4874291
  ## 1     c -0.6264538
  ## 9     c  0.5757814
  ## 2     d  0.1836433
  ## 8     d  0.7383247
  ## 6     e -0.8204684
  ## 4     e  1.5952808

# Tri selon la variable var1 puis les valeurs décroissantes
# de var2
df6 <- df6[order(df6$var1, - df6$var2), ]
df6
  ##    var1       var2
  ## 5     a  0.3295078
  ## 10