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    a -0.3053884
  ## 7     b  0.4874291
  ## 3     b -0.8356286
  ## 9     c  0.5757814
  ## 1     c -0.6264538
  ## 8     d  0.7383247
  ## 2     d  0.1836433
  ## 4     e  1.5952808
  ## 6     e -0.8204684

 

  • ne sélectionner que les valeurs distinctes pour certaines variables avec unique() : la fonction unique() utilisée sur les vecteurs est également applicable aux data.frame.
# Ajout de la variable var3
df6$var3 <- rep(1:2, each = 5)
df6
  ##    var1       var2 var3
  ## 5     a  0.3295078    1
  ## 10    a -0.3053884    1
  ## 7     b  0.4874291    1
  ## 3     b -0.8356286    1
  ## 9     c  0.5757814    1
  ## 1     c -0.6264538    2
  ## 8     d  0.7383247    2
  ## 2     d  0.1836433    2
  ## 4     e  1.5952808    2
  ## 6     e -0.8204684    2

# Sélection de toutes les valeurs distinctes de var1 et var3
unique(df6[,c("var1", "var3")])
  ##   var1 var3
  ## 5    a    1
  ## 7    b    1
  ## 9    c    1
  ## 1    c    2
  ## 8    d    2
  ## 4    e    2

 

  • ajouter des lignes ou des colonnes à un data.frame avec les fonctions cbind() et rbind().
# Création du data.frame df7
df7 <- data.frame(
  var1 = c("f","f")
  , var2 = rnorm(2)
  , var3 = 3
)
df7
  ##   var1      var2 var3
  ## 1    f 1.5117812    3
  ## 2    f 0.3898432    3

# Création du data.frame df8 par concaténation des lignes 
# de df6 et de df7
df8 <- rbind(df6, df7)
df8
  ##    var1       var2 var3
  ## 5     a  0.3295078    1
  ## 10    a -0.3053884    1
  ## 7     b  0.4874291    1
  ## 3     b -0.8356286    1
  ## 9     c  0.5757814    1
  ## 1     c -0.6264538    2
  ## 8     d  0.7383247    2
  ## 2     d  0.1836433    2
  ## 4     e  1.5952808    2
  ## 6     e -0.8204684    2
  ## 11    f  1.5117812    3
  ## 21    f  0.3898432    3

# Note : il faut que les deux data.frame aient exactement
# les mêmes variables avec le même nom pour que cela fonctionne
rbind(df6, df7[, c("var1", "var3")])
  ## Error in rbind(deparse.level, ...): les nombres de colonnes des arguments ne correspondent pas

 

  • fusionner des données sur la base d’un identifiant : la fonction merge() permet de fusionner deux data.frame (pas plus) sur la base d’un identifiant. À noter que les tables n’ont pas besoin d’être triées au préalable.
# Création du data.frame df9
df9 <- data.frame(
  var3 = 2:4
  , var4 = c(TRUE, FALSE, TRUE)
)
df9
  ##   var3  var4
  ## 1    2  TRUE
  ## 2    3 FALSE
  ## 3    4  TRUE

# Fusion de df8 et de df9 selon la variable var3
merge(df8, df9, by = "var3")
  ##   var3 var1       var2  var4
  ## 1    2    c -0.6264538  TRUE
  ## 2    2    d  0.7383247  TRUE
  ## 3    2    d  0.1836433  TRUE
  ## 4    2    e  1.5952808  TRUE
  ## 5    2    e -0.8204684  TRUE
  ## 6    3    f  1.5117812 FALSE
  ## 7    3    f  0.3898432 FALSE
# Par défaut, merge() se restreint aux valeurs communes aux deux tables.

# Conservation de toutes les observations de df8 avec all.x = TRUE
merge(df8, df9, by = "var3", all.x = TRUE)
  ##    var3 var1       var2  var4
  ## 1     1    a  0.3295078    NA
  ## 2     1    a -0.3053884    NA
  ## 3     1    b  0.4874291    NA
  ## 4     1    b -0.8356286    NA
  ## 5     1    c  0.5757814    NA
  ## 6     2    c -0.6264538  TRUE
  ## 7     2    d  0.7383247  TRUE
  ## 8     2    d  0.1836433  TRUE
  ## 9     2    e  1.5952808  TRUE
  ## 10    2    e -0.8204684  TRUE
  ## 11    3    f  1.5117812 FALSE
  ## 12    3    f  0.3898432 FALSE

# Conservation de toutes les observations de df9 avec all.y = TRUE
merge(df8, df9, by = "var3", all.y = TRUE)
  ##   var3 var1       var2  var4
  ## 1    2    c -0.6264538  TRUE
  ## 2    2    d  0.7383247  TRUE
  ## 3    2    d  0.1836433  TRUE
  ## 4    2    e  1.5952808  TRUE
  ## 5    2    e -0.8204684  TRUE
  ## 6    3    f  1.5117812 FALSE
  ## 7    3    f  0.3898432 FALSE
  ## 8    4 <NA>         NA  TRUE

À noter qu’il peut y avoir plusieurs variables de fusion et qu’il n’est pas indispensable qu’elles aient le même nom.

# Création du data.frame df10
df10 <- data.frame(
  v1 = c("c","f")
  , v3 = c(2, 3)
  , v5 = c("Rouge", "Bleu")
)
df10
  ##   v1 v3    v5
  ## 1  c  2 Rouge
  ## 2  f  3  Bleu

# Fusion de df8 et de df10
merge(df8, df10, by.x = c("var3", "var1"), by.y = c("v3", "v1"), all = TRUE)
  ##    var3 var1       var2    v5
  ## 1     1    a  0.3295078  <NA>
  ## 2     1    a -0.3053884  <NA>
  ## 3     1    b  0.4874291  <NA>
  ## 4     1    b -0.8356286  <NA>
  ## 5     1    c  0.5757814  <NA>
  ## 6     2    c -0.6264538 Rouge
  ## 7     2    d  0.7383247  <NA>
  ## 8     2    d  0.1836433  <NA>
  ## 9     2    e -0.8204684  <NA>
  ## 10    2    e  1.5952808  <NA>
  ## 11    3    f  1.5117812  Bleu
  ## 12    3    f  0.3898432  Bleu

 

Cas pratique 3.3 Modifier la structure de données statistiques

  1. À partir de la table eec, on souhaite produire une nouvelle table (eec5) qui ne comporte qu’un individu par ménage, le plus âgé. Les ménages sont identifiés par la variable ident et la variable age code l’âge des individus.

    1. Comment détermineriez-vous le nombre de ménages dans la table eec ?

       

       

    2. On cherche d’abord à constituer une table ne comportant qu’un seul individu par ménage, quel que soit son âge. Comment procéderiez-vous ?

       

       

    3. Comment adapteriez-vous la réponse à la question précédente pour sélectionner l’individu le plus âgé du ménage (sans chercher à maîtriser celui qui est sélectionné quand plusieurs membres d’un même ménage ont le même âge) ?

       

       

    4. (Optionnel) Comment adapteriez-vous la réponse à la question précédente pour effectuer un tirage au sort quand plusieurs membres d’un même ménage ont le même âge ?

       

       

  2. Retour sur les PCS. Entre le niveau de la variable cse (niveau 3) et le niveau le plus agrégé de la variable cs créée dans le cas pratique 3.1 (niveau 1), il existe un niveau intermédiaire (niveau 2). La correspondance entre le niveau 3 et le niveau 2 n’est pas directe, et en règle générale on utilise la table de passage pcs2003_c_n4_n1.dbf (téléchargée depuis le site de l’Insee) pour la réaliser.

    1. Utilisez le package foreign et la fonction read.dbf pour importer cette table dans R. La nomenclature comporte quatre niveaux, mais le quatrième (variable N4) ne nous intéresse pas : agrégez la table de façon à ne conserver que les valeurs distinctes pour les niveaux 2 et 3 de la nomenclature.

       

       

    2. Utilisez la fonction merge() pour fusionner cette table de passage avec le fichier eec et créer une nouvelle table (eec6) contenant une variable supplémentaire correspondant au niveau 2 de la PCS.

       

       

    3. (Difficile) À partir de la table de passage agrégée à la sous-question i., créez le vecteur n2 dont les éléments sont les valeurs de la variable N2 et dont les noms sont les valeurs de la variable N3. Comment pourriez-vous utiliser ce vecteur pour obtenir le même résultat qu’à la question ii. ?

       

       

Effectuer des calculs sur un data.frame

La proximité des data.frame avec les listes se retrouve dans le type d’opérations qu’il est possible de leur appliquer : comme avec les listes, il est possible d’utiliser les fonctions lapply() et sapply(), qui s’appliquent colonne par colonne.

df11 <- data.frame(
  var1 = 1:5
  , var2 = 11:15
  , var3 = 21:25
)
df11
  ##   var1 var2 var3
  ## 1    1   11   21
  ## 2    2   12   22
  ## 3    3   13   23
  ## 4    4   14   24
  ## 5    5   15   25
  
# lapply(), sapply() : comme une liste
lapply(df11, sum)
  ## $var1
  ## [1] 15
  ## 
  ## $var2
  ## [1] 65
  ## 
  ## $var3
  ## [1] 115
sapply(df11, mean)
  ## var1 var2 var3 
  ##    3   13   23

 

Une des opérations les plus utiles consiste à appliquer une même fonction à des groupes d’observations définis par les modalités d’une autre variables (comme avec une instruction BY dans SAS).

Exemple Âge moyen par région, salaire moyen par sexe, etc.

 

Plusieurs fonctions de R permettent de mener à bien ce type d’opération :

  • la fonction aggregate() ;

    df6
      ##    var1       var2 var3
      ## 5     a  0.3295078    1
      ## 10    a -0.3053884    1
      ## 7     b  0.4874291    1
      ## 3     b -0.8356286    1
      ## 9     c  0.5757814    1
      ## 1     c -0.6264538    2
      ## 8     d  0.7383247    2
      ## 2     d  0.1836433    2
      ## 4     e  1.5952808    2
      ## 6     e -0.8204684    2
    # On souhaite calculer la moyenne de var2
    # selon les modalités de var3
    
    aggregate(df6$var2, list(df6$var1), mean)
      ##   Group.1           x
      ## 1       a  0.01205969
      ## 2       b -0.17409978
      ## 3       c -0.02533623
      ## 4       d  0.46098401
      ## 5       e  0.38740621
  • la fonction tapply() ;

    tapply(df6$var2, df6$var1, mean)
      ##           a           b           c           d           e 
      ##  0.01205969 -0.17409978 -0.02533623  0.46098401  0.38740621
  • la fonction split() combinée à un lapply() ou un sapply().

    # La fonction split(x, f) "éclate" le data.frame x en une 
    # liste de data.frame selon les modalités du factor f
    split(df6, df6$var1)
      ## $a
      ##    var1       var2 var3
      ## 5     a  0.3295078    1
      ## 10    a -0.3053884    1
      ## 
      ## $b
      ##   var1       var2 var3
      ## 7    b  0.4874291    1
      ## 3    b -0.8356286    1
      ## 
      ## $c
      ##   var1       var2 var3
      ## 9    c  0.5757814    1
      ## 1    c -0.6264538    2
      ## 
      ## $d
      ##   var1      var2 var3
      ## 8    d 0.7383247    2
      ## 2    d 0.1836433    2
      ## 
      ## $e
      ##   var1       var2 var3
      ## 4    e  1.5952808    2
      ## 6    e -0.8204684    2
    
    # Il ne reste alors plus qu'à appliquer
    # à chaque élément de la liste ainsi produite 
    # la fonction souhaitée par le biais d'un sapply()
    sapply(split(df6, df6$var1), function(x) mean(x$var2))
      ##           a           b           c           d           e 
      ##  0.01205969 -0.17409978 -0.02533623  0.46098401  0.38740621

Remarques

  1. Les performances de ces méthodes diffèrent sensiblement :
# Installation et chargement de la bibliothèque de test 
# de performance microbenchmark
# install.packages("microbenchmark")
library(microbenchmark)

# Compararison des trois méthodes + variante optimisée de sapply()
microbenchmark(times = 1000
  , aggregate = aggregate(df6$var2, list(df6$var1), mean)
  , sapply = sapply(split(df6, df6$var1), function(x) mean(x$var2))
  , tapply = tapply(df6$var2, df6$var1, mean)
  , sapply2 = sapply(split(df6$var2, df6$var1), mean)
)
  ## Unit: microseconds
  ##       expr     min       lq      mean    median        uq       max neval
  ##  aggregate 804.059 954.1130 1273.1944 1054.7315 1312.2340  7488.553  1000
  ##     sapply 385.801 461.3605  630.4129  514.8380  619.4915  5815.864  1000
  ##     tapply 142.777 180.1195  277.9384  207.6895  257.7220 17500.783  1000
  ##    sapply2 114.882 143.5145  205.5141  164.6825  201.5695  3096.067  1000
  1. Le package sqldf permet d’utiliser le langage SQL dans R (à l’image de la PROC SQL dans SAS), aussi bien pour des agrégations (par groupe notamment) que pour des fusions.

 

Cas pratique 3.4 Effectuer des manipulations complexes sur des données statistiques

  1. On souhaite calculer le taux de chômage au niveau national et régional. Le taux de chômage est défini par le ratio du nombre total d’individus au chômage (acteu %in% "2") sur la taille de la population active (acteu %in% c("1", "2")).


    Remarque Le fichier utilisé ici ne comporte que les logements en première ou sixième interrogation : les estimations effectuées dans ce cas pratique n’ont donc aucune raison de coïncider avec les estimations officielles (qui par ailleurs sont CVS-CJO).


    1. Calculez le taux de chômage national, d’abord non-pondéré puis pondéré par la variable extri1613.

       

    2. Utilisez les fonctions aggregate(), tapply() et sapply() (avec split() dans le dernier cas) pour calculer un taux de chômage non-pondéré et par région (variable reg).

       

    3. Utilisez la fonction sapply() avec split() pour calculer un taux de chômage pondéré et par région.

       

  2. Pour des raisons de stockage et de performances, on souhaite optimiser le type des variables de l’objet eec. En effet, R manipule beaucoup plus efficacement les variables de type numérique que les variables de type caractère.

    1. Déterminer sous la forme d’un vecteur logique quelles variables de l’objet eec sont de type caractère.

       

       

    2. Créez la table eec7 dans lequel toutes les variables de type caractère de eec à l’exception de ident et noi sont converties en variables de type numérique.

       

    3. Pour chaque variable numérique de eec7, testez si la conversion en nombre entier (grâce à la fonction as.integer()) est sans perte. Quand c’est le cas, convertissez la variable en nombre entier.

       

 

Calculer des statistiques descriptives

La plupart des fonctions permettant de calculer des statistiques descriptives ont été présentées tout au long de la formation : table(), summary(), etc. Cette partie revient sur l’utilisation de ces fonctions dans une perspective proprement statistique, en élargissant leur utilisation au cas des données pondérées.

L’ensemble des éléments introduits dans cette partie sont mis en pratique sur les données de l’enquête Pisa 2012 (cf. dernière sous-partie).

Variables qualitatives

La fonction table() calcule les fréquences (non-pondérées) des modalités ou des croisements de modalités d’une ou plusieurs variables qualitatives.

# Fréquences des modalités de la variable pub3fp
# Signification des modalités : 
# 1 : Fonction publique d'Etat
# 2 : Fonction publique territoriale
# 3 : Fonction publique hospitalitère
# 4 : Secteur privé
table(eec$pub3fp)
  ## 
  ##     1     2     3     4 
  ##  1415  1246   757 11701

# Utilisation de l'argument useNA pour afficher les valeurs manquantes
table(eec$pub3fp, useNA = "always")
  ## 
  ##     1     2     3     4  <NA> 
  ##  1415  1246   757 11701 19794

# Croisement avec le sexe
table(eec$pub3fp, eec$sexe, useNA = "always")
  ##       
  ##            1     2  <NA>
  ##   1      633   782     0
  ##   2      452   794     0
  ##   3      164   593     0
  ##   4     6234  5467     0
  ##   <NA>  9099 10695     0

Pour améliorer l’affichage des résultats de la fonction table(), le plus simple est de transformer les variables caractères utilisées en facteurs, au préalable ou directement dans la fonction table().

# Transformation de pub3fp en factor
eec$pub3fp <- factor(eec$pub3fp, labels = c(
  "Fonction publique d'Etat"
  , "Fonction publique territoriale"
  , "Fonction publique hospitalière"
  , "Secteur privé"
))

# Impact sur l'affichage de table()
table(eec$pub3fp, eec$sexe, useNA = "always")
  ##                                 
  ##                                      1     2  <NA>
  ##   Fonction publique d'Etat         633   782     0
  ##   Fonction publique territoriale   452   794     0
  ##   Fonction publique hospitalière   164   593     0
  ##   Secteur privé                   6234  5467     0
  ##   <NA>                            9099 10695     0

# Tranformation à la volée de eec$sexe en factor
table(eec$pub3fp, factor(eec$sexe, labels = c("Homme","Femme")), useNA = "always")
  ##                                 
  ##                                  Homme Femme  <NA>
  ##   Fonction publique d'Etat         633   782     0
  ##   Fonction publique territoriale   452   794     0
  ##   Fonction publique hospitalière   164   593     0
  ##   Secteur privé                   6234  5467     0
  ##   <NA>                            9099 10695     0

 

Les fonctions addmargins() et prop.table() permettent d’ajouter les marges et de calculer des pourcentages respectivement.

t <- table(eec$pub3fp, eec$sexe, useNA = "always")

# Ajout de marges avec la fonction addmargins()
addmargins(t)
  ##                                 
  ##                                      1     2  <NA>   Sum
  ##   Fonction publique d'Etat         633   782     0  1415
  ##   Fonction publique territoriale   452   794     0  1246
  ##   Fonction publique hospitalière   164   593     0   757
  ##   Secteur privé                   6234  5467     0 11701
  ##   <NA>                            9099 10695     0 19794
  ##   Sum                            16582 18331     0 34913

# Calcul de pourcentages
prop.table(t) # Pourcentages de cellule
  ##                                 
  ##                                            1           2        <NA>
  ##   Fonction publique d'Etat       0.018130782 0.022398533 0.000000000
  ##   Fonction publique territoriale 0.012946467 0.022742245 0.000000000
  ##   Fonction publique hospitalière 0.004697391 0.016985077 0.000000000
  ##   Secteur privé                  0.178558130 0.156589236 0.000000000
  ##   <NA>                           0.260619254 0.306332885 0.000000000
prop.table(t, 1) # Pourcentages en ligne
  ##                                 
  ##                                          1         2      <NA>
  ##   Fonction publique d'Etat       0.4473498 0.5526502 0.0000000
  ##   Fonction publique territoriale 0.3627608 0.6372392 0.0000000
  ##   Fonction publique hospitalière 0.2166446 0.7833554 0.0000000
  ##   Secteur privé                  0.5327750 0.4672250 0.0000000
  ##   <NA>                           0.4596848 0.5403152 0.0000000
prop.table(t, 2) # Pourcentages en colonne
  ##                                 
  ##                                            1           2 <NA>
  ##   Fonction publique d'Etat       0.038173924 0.042659975     
  ##   Fonction publique territoriale 0.027258473 0.043314604     
  ##   Fonction publique hospitalière 0.009890242 0.032349572     
  ##   Secteur privé                  0.375949825 0.298237958     
  ##   <NA>                           0.548727536 0.583437892

 

La fonction chisq.test() mène le test d’indépendance du \(\chi^2\).

# Test du chi2 sur le lien entre eec$pub3fp et eec$sexe
chisq.test(eec$pub3fp, eec$sexe)
  ## 
  ##    Pearson's Chi-squared test
  ## 
  ## data:  eec$pub3fp and eec$sexe
  ## X-squared = 401.45, df = 3, p-value < 2.2e-16

 


 

Au-delà de ces fonctions natives, le package descr facilite considérablement l’analyse uni- et bivariée de variables qualitatives, en particulier quand les données ont à être pondérées (données d’enquête).

# Installation du package descr
# install.packages("descr")

# Chargement du package descr
library(descr)

La fonction freq() présente les résultats d’un tri à plat de façon plus complète et plus naturelle et son argument w permet de pondérer les calculs.

# Tri à plat non-pondéré sur la variable eec$pub3fp
freq(eec$pub3fp)
  ## eec$pub3fp 
  ##                                Frequency Percent Valid Percent
  ## Fonction publique d'Etat            1415   4.053         9.359
  ## Fonction publique territoriale      1246   3.569         8.241
  ## Fonction publique hospitalière       757   2.168         5.007
  ## Secteur privé                      11701  33.515        77.393
  ## NA's                               19794  56.695              
  ## Total                              34913 100.000       100.000
# Tri à plat pondéré sur la variable eec$pub3fp
freq(eec$pub3fp, w = eec$extri1613)
  ## eec$pub3fp 
  ##                                Frequency Percent Valid Percent
  ## Fonction publique d'Etat         2148336   4.254         9.453
  ## Fonction publique territoriale   1816318   3.596         7.992
  ## Fonction publique hospitalière   1095084   2.168         4.819
  ## Secteur privé                   17666632  34.981        77.736
  ## NA's                            27777229  55.000              
  ## Total                           50503600 100.000       100.000

De même, la fonction crosstab() simplifie l’interprétation d’un tri croisé et l’utilisation de pondérations.

# Tri croisé non-pondéré des variables eec$pub3fp et eec$sexe
crosstab(eec$pub3fp, eec$sexe)
  ##    Cell Contents 
  ## |-------------------------|
  ## |                   Count | 
  ## |-------------------------|
  ## 
  ## =====================================================
  ##                                   eec$sexe
  ## eec$pub3fp                           1      2   Total
  ## -----------------------------------------------------
  ## Fonction publique d'Etat           633    782    1415
  ## -----------------------------------------------------
  ## Fonction publique territoriale     452    794    1246
  ## -----------------------------------------------------
  ## Fonction publique hospitalière     164    593     757
  ## -----------------------------------------------------
  ## Secteur privé                     6234   5467   1.17e+04
  ## -----------------------------------------------------
  ## Total                             7483   7636   1.512e+04
  ## =====================================================

# Tri croisé pondéré des variables eec$pub3fp et eec$sexe
crosstab(eec$pub3fp, eec$sexe, w = eec$extri1613)
  ##    Cell Contents 
  ## |-------------------------|
  ## |                   Count | 
  ## |-------------------------|
  ## 
  ## ================================================================
  ##                                   eec$sexe
  ## eec$pub3fp                               1          2      Total
  ## ----------------------------------------------------------------
  ## Fonction publique d'Etat          9.712e+05   1.177e+06   2.148e+06
  ## ----------------------------------------------------------------
  ## Fonction publique territoriale    6.663e+05   1.15e+06   1.816e+06
  ## ----------------------------------------------------------------
  ## Fonction publique hospitalière    2.426e+05   8.525e+05   1.095e+06
  ## ----------------------------------------------------------------
  ## Secteur privé                     9.539e+06   8.127e+06   1.767e+07
  ## ----------------------------------------------------------------
  ## Total                             1.142e+07   1.131e+07   2.273e+07
  ## ================================================================

# Ajout des pourcentages en ligne et en colonne
crosstab(eec$pub3fp, eec$sexe, w = eec$extri1613, prop.r = TRUE, prop.c = TRUE)
  ##    Cell Contents 
  ## |-------------------------|
  ## |                   Count | 
  ## |             Row Percent | 
  ## |          Column Percent | 
  ## |-------------------------|
  ## 
  ## ================================================================
  ##                                   eec$sexe
  ## eec$pub3fp                               1          2      Total
  ## ----------------------------------------------------------------
  ## Fonction publique d'Etat           971236    1177100    2148336 
  ##                                      45.2%      54.8%       9.5%
  ##                                       8.5%      10.4%           
  ## ----------------------------------------------------------------
  ## Fonction publique territoriale     666324    1149994    1816318 
  ##                                      36.7%      63.3%       8.0%
  ##                                       5.8%      10.2%           
  ## ----------------------------------------------------------------
  ## Fonction publique hospitalière     242581     852503    1095084 
  ##                                      22.2%      77.8%       4.8%
  ##                                       2.1%       7.5%           
  ## ----------------------------------------------------------------
  ## Secteur privé                     9539319    8127313   17666632 
  ##                                      54.0%      46.0%      77.7%
  ##                                      83.5%      71.9%           
  ## ----------------------------------------------------------------
  ## Total                            11419460   11306910   22726370 
  ##                                      50.2%      49.8%           
  ## ================================================================

# Test du chi2
crosstab(
  eec$pub3fp, eec$sexe, w = eec$extri1613 / mean(eec$extri1613)
  , prop.chisq = TRUE,  chisq = TRUE
)
  ##    Cell Contents 
  ## |-------------------------|
  ## |                   Count | 
  ## | Chi-square contribution | 
  ## |-------------------------|
  ## 
  ## ===========================================================
  ##                                   eec$sexe
  ## eec$pub3fp                              1         2   Total
  ## -----------------------------------------------------------
  ## Fonction publique d'Etat              671       814    1485
  ##                                     7.585     7.662        
  ## -----------------------------------------------------------
  ## Fonction publique territoriale        461       795    1256
  ##                                    45.874    46.338        
  ## -----------------------------------------------------------
  ## Fonction publique hospitalière        168       589     757
  ##                                   118.598   119.797        
  ## -----------------------------------------------------------
  ## Secteur privé                        6595      5618   12213
  ##                                    34.148    34.494        
  ## -----------------------------------------------------------
  ## Total                                7895      7816   15711
  ## ===========================================================
  ## 
  ## Statistics for All Table Factors
  ## 
  ## Pearson's Chi-squared test 
  ## ------------------------------------------------------------
  ## Chi^2 = 414.4949      d.f. = 3      p <2e-16 
  ## 
  ##         Minimum expected frequency: 376.5968

Variables quantitatives

Contrairement à d’autres logiciels statistiques (SAS tout particulièrement), R ne possède pas une procédure permettant de calculer automatiquement l’ensemble des statistiques descriptives standards dans le cas d’une variable de nature quantitative, mais un ensemble de fonctions élémentaires (cf. tableau).

Code R Résultat
sum(x) Somme de x
mean(x) Moyenne de x
var(x) Variance empirique de x
sd(x) Écart-type empirique de x
quantile(x) Quantiles de x
summary(x) Moyenne et quantiles de x
max(x) Valeur maximum de x
min(x) Valeur minimum de x
range(x) Valeur minimale et valeur maximale de x
cor.test(x, y) Corrélation entre x et y

En présence de valeurs manquantes (NA), la plupart de ces fonctions renvoient la valeur NA : l’argument na.rm = TRUE permet de modifier ce comportement.

# Statistiques descriptives standards sur le salaire dans l'EEC

mean(eec$salred) 
  ## [1] NA
# Il y a manifestement des valeurs manquantes
sum(is.na(eec$salred))
  ## [1] 19794
# Les valeurs manquantes correspondent à 19 794 observations 
# sur 34 913, ce qui est logique : ni les inactifs ni les non-
# salariés ne touchent de salaire. 

mean(eec$salred, na.rm = TRUE)
  ## [1] 1819.209
sd(eec$salred, na.rm = TRUE)
  ## [1] 1195.574
quantile(eec$salred, na.rm = TRUE)
  ##    0%   25%   50%   75%  100% 
  ##    24  1219  1600  2158 30042
quantile(eec$salred, na.rm = TRUE, probs = c(0.01, 0.05, 0.95, 0.99))
  ##      1%      5%     95%     99% 
  ##  180.00  500.00 3683.00 6113.94
range(eec$salred, na.rm = TRUE)
  ## [1]    24 30042

# Coefficients de corrélation
cor.test(eec$salred, as.numeric(eec$age), method = "pearson")
  ## 
  ##    Pearson's product-moment correlation
  ## 
  ## data:  eec$salred and as.numeric(eec$age)
  ## t = 21.844, df = 15117, p-value < 2.2e-16
  ## alternative hypothesis: true correlation is not equal to 0
  ## 95 percent confidence interval:
  ##  0.1594280 0.1903331
  ## sample estimates:
  ##       cor 
  ## 0.1749237
cor.test(eec$salred, as.numeric(eec$age), method = "spearman")
  ## Warning in cor.test.default(eec$salred, as.numeric(eec$age), method =
  ## "spearman"): Cannot compute exact p-value with ties
  ## 
  ##    Spearman's rank correlation rho
  ## 
  ## data:  eec$salred and as.numeric(eec$age)
  ## S = 464970000000, p-value < 2.2e-16
  ## alternative hypothesis: true rho is not equal to 0
  ## sample estimates:
  ##       rho 
  ## 0.1927504
cor.test(eec$salred, as.numeric(eec$age), method = "kendall")
  ## 
  ##    Kendall's rank correlation tau
  ## 
  ## data:  eec$salred and as.numeric(eec$age)
  ## z = 24.949, p-value < 2.2e-16
  ## alternative hypothesis: true tau is not equal to 0
  ## sample estimates:
  ##       tau 
  ## 0.1371121

Comme dans le cas des variables qualitatives, par défaut R ne prend pas en charge le calcul de statistiques descriptives pondérées. C’est ce que fait en revanche le package Hmisc, avec la série des fonctions wtd : wtd.mean(), wtd.var(), wtd.quantile() notamment, qui comportent un argument weights.

# Installation du package Hmisc
# install.packages("Hmisc")

# Chargement du package Hmisc
library(Hmisc)

# Statistiques pondérées avec Hmisc
wtd.mean(eec$salred, weights = eec$extri1613)
  ## [1] 1833.879
sqrt(wtd.var(eec$salred, weights = eec$extri1613))
  ## [1] 1224.982
wtd.quantile(eec$salred, weights = eec$extri1613, probs = seq(0, 1, 0.05))
  ##    0%    5%   10%   15%   20%   25%   30%   35%   40%   45%   50%   55% 
  ##    24   507   758  1000  1150  1233  1302  1400  1459  1517  1600  1700 
  ##   60%   65%   70%   75%   80%   85%   90%   95%  100% 
  ##  1800  1900  2000  2164  2332  2578  2984  3695 30042
# Note : les fonctions wtd. du package Hmisc disposent 
# également d'un paramètres na.rm, mais sa valeur est TRUE
# par défaut.

Graphiques

La production de graphiques est relativement simple dans R : dans la plupart des cas, c’est la fonction plot() qu’il convient d’utiliser, qui adapte automatiquement le graphique aux caractéristiques de l’objet représenté. De nombreuses options graphiques (taper ? plot pour en afficher quelques unes) permettent de personnaliser assez finement l’affichage.

 

Pour représenter un nuage de points, il suffit par exemple d’appliquer plot() aux deux variables à représenter.

# On se restreint à une sous-base pour ne pas avoir un
# nuage de points trop dense
eec8 <- eec[which(!is.na(eec$salred))[1:100], ]
eec8$age <- as.numeric(eec8$age)

# Représentation du salaire en fonction de l'âge
plot(eec8$age, eec8$salred)

Plusieurs options de base contrôlent l’affichage des titres et des axes :

  • main : titre principal du graphique ;
  • xlab, ylab : titres des axes ;
  • xlim, ylim : vecteurs de longueur 2 indiquant les limites des axes des abscisses et des ordonnées respectivement.
# Personnalisation du graphique précédent (1)
plot(
  eec8$age, eec8$salred
  , main = "Âge et salaire dans l'EEC 2012 T4"
  , xlab = "Âge", ylab = "Salaire en euros"
  , xlim= c(15, 75)
)

Les options pch et col permettent de modifier la forme et la couleur des points représentés.

# Personnalisation du graphique précédent (2)
plot(
  eec8$age, eec8$salred
  , main = "Âge et salaire dans l'EEC 2012 T4"
  , xlab = "Âge", ylab = "Salaire en euros"
  , xlim= c(15, 75)
  , pch = 0, col = 2
)

Utilisées avec des vecteurs et la fonction legend(), pch et col permettent de représenter le croisement de plusieurs variables.

# Utilisation de pch pour distinguer hommes et femmes 
# sur le graphique
plot(
  eec8$age, eec8$salred
  , main = "Âge et salaire dans l'EEC 2012 T4"
  , xlab = "Âge", ylab = "Salaire en euros"
  , xlim= c(15, 75)
  , pch = as.numeric(eec8$sexe == "2") 
)

# Ajout d'une légende
legend("topright", legend=c("Hommes","Femmes"), pch=c(0, 1))

# Sauvegarde du graphique pour la suite
g1 <- recordPlot()

Les fonctions abline() et curve() ajoutent respectivement des lignes et des courbes à un graphique existant.

# Modèle de régression linéaire : salaire = age + sexe
# (cf. dernière partie)
eec8$femme <- eec8$sexe == "2"
m1 <- lm(salred ~ age + femme, data = eec8)

# Représentation des droites de régression correspondant
# aux hommes et aux femmes respectivement
g1
abline(a = coef(m1)[1], b = coef(m1)[2])
abline(a = coef(m1)[1] + coef(m1)[3], b = coef(m1)[2], lty = 2)

# Modèle de régression linéaire : salaire = age + age^2 + sexe
# (cf. sous-partie suivante)
eec8$age2 <- eec8$age^2
m2 <- lm(salred ~ age + age2 + femme, data = eec8)

# Représentation des courbes de régression correspondant
# aux hommes et aux femmes respectivement
g1
curve(coef(m2)[1] + coef(m2)[2]*x + coef(m2)[3]*x^2, add = TRUE)
curve(coef(m2)[1] + coef(m2)[4] + coef(m2)[2]*x + coef(m2)[3]*x^2, lty=2, add = TRUE)

 

La fonction plot() permet également de représenter la fonction de répartition et la densité empirique d’une distribution, par le biais des fonctions ecdf() et density().

# Fonction de répartition empirique du salaire
plot(
  ecdf(eec$salred)
  , main = "Fonction de répartition empirique du salaire"
)


# Densité empirique du salaire
plot(
  density(eec$salred, na.rm = TRUE)
  , main = "Densité empirique du salaire"
)

 

Au-delà de la fonction plot(), de nombreuses fonctions permettent d’effectuer des représentations spécifiques dans R :

  • hist() produit l’histogramme d’une distribution ;

    # Histogramme du salaire dans l'EEC
    hist(eec$salred, xlim = c(0, 4000), breaks = seq(0, 100000, 250))

  • barplot() et pie() produisent respectivement le diagramme en bâtons et le diagramme circulaire représentant la fréquence d’une variable qualitative.

    # Distribution de la position du marché du travail
    # en milliers
    pos <- by(eec$extri1613, eec$acteu, sum) / 1000
    
    # Diagramme en bâtons de la position sur le marché du travail
    barplot(
      pos
      , names.arg = c("Actifs occupés", "Chômeurs", "Inactifs")
      , main = "Position sur le marché du travail au 2012 T4 \n (en milliers)"
    )

    
    # Diagramme circulaire de la position sur le marché du travail
    pie(
      pos
      , labels = paste0(c("Actifs occupés", "Chômeurs", "Inactifs"), " (", round(pos),")")
      , main = "Position sur le marché du travail au 2012 T4 \n (en milliers)"
    )

Application à l’enquête Pisa 2012

L’enquête Pisa () est une enquête réalisée tous les trois ans par l’Organisation de coopération et de développement économique (OCDE) dans une soixantaine de pays auprès des élèves de 15 ans (quelle que soit leur classe au moment de l’enquête).

Elle vise à mesurer les acquis des élèves de 15 ans dans trois disciplines : mathématiques, compréhension de l’écrit (ou littératie) et sciences. En plus des scores aux tests standardisés de mathématiques, compréhension de l’écrit et sciences, cette enquête comporte de très nombreuses informations sur l’origine sociale des élèves, leurs conditions d’enseignement ainsi que leur rapport aux enseignants et à l’école.

Organisation des fichiers Les fichiers de l’enquête Pisa 2012 et leur documentation sont librement téléchargeables sur le site de l’OCDE. Seuls deux des nombreux fichiers de données qui constituent l’enquête seront utilisés :

  • le fichier élève pisa_stu.sas7bdat;
  • le fichier établissement pisa_sch.sas7bdat.

Ces deux fichiers ont été restreints à la France et à un ensemble réduit de variables:

Fichier élève (pisa_stu.sas7bdat)

Variable Description
cnt Pays
stidstd Identifiant de l’élève
schoolid Identifiant de l’établissement
w_fstuwt Poids de sondage final de l’élève
st01q01 Classe en nombre d’années depuis l’entrée en primaire: la 10\(^{ème}\) classe correspond à la seconde en France.
st04q01 Sexe : (1) Femme (2) Homme
st05q01 A suivi une scolarité pré-primaire (1) Non (2) Oui, un an ou moins (3) Oui, plus d’un an
st07q01 st07q02 st07q03 A redoublé à un moment de sa scolarité : (1) Non (2-3) Oui, une ou plusieurs fois
st08q01 Est arrivé en retard au cours des deux semaines précédant l’enquête
st09q01 A séché les cours au cours des deux semaines précédant l’enquête
anxmat Score synthétique d’anxiété en mathématiques
disclima Score synthétique de climat de discipline dans la classe
escs Indicateur synthétique de statut économique, social et culturel
immig Immigration : (1) Né en France (2) Immigré de deuxième génération (3) Immigré de première génération
hisced Niveau d’étude le plus élevé des parents (nomenclature CITE)
pv1math Score synthétique à l’évaluation de mathématiques
pv1read Score synthétique à l’évaluation de compréhension de l’écrit
pv1scie Score synthétique à l’évaluation de sciences

Fichier établissement (pisa_sch.sas7bdat)

Variable Description
cnt Pays
schoolid Identifiant de l’établissement
senwgt_scq Poids de sondage (la somme vaut 1 000 dans chaque pays)
sc01q01 Statut public ou privé (1) public (2) privé
sc03q01 Taille de la commune de l’établissement : (1) Village (2) Small town (3) Town (4) City (5) Large city
sc05q01 Taille de la classe en cours de français : (01) 15 ou moins (02) 16-20 (03) 21-25 … (08) 46-50 (09) Plus de 50 élèves

 

Cas pratique 3.5 Application à l’enquête Pisa 2012 : Importation et mise en forme des données

  1. Utilisez la fonction read_sas() du package haven (cf. module 1) pour importer les deux fichiers dans les objets stu et sch respectivement. Afin de faciliter les exploitations futures, passez leurs noms de variables en minuscules.

     

  2. On souhaite pouvoir utiliser les informations au niveau de l’établissement dans des exploitations au niveau des élèves. Pour ce faire, il convient de fusionner les tables stu et sch sur la base de la variable schoolid.

    1. Utilisez les fonctions unique(), intersect() et setdiff() pour vérifier que l’identifiant schoolid prend bien les mêmes valeurs dans les deux tables.

       

    2. Vérifiez que la variable schoolid est un identifiant pour la table sch, à savoir : (1) qu’elle est renseignée pour chaque ligne (2) qu’elle prend une valeur distincte pour chaque ligne.

       

    3. Utilisez la fonction merge() pour fusionner stu et sch par schoolid et créez la table stu2. Vérifiez que ses propriétés sont cohérentes avec le résultat des questions précédentes : même nombre de lignes que stu, nombre de colonnes égal à celui de stu et de sch moins 1.

       

  3. Recodage de variables

    1. Recodez la variable de sexe en facteur dont les libellés sont "Femme" et "Homme" pour faciliter la lecture des tableaux et graphiques.

       

    2. Un élève a redoublé à un moment dans sa scolarité dès lors qu’une des variables st07q01, st07q02 ou st07q03 vaut 2 ou 3. Créez la variable indicatrice redoublant valant TRUE si un élève a redoublé au cours de sa scolarité.

       

    3. Quelle est la nature de l’indicateur synthétique de statut économique, social et culturel ? Recodez-le sous la forme d’une variable qualitative à 5 modalités (en utilisant les fonctions cut() et quantile()).

       

 

Cas pratique 3.6 Application à l’enquête Pisa 2012 : Statistiques descriptives

  1. En utilisant le package descr, effectuez le tri croisé entre sexe des élèves et redoublement et interprétez-le.

     

  2. Calculez le coefficient de corrélation linéaire de Pearson entre notes en mathématiques et en sciences et menez le test de nullité de ce coefficient (avec la fonction cor.test()).

     

  3. Calculez le score moyen en mathématiques selon les quintiles de statut économique, social et culturel (cf. le recodage du cas pratique précédent).

     

 

Cas pratique 3.7 Application à l’enquête Pisa 2012 : Graphiques

  1. Construisez un diagramme en bâton pour illustrer la relation entre statut économique, social et culturel (en quintiles) et redoublement. Utilisez les options de mise en forme pour améliorer sa présentation (ajouter un titre avec main(), modifiez les titres des axes avec xlab() et ylab(), etc.).

     

  2. Utilisez la fonction plot() pour représenter le nuage de points de la relation entre le score en mathématiques et le score en sciences.

     

  3. Construisez la “boîte à moustaches” représentant la relation entre stat au moinst économique, social et culturel (en quintiles) et score en mathématiques à l’aide de la fonction boxplot().

     

  4. Utilisez les fonctionnalités de RStudio (menus déroulants de la fenêtre de graphiques) pour sauvegarder ces graphiques dans la qualité et le format souhaités.

 

Quelques liens pour aller plus loin

Formation R perfectionnement

Le support de la formation R perfectionnement est en ligne à l’adresse : t.slmc.fr/perf. Elle aborde trois sujets :

  1. Outils et méthodes pour se perfectionner avec R ;

  2. Traitements avancés sur des données dans R : retour sur les fonctions *apply() et assimilées, optimisation en base R, packages dplyr et data.table, parallélisation et utilisation de langages de bas niveau dans R.

  3. Graphiques et reporting avec R : package ggplot2, production automatique de documents avec Rmarkdown.

Un cycle de formations perfectionnement est également proposé par la Division Formation : certaines portent sensiblement sur les mêmes sujets, mais pas toutes (notamment une consacrée à R Shiny).

Utiliser des techniques d’analyse de données multidimensionnelles

Le package FactoMineR (attention à la casse !) rend extrêmement simple la mise en oeuvre sous R de techniques d’analyse de données multidimensionnelles : analyse en composante principale (ACP), analyse des correspondances multiples (ACM) ou encore classification ascendante hiérarchique (CAH).

Ce document d’introduction (“vignette” dans la terminologie de R) présente ces méthodes et leur mise en oeuvre avec FactoMineR.

Estimer des modèles de régression

Les fonctions natives de R lm() et glm() permettent respectivement d’estimer des modèles linéaires et linéaires généralisés (dont les modèles logistiques).

Cette page (destinée à un public de non-statisticiens) introduit les méthodes de régression et propose de nombreux exemples d’estimation de modèles dans R.