Dernière mise à jour: 2025-11-28 09:43:

  • enlevé la commande rm.

Pour télécharger et ouvrir ce fichier, vérifiez d’abord d’être dans le bon dossier de travail (working directory), puis copier-coller ces lignes dans la console R:

download.file("https://matteo.gagliolo.web.ulb.be/INFOD203/dplyr.r",
              "dplyr.r")
file.edit("dplyr.r")

Commencez, comme d’habitude, par charger les packages nécessaires pour cette leçon.

library("dplyr")

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
library("questionr")

1 Introduction à dplyr

1.1 À quoi sert dplyr?

Le paquet dplyr est incontournable dans le tidyverse: il permet de manipuler des jeux de données, en extraire des sous-ensembles (retirer des variables, retirer des individus), en faire des résumés, définir des nouvelles variables, fusionner plusieurs jeux de données, etc.

dplyr est composé d’une série de fonctions dites «verbes», chacune dédiée à une manipulation différente: des manipulations plus complexes peuvent être obtenues en enchaînant plusieurs fonctions, de façon que le résultat de l’une soit l’argument de la suivante.

Dans ce fichier on va voir les «verbes» les plus importants et apprendre à les enchaîner.

Notez qu’avant que dplyr existe, on manipulait les données au moyen de l’indexation. C’est un approche qui est toujours valable et nous vous en avons présenté quelques aspects dans l’initiation, mais nous ne nous y attarderons pas cette année, car travailler avec dplyr est généralement plus simple.

1.2 Préparation des données

Création d’un petit jeu de données pour comprendre les principes. Cette fois on va créer une tibble: c’est la version tidyverse du data.frame. Pour le moment on le traite comme un data frame quelconque, on va voir les différences dans la suite.

df <- tibble(ID = 1:5,
             resultats_info = c(15, 17, 9, 13, 12),
             section = c("SHUM", "SOCA", "POLI", "SHUM", "SOCA"))

df
# A tibble: 5 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     1             15 SHUM   
2     2             17 SOCA   
3     3              9 POLI   
4     4             13 SHUM   
5     5             12 SOCA   

Par rapport à un data.frame, le tibble donne des informations supplémentaires: taille et type des colonnes (dans la notation abrégé de str).

Nous utiliserons aussi deux bases de données plus conséquentes.

data("hdv2003")
data("rp2012")

Rappel: Demandez toujours un minimum d’information concernant vos données

class(df) 
[1] "tbl_df"     "tbl"        "data.frame"
str(df)
tibble [5 × 3] (S3: tbl_df/tbl/data.frame)
 $ ID            : int [1:5] 1 2 3 4 5
 $ resultats_info: num [1:5] 15 17 9 13 12
 $ section       : chr [1:5] "SHUM" "SOCA" "POLI" "SHUM" ...

Pour plus d’infos sur hdv2003: France; voir description dans le syllabus.

class(hdv2003)
[1] "data.frame"
str(hdv2003)
'data.frame':   2000 obs. of  20 variables:
 $ id           : int  1 2 3 4 5 6 7 8 9 10 ...
 $ age          : int  28 23 59 34 71 35 60 47 20 28 ...
 $ sexe         : Factor w/ 2 levels "Homme","Femme": 2 2 1 1 2 2 2 1 2 1 ...
 $ nivetud      : Factor w/ 8 levels "N'a jamais fait d'etudes",..: 8 NA 3 8 3 6 3 6 NA 7 ...
 $ poids        : num  2634 9738 3994 5732 4329 ...
 $ occup        : Factor w/ 7 levels "Exerce une profession",..: 1 3 1 1 4 1 6 1 3 1 ...
 $ qualif       : Factor w/ 7 levels "Ouvrier specialise",..: 6 NA 3 3 6 6 2 2 NA 7 ...
 $ freres.soeurs: int  8 2 2 1 0 5 1 5 4 2 ...
 $ clso         : Factor w/ 3 levels "Oui","Non","Ne sait pas": 1 1 2 2 1 2 1 2 1 2 ...
 $ relig        : Factor w/ 6 levels "Pratiquant regulier",..: 4 4 4 3 1 4 3 4 3 2 ...
 $ trav.imp     : Factor w/ 4 levels "Le plus important",..: 4 NA 2 3 NA 1 NA 4 NA 3 ...
 $ trav.satisf  : Factor w/ 3 levels "Satisfaction",..: 2 NA 3 1 NA 3 NA 2 NA 1 ...
 $ hard.rock    : Factor w/ 2 levels "Non","Oui": 1 1 1 1 1 1 1 1 1 1 ...
 $ lecture.bd   : Factor w/ 2 levels "Non","Oui": 1 1 1 1 1 1 1 1 1 1 ...
 $ peche.chasse : Factor w/ 2 levels "Non","Oui": 1 1 1 1 1 1 2 2 1 1 ...
 $ cuisine      : Factor w/ 2 levels "Non","Oui": 2 1 1 2 1 1 2 2 1 1 ...
 $ bricol       : Factor w/ 2 levels "Non","Oui": 1 1 1 2 1 1 1 2 1 1 ...
 $ cinema       : Factor w/ 2 levels "Non","Oui": 1 2 1 2 1 2 1 1 2 2 ...
 $ sport        : Factor w/ 2 levels "Non","Oui": 1 2 2 2 1 2 1 1 1 2 ...
 $ heures.tv    : num  0 1 0 2 3 2 2.9 1 2 2 ...

Pour plus d’infos sur rp2012: la version actuellement affichée dans le syllabus est rp2018, mais on peut encore repérer rp2012 via la waybackmachine (les noms des variables sont les mêmes pour la plus part).

2 Sélectionner des individus: filter

2.1 Le principe de filter

Le verbe filter(data, condition) extrait un sous-ensemble du data frame data, en sélectionnant uniquement les lignes (= individus) qui respectent la condition.

La condition est une expression à valeur booléenne (TRUE ou FALSE pour chaque ligne), incluant des noms des colonnes (les variables), des relations, et éventuellement des connecteurs logiques si plusieurs conditions doivent être combinées.

Par exemple, si l’on ne veut garder que les étudiants qui ont réussi l’examen d’informatique:

filter(df, resultats_info >= 10)
# A tibble: 4 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     1             15 SHUM   
2     2             17 SOCA   
3     4             13 SHUM   
4     5             12 SOCA   

On peut également combiner deux conditions.

En conjonction (et) — par exemple si l’on ne veut garder que les étudiants qui ont réussi l’examen d’informatique et sont inscrits en SHUM:

filter(df, resultats_info >= 10 & section == "SHUM")
# A tibble: 2 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     1             15 SHUM   
2     4             13 SHUM   

En disjonction (ou) — par exemple si l’on ne veux garder que les étudiants qui sont inscrits en POLI ou en SHUM:

filter(df, section == "POLI" | section == "SHUM") 
# A tibble: 3 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     1             15 SHUM   
2     3              9 POLI   
3     4             13 SHUM   

Rappel: voir la leçon précédente sur la logique et les ensembles avant de continuer si vous êtes largué.e.s.

Remarque: avant l’usage de filter, subset était la fonction qu’on proposait pour filtrer une base de données.

subset(df, resultats_info >= 10 & section == "SHUM")
# A tibble: 2 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     1             15 SHUM   
2     4             13 SHUM   

2.2 Procédure

Voyons maintenant un exemple plus réaliste. Essayons d’extraire un sous-ensemble de hdv2003, ne reprenant que les femmes retraitées:

  1. On commence par afficher les noms des variables pour identifier celles sur lesquelles porterons notre condition.
names(hdv2003)
 [1] "id"            "age"           "sexe"          "nivetud"      
 [5] "poids"         "occup"         "qualif"        "freres.soeurs"
 [9] "clso"          "relig"         "trav.imp"      "trav.satisf"  
[13] "hard.rock"     "lecture.bd"    "peche.chasse"  "cuisine"      
[17] "bricol"        "cinema"        "sport"         "heures.tv"    

Les variables sexe et occup nous seront utiles pour isoler les femmes retraitées.

  1. On affiche ensuite les niveaux des variables sexe et occup.
levels(hdv2003$sexe) 
[1] "Homme" "Femme"
levels(hdv2003$occup)
[1] "Exerce une profession" "Chomeur"               "Etudiant, eleve"      
[4] "Retraite"              "Retire des affaires"   "Au foyer"             
[7] "Autre inactif"        
  1. Les deux niveaux dont on a besoin pour créer notre condition sont Femme (dans sexe) et Retraite (dans occup). On peut donc employer le verbe filter pour créer notre sous-ensemble.
femmes_retraite <- filter(hdv2003, occup == "Retraite" & sexe == "Femme")

Attention: N’écrasez pas vos résultats sur votre base de données originale. Vous remarquez que l’on donne un nouveau nom à notre sous-ensemble: femmes_retraite. Si vous redonnez hdv2003 comme nom à votre sous-ensemble, vous perdrez votre data frame original, ce que l’on ne souhaite pas. En cas de besoin, réévaluez la commande data("hdv2003") pour restaurer les données originales.

  1. Enfin, on termine toujours par vérifier nos résultats: dans ce cas on peut le faire moyennant un tableau de contingence.
table(femmes_retraite$sexe, femmes_retraite$occup)
       
        Exerce une profession Chomeur Etudiant, eleve Retraite
  Homme                     0       0               0        0
  Femme                     0       0               0      184
       
        Retire des affaires Au foyer Autre inactif
  Homme                   0        0             0
  Femme                   0        0             0

Dans les données filtrées, nous n’avons bien que des femmes à la retraite, 184 au total. Comparons avec l’original:

table(hdv2003$sexe, hdv2003$occup)
       
        Exerce une profession Chomeur Etudiant, eleve Retraite
  Homme                   520      54              48      208
  Femme                   529      80              46      184
       
        Retire des affaires Au foyer Autre inactif
  Homme                  39        0            30
  Femme                  38      171            53

Elles étaient bien 184 aussi, elles sont toutes là!

Pour un filtrage aussi simple, on aurait pu tester le résultat avec unique:

unique(femmes_retraite$sexe)
[1] Femme
Levels: Homme Femme
unique(femmes_retraite$occup)
[1] Retraite
7 Levels: Exerce une profession Chomeur Etudiant, eleve ... Autre inactif

mais avec des consignes plus complexes, avec plusieurs niveaux par variable, on doit s’assurer que les conditions sont vérifiées pour chaque ligne de notre résultat: un tableau croisé est donc indispensable.

Attention: Pour combiner plusieurs conditions, il faut faire bien attention aux parenthèses! R est encore moins tolérant aux erreurs de syntaxe que votre prof de Forma, et il ne va pas vous dire quand le résultat est différent de vos attentes, même si il produit un résultat sans erreur apparente dans la console. Il est donc vraiment important de vérifier si le résultat obtenu est conforme à vos attentes/la consigne grâce à un table ou unique.

Exercices.

3 Sélectionner des variables: select

3.1 Le principe de select

Le verbe select(data, variables) permet d’extraire une partie du data frame data, en sélectionnant uniquement les colonnes (= variables) reprise dans la commande.

Par exemple:

Si l’on veut un data frame qui ne reprend plus la section de l’étudiant.

select(df, ID, resultats_info)
# A tibble: 5 × 2
     ID resultats_info
  <int>          <dbl>
1     1             15
2     2             17
3     3              9
4     4             13
5     5             12

Cela fonctionne aussi avec le - pour retirer une variable.

select(df, -section)
# A tibble: 5 × 2
     ID resultats_info
  <int>          <dbl>
1     1             15
2     2             17
3     3              9
4     4             13
5     5             12

Remarque: avant select, on indexait les colonnes grâce aux [], comme dans l’exemple suivant, ou avec la fonction subset.

df[, c("ID", "resultats_info")]
# A tibble: 5 × 2
     ID resultats_info
  <int>          <dbl>
1     1             15
2     2             17
3     3              9
4     4             13
5     5             12

3.2 Procédure

Compliquons un peu avec une base de données plus conséquente. Essayons d’extraire un sous-ensemble de hdv2003, ne reprenant que les 10 premières variables :

  1. On commence par afficher les noms des variables présentes dans le data frame original
names(hdv2003)
 [1] "id"            "age"           "sexe"          "nivetud"      
 [5] "poids"         "occup"         "qualif"        "freres.soeurs"
 [9] "clso"          "relig"         "trav.imp"      "trav.satisf"  
[13] "hard.rock"     "lecture.bd"    "peche.chasse"  "cuisine"      
[17] "bricol"        "cinema"        "sport"         "heures.tv"    
  1. On peut ensuite employer le verbe select.
hdv2003_2 <- select(hdv2003, id, age, sexe, nivetud, poids, occup,
                    qualif, freres.soeurs, clso, relig)

Notez que vous pouvez employez les : (à) pour sélectionner un série de variables qui se suivent. C’est le cas de l’exemple précédent où on l’on sélectionne les 10 premières variables de id à relig.

hdv2003_2 <- select(hdv2003, id:relig)
  1. On vérifie ensuite son résultat.
names(hdv2003_2)
 [1] "id"            "age"           "sexe"          "nivetud"      
 [5] "poids"         "occup"         "qualif"        "freres.soeurs"
 [9] "clso"          "relig"        

Il est possible de combiner : et -.

hdv2003_3 <- select(hdv2003, age:heures.tv, -poids, -(hard.rock:sport))

Attention pour enlever plusieurs variables avec : il faut des parenthèses comme ci-dessus, sinon ça ne marche pas.

Comparez:

names(select(hdv2003, age:heures.tv, -poids, -(hard.rock:sport))) # avec parenthèses (correct)
 [1] "age"           "sexe"          "nivetud"       "occup"        
 [5] "qualif"        "freres.soeurs" "clso"          "relig"        
 [9] "trav.imp"      "trav.satisf"   "heures.tv"    
names(select(hdv2003, age:heures.tv, -poids, -hard.rock:sport)) # sans parenthèses (erreur)
Warning in x:y: numerical expression has 19 elements: only the first used
 [1] "age"           "sexe"          "nivetud"       "occup"        
 [5] "qualif"        "freres.soeurs" "clso"          "relig"        
 [9] "trav.imp"      "trav.satisf"   "hard.rock"     "lecture.bd"   
[13] "peche.chasse"  "cuisine"       "bricol"        "cinema"       
[17] "sport"         "heures.tv"     "id"            "poids"        

Astuce: Employez la fonction everything(). Elle vous permet de modifier l’ordre des variables dans la base de données en sélectionnant toutes celles qui n’ont pas encore été reprises.

Par exemple, dans la commande qui suit, les variables sexe, age et heures.tv sont placées aux trois premières colonnes, suivies par les variables restantes, à l’exclusion de poids:

  1. On affiche les noms:
names(hdv2003) 
 [1] "id"            "age"           "sexe"          "nivetud"      
 [5] "poids"         "occup"         "qualif"        "freres.soeurs"
 [9] "clso"          "relig"         "trav.imp"      "trav.satisf"  
[13] "hard.rock"     "lecture.bd"    "peche.chasse"  "cuisine"      
[17] "bricol"        "cinema"        "sport"         "heures.tv"    
  1. On emploie la fonction select:
hdv2003_4 <- select(hdv2003, sexe, age, heures.tv, everything())
  1. On vérifie le résultat
names(hdv2003_4)
 [1] "sexe"          "age"           "heures.tv"     "id"           
 [5] "nivetud"       "poids"         "occup"         "qualif"       
 [9] "freres.soeurs" "clso"          "relig"         "trav.imp"     
[13] "trav.satisf"   "hard.rock"     "lecture.bd"    "peche.chasse" 
[17] "cuisine"       "bricol"        "cinema"        "sport"        

Remarque Notez que si vous avez exclus des variables avant d’employer la fonction everything, R ira les rechercher: pour combiner everything et - il faut donc enlever après everything().

Comparez:

"poids" %in% names(select(hdv2003, sexe, age, heures.tv, -poids, everything()))
[1] TRUE
"poids" %in% names(select(hdv2003, sexe, age, heures.tv, everything(), -poids))
[1] FALSE

Astuce: Pour éviter les fautes de frappe, vous pouvez utiliser dput pour imprimer les noms des colonnes, puis copiez/collez le résultat de la console au script (en général il faut éviter ça mais dput fait exception à la règle).

C’est possible car select accepte les noms des colonnes encodés directement, ou bien sous forme de vecteur “character”. Par exemple:

dput(names(hdv2003))
c("id", "age", "sexe", "nivetud", "poids", "occup", "qualif", 
"freres.soeurs", "clso", "relig", "trav.imp", "trav.satisf", 
"hard.rock", "lecture.bd", "peche.chasse", "cuisine", "bricol", 
"cinema", "sport", "heures.tv")
hdv2003_dput <- select(hdv2003, 
                       c("id", "age", "nivetud", "trav.imp",
                         "trav.satisf", "heures.tv"))
hdv2003_dput <- select(hdv2003,
                       "id", "age", "nivetud", "trav.imp",
                         "trav.satisf", "heures.tv")
names(hdv2003_dput)
[1] "id"          "age"         "nivetud"     "trav.imp"    "trav.satisf"
[6] "heures.tv"  

Ça marche avec ou sans le c( ), et les variations ci-dessus. Par exemple, ces trois commandes donnent le même résultat:

names(select(hdv2003, age:nivetud, everything(), -poids, -(hard.rock:sport)))
 [1] "age"           "sexe"          "nivetud"       "id"           
 [5] "occup"         "qualif"        "freres.soeurs" "clso"         
 [9] "relig"         "trav.imp"      "trav.satisf"   "heures.tv"    
names(select(hdv2003, "age":"nivetud", everything(), -"poids", -("hard.rock":"sport")))
 [1] "age"           "sexe"          "nivetud"       "id"           
 [5] "occup"         "qualif"        "freres.soeurs" "clso"         
 [9] "relig"         "trav.imp"      "trav.satisf"   "heures.tv"    
names(select(hdv2003, c("age":"nivetud", everything(), -"poids", -("hard.rock":"sport"))))
 [1] "age"           "sexe"          "nivetud"       "id"           
 [5] "occup"         "qualif"        "freres.soeurs" "clso"         
 [9] "relig"         "trav.imp"      "trav.satisf"   "heures.tv"    

Attention: select est une des rares fonctions de dplyr qui permets d’utiliser des "characters" à la place des noms des variables sans "" (rename est une autre). Ne faites pas ça avec filter ou autres!

Exercices.

4 Enchaîner les commandes : %>% (then)

4.1 Le principe du %>%

Jusqu’à présent, vous emboîtiez les commandes les unes dans les autres. Par exemple, si vous souhaitez enchaîner un filter et un select dans une seule commande, cela donne ceci:

df_boite <- select(filter(df, resultats_info >= 10), -section)

Il y a plusieurs inconvenants dans cette notation:

  • Gérer les parenthèses devient compliqué, on ne sait plus très bien quel paramètre se rapporte à quelle fonction.

  • Les commandes se lisent dans l’ordre inverse par rapport à leur exécution, la commande la plus à droite étant la première qui sera exécutée.

  • Quand il y a d’autres fonctions, celles-ci s’éloignent de plus en plus de leurs paramètres à chaque fonction qu’on rajoute.

Une solution pour avoir un code plus lisible est d’utiliser une commande par fonction, en sauvegardant les résultats de celle-ci dans un objet que vous réutilisez progressivement. Reprenons notre exemple précédent:

df2 <- filter(df, resultats_info >= 10)
df3 <- select(df2, -section)

Cette approche permet d’avoir un script plus lisible, et de vérifier les étapes intermédiaires, mais ça multiplie aussi le nombre d’objets dans l’espace de travail.

Une autre alternative qui est souvent évoquée est celle de réécrire dans la même variable à chaque étape, ce qui est une recette pour le désastre assuré!

Une meilleure alternative est l’usage du pipe, provenant du terme pipeline, l’idée étant d’enchaîner les commandes dans l’ordre dans lequel on les pense, et les lit. Notez que l’assignation à un objet se fait dès lors à la fin, et plus au début. On utilisera donc -> plutôt que <-

df %>% filter(resultats_info >= 10) %>% select(-section) -> df_pipe 

On lit le symbole %>% then (donc puis en français). Remarquez aussi que on ne doit plus encoder l’argument df, le %>% s’en occupe! En gros, si on écrit x %>% fun(y), R va lire fun(x, y).

Astuce: Dans RStudio, on peut insérer le %>% avec la combinaison de touches <Ctrl>+<Shift>+<M> (<Cmd>+<Shift>+<M> sous MacOsX). Malheureusement il n’y a pas encore de raccourci pour ->: son usage n’est pas assez répandu.

Malheureusement il n’y a pas encore de raccourci pour ->: son usage n’est pas assez répandu. En effet, le pipe marche aussi avec l’assignation traditionnelle, et même si moins lisible, c’est cette convention que vous allez trouver dans le 99% des exemples en ligne!

L’exemple ci dessous avec <- devient:

df_pipe <- df %>% filter(resultats_info >= 10) %>% select(-section)

Ça marche aussi mais ça fait mal au crâne, donc dans ce cours on préfère combiner %>% avec ->, pour garder une lecture de gauche à droite jusqu’aux résultats.

Pour plus d’infos sur le pipe: %>% est accessible après chargement de chaque paquet du tidyverse, mais il provient plus spécifiquement du package magrittr.

4.2 Procédure

Prenons un exemple un peu plus complexe. Créons un sous-ensemble de la base de données hdv2003 ne contenant que les femmes retraitées ou retirées des affaires, et ne reprenant que les 10 premières variables, sauf le niveau de qualification et la classe sociale, mais contenant aussi leur pratique du sport.

  1. On commence par afficher les variables présentes dans la base de données originale.
dput(names(hdv2003))
c("id", "age", "sexe", "nivetud", "poids", "occup", "qualif", 
"freres.soeurs", "clso", "relig", "trav.imp", "trav.satisf", 
"hard.rock", "lecture.bd", "peche.chasse", "cuisine", "bricol", 
"cinema", "sport", "heures.tv")
  1. On affiche les niveaux qui seront nécessaire à notre condition portant sur le filter.
dput(levels(hdv2003$sexe))
c("Homme", "Femme")
dput(levels(hdv2003$occup))
c("Exerce une profession", "Chomeur", "Etudiant, eleve", "Retraite", 
"Retire des affaires", "Au foyer", "Autre inactif")
  1. On crée la commande complète en enchaînant les fonctions dans le pipe.
hdv2003 %>%
    filter(occup %in% c("Retraite", "Retire des affaires") & sexe == "Femme") %>%
    select ("id":"relig", -c("qualif", "clso"), "sport") ->
    hdv2003_pipe

Attention: Vous devez employer la fonction combine (c) si vous souhaitez enlever plusieurs variables à la fois: en alternative à -c("qualif", "clso") on aurait pu aussi écrire -"qualif", -"clso".

  1. On vérifie le résultat.
names(hdv2003_pipe)
[1] "id"            "age"           "sexe"          "nivetud"      
[5] "poids"         "occup"         "freres.soeurs" "relig"        
[9] "sport"        
table(hdv2003_pipe$sexe, hdv2003_pipe$occup)
       
        Exerce une profession Chomeur Etudiant, eleve Retraite
  Homme                     0       0               0        0
  Femme                     0       0               0      184
       
        Retire des affaires Au foyer Autre inactif
  Homme                   0        0             0
  Femme                  38        0             0
# On va comparer avec l'original:

table(hdv2003$sexe, hdv2003$occup)
       
        Exerce une profession Chomeur Etudiant, eleve Retraite
  Homme                   520      54              48      208
  Femme                   529      80              46      184
       
        Retire des affaires Au foyer Autre inactif
  Homme                  39        0            30
  Femme                  38      171            53
# Dans les deux cas on voit 38 femmes retirées des affaires et 184 à la
# retraite, on n'a rien perdu!

Astuce: Vous pouvez travailler à une commande complexe de façon incrémentale, en enchaînant avec View à la fin de chaque étape1. Vous pouvez annoter chaque onglet avec l’argument title.

Par exemple, pour construire la commande ci-dessous:

hdv2003 %>%
    filter(occup %in% c("Retraite", "Retire des affaires") & sexe == "Femme") %>%
    View(title = "Après filter")

hdv2003 %>%
    filter(occup %in% c("Retraite", "Retire des affaires") & sexe == "Femme") %>%
    select ("id":"relig", -c("qualif", "clso"), "sport") %>%
    View(title = "Après select")

On se retrouve maintenant avec pas mal de données temporaires dans la mémoire de travail. Pas un soucis pour un ordi, mais bien compliqué pour l’humain devant l’écran. Faisons un peu de ménage:

dput(objects())
c("df", "df2", "df3", "df_boite", "df_pipe", "download.file", 
"femmes_retraite", "hdv2003", "hdv2003_2", "hdv2003_3", "hdv2003_4", 
"hdv2003_dput", "hdv2003_pipe", "rp2012")

On va copier/coller le résultat en remplaçant le c avec rm, puis biffer les variables qu’on veut garder pour la suite.

rm("df2", "df3", "df_boite", "df_pipe", "femmes_retraite", 
   "hdv2003_3", "hdv2003_4", "hdv2003_dput", "hdv2003_pipe", 
   "hdv2003_2", "x")
Warning in rm("df2", "df3", "df_boite", "df_pipe", "femmes_retraite",
"hdv2003_3", : object 'x' not found
objects()
[1] "df"            "download.file" "hdv2003"       "rp2012"       

Ça va mieux!

Exercices.

5 Ordonner des variables: arrange

5.1 Le principe de arrange

Le verbe arrange(data, column1, column2, ...) ordonne les individus selon les valeurs d’une ou plusieurs variable(s).

arrange(df, resultats_info)
# A tibble: 5 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     3              9 POLI   
2     5             12 SOCA   
3     4             13 SHUM   
4     1             15 SHUM   
5     2             17 SOCA   

On peut aussi ordonner une variable par ordre décroissant.

arrange(df, -resultats_info)
# A tibble: 5 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     2             17 SOCA   
2     1             15 SHUM   
3     4             13 SHUM   
4     5             12 SOCA   
5     3              9 POLI   

Pour une variable "character", l’ordre est alphabétique.

arrange(df, section)
# A tibble: 5 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     3              9 POLI   
2     1             15 SHUM   
3     4             13 SHUM   
4     2             17 SOCA   
5     5             12 SOCA   

On peut enfin, ordonner selon plusieurs variables, la priorité étant donnée à la première indiquée. Imaginons que l’on souhaite trier les étudiants par section, et au sein de leur section, les classer par ordre croissant de leur résultat en informatique:

arrange(df, section, resultats_info)
# A tibble: 5 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     3              9 POLI   
2     4             13 SHUM   
3     1             15 SHUM   
4     5             12 SOCA   
5     2             17 SOCA   

5.2 Procédure

Imaginons un exemple sur une base de données plus conséquente. Nous souhaitons distinguer plus clairement les personnes écoutant du hard rock, et de les ordonner par âge décroissant.

  1. On commence par demander les noms des variables du data frame original.
names(hdv2003)
 [1] "id"            "age"           "sexe"          "nivetud"      
 [5] "poids"         "occup"         "qualif"        "freres.soeurs"
 [9] "clso"          "relig"         "trav.imp"      "trav.satisf"  
[13] "hard.rock"     "lecture.bd"    "peche.chasse"  "cuisine"      
[17] "bricol"        "cinema"        "sport"         "heures.tv"    
  1. On applique le verbe arrange, en utilisant le pipe pour s’exercer.
hdv2003 %>% arrange(hard.rock, -age) -> hdv2003_ordre
  1. On vérifie le résultat.

Rappel: head(data, n=10) vous permet d’afficher les 10 premières observations et tail(data, n=10) vous permet d’afficher les 10 dernière observations. C’est un bon moyen de vérification.

head(hdv2003_ordre, 10)
     id age  sexe                           nivetud      poids
1  1916  97 Femme Derniere annee d'etudes primaires  2162.8346
2   270  96 Femme Derniere annee d'etudes primaires  9993.0201
3  1542  93 Femme Derniere annee d'etudes primaires  7107.8406
4   235  92 Homme          N'a jamais fait d'etudes   827.4537
5  1151  91 Homme Derniere annee d'etudes primaires  4291.0088
6    96  90 Femme Derniere annee d'etudes primaires  5875.0400
7  1122  90 Femme                         1er cycle 14812.2158
8  1767  90 Femme Derniere annee d'etudes primaires 12265.6080
9   759  89 Femme Derniere annee d'etudes primaires  9554.6802
10  971  89 Homme Derniere annee d'etudes primaires  2373.6318
                 occup             qualif freres.soeurs        clso
1        Autre inactif              Autre             5         Non
2             Retraite               <NA>             1         Oui
3  Retire des affaires               <NA>             7         Non
4             Retraite Ouvrier specialise             0 Ne sait pas
5  Retire des affaires               <NA>             3         Oui
6             Au foyer            Employe             7         Non
7             Retraite            Employe             3         Oui
8  Retire des affaires               <NA>             2         Non
9             Au foyer               <NA>             1         Non
10            Retraite Ouvrier specialise             5         Oui
                         relig trav.imp trav.satisf hard.rock lecture.bd
1       Pratiquant occasionnel     <NA>        <NA>       Non        Non
2  Ni croyance ni appartenance     <NA>        <NA>       Non        Non
3       Pratiquant occasionnel     <NA>        <NA>       Non        Non
4       Pratiquant occasionnel     <NA>        <NA>       Non        Non
5   Appartenance sans pratique     <NA>        <NA>       Non        Non
6       Pratiquant occasionnel     <NA>        <NA>       Non        Non
7          Pratiquant regulier     <NA>        <NA>       Non        Non
8   Appartenance sans pratique     <NA>        <NA>       Non        Non
9          Pratiquant regulier     <NA>        <NA>       Non        Non
10         Pratiquant regulier     <NA>        <NA>       Non        Non
   peche.chasse cuisine bricol cinema sport heures.tv
1           Non     Non    Non    Non   Non         3
2           Non     Non    Non    Non   Non         6
3           Non     Non    Non    Oui   Non         3
4           Non     Non    Non    Non   Non         3
5           Non     Non    Oui    Non   Non         0
6           Non     Non    Non    Non   Non         4
7           Non     Oui    Non    Non   Non         5
8           Non     Non    Non    Non   Non         3
9           Non     Non    Non    Non   Oui         3
10          Non     Non    Non    Non   Non         5
tail(hdv2003_ordre, 10)
       id age  sexe                                              nivetud
1991   32  29 Homme        Enseignement technique ou professionnel court
1992 1119  29 Homme Enseignement superieur y compris technique superieur
1993 1121  26 Homme        Enseignement technique ou professionnel court
1994  908  25 Femme                                                 <NA>
1995 1665  23 Femme Enseignement superieur y compris technique superieur
1996 1710  23 Femme                                            1er cycle
1997 1225  22 Femme                                                 <NA>
1998 1847  21 Homme        Enseignement technique ou professionnel court
1999  969  19 Femme        Enseignement technique ou professionnel court
2000 1381  19 Femme        Enseignement technique ou professionnel court
         poids                 occup           qualif freres.soeurs        clso
1991 14161.760 Exerce une profession            Autre             2         Oui
1992  7152.535               Chomeur          Employe             1         Non
1993  5077.781 Exerce une profession          Employe             0         Non
1994  7785.060       Etudiant, eleve             <NA>             3         Oui
1995  5429.595 Exerce une profession          Employe             2         Non
1996  6905.598               Chomeur          Employe             6         Non
1997  4253.383       Etudiant, eleve             <NA>             1         Oui
1998  4630.436               Chomeur Ouvrier qualifie             2 Ne sait pas
1999 16124.210         Autre inactif             <NA>             1         Non
2000  6062.769 Exerce une profession          Employe             1         Non
                           relig                     trav.imp  trav.satisf
1991  Appartenance sans pratique Moins important que le reste Satisfaction
1992 Ni croyance ni appartenance                         <NA>         <NA>
1993  Appartenance sans pratique Moins important que le reste    Equilibre
1994  Appartenance sans pratique                         <NA>         <NA>
1995 Ni croyance ni appartenance Moins important que le reste Satisfaction
1996 Ni croyance ni appartenance                         <NA>         <NA>
1997 Ni croyance ni appartenance                         <NA>         <NA>
1998 Ni croyance ni appartenance                         <NA>         <NA>
1999 Ni croyance ni appartenance                         <NA>         <NA>
2000 Ni croyance ni appartenance Moins important que le reste    Equilibre
     hard.rock lecture.bd peche.chasse cuisine bricol cinema sport heures.tv
1991       Oui        Non          Non     Oui    Oui    Oui   Non       1.0
1992       Oui        Non          Non     Non    Non    Oui   Non       0.1
1993       Oui        Non          Oui     Oui    Oui    Oui   Oui       4.0
1994       Oui        Non          Oui     Oui    Non    Oui   Non       5.1
1995       Oui        Non          Non     Oui    Non    Non   Non       3.0
1996       Oui        Non          Non     Non    Non    Oui   Non       4.0
1997       Oui        Non          Non     Oui    Oui    Oui   Oui       0.3
1998       Oui        Non          Non     Non    Non    Non   Non       2.0
1999       Oui        Non          Non     Non    Oui    Oui   Oui       2.0
2000       Oui        Non          Non     Oui    Non    Oui   Non       4.0

Nous n’avons bien, en tête, que des personnes qui n’écoutent pas de hard rock, classées par ordre décroissant d’âge, donc que les plus âgés, et en queue, que des personnes qui écoutent du hard rock, classées par ordre décroissant d’âge, donc que les plus jeunes.

6 Définir des nouvelles variables: mutate et transmute

6.1 Principe

Ces fonctions permettent de créer de nouvelles variables sur base des variables existantes moyennant de petites opérations mathématiques (simples, c’est promis).

Par exemple, si on veut transformer la note des étudiants en informatique en pourcent, on peut le faire. On crée une nouvelle variable dans le data frame.

df %>% mutate(resultat_pourcent = resultats_info*5)
# A tibble: 5 × 4
     ID resultats_info section resultat_pourcent
  <int>          <dbl> <chr>               <dbl>
1     1             15 SHUM                   75
2     2             17 SOCA                   85
3     3              9 POLI                   45
4     4             13 SHUM                   65
5     5             12 SOCA                   60

On peut aussi vouloir créer plusieurs variables en une seule commande.

df %>% mutate(resultat_pourcent = resultats_info*5,
              resultat_sur10 = resultats_info/2)
# A tibble: 5 × 5
     ID resultats_info section resultat_pourcent resultat_sur10
  <int>          <dbl> <chr>               <dbl>          <dbl>
1     1             15 SHUM                   75            7.5
2     2             17 SOCA                   85            8.5
3     3              9 POLI                   45            4.5
4     4             13 SHUM                   65            6.5
5     5             12 SOCA                   60            6  

On peut enfin vouloir créer un nouveau data frame, sans les variables originales, avec uniquement la ou les nouvelles variables. On utilise alors transmute.

df %>% transmute(resultat_pourcent = resultats_info*5,
                 resultat_sur10 = resultats_info/2)
# A tibble: 5 × 2
  resultat_pourcent resultat_sur10
              <dbl>          <dbl>
1                75            7.5
2                85            8.5
3                45            4.5
4                65            6.5
5                60            6  

transmute nous permets aussi de garder certaines des variables (comme select, mais sans les trucs plus avancés genre -, :). Voici un exemple sur une base de données un peu plus conséquente.

transmute(rp2012, code_insee, commune,
          nb.proprio = round(pop_tot * proprio / 100),
          nb.locataire = round(pop_tot * locataire / 100)) %>%
    View(title = "Nombre de propriétaires/locataires par commune")

Exercices.

7 Renommer des variables: rename

7.1 Le principe de rename

Parfois certains noms de variables ne sont pas très explicites et on pourrait vouloir les modifier. On peut renommer autant de variables que l’on souhaite en une fois, sachant qu’il faut respecter la structure nouveau_nom = ancien_nom. Par exemple,

df %>%
    rename(resultat = resultats_info, filiere = section) -> df_ren
df_ren
# A tibble: 5 × 3
     ID resultat filiere
  <int>    <dbl> <chr>  
1     1       15 SHUM   
2     2       17 SOCA   
3     3        9 POLI   
4     4       13 SHUM   
5     5       12 SOCA   

Tout comme pour select, il n’est pas nécessaire d’employer les guillemets puisque vous travaillez avec des variables, et pas avec leurs niveaux/valeurs, mais rename nous permets aussi de les utiliser. Pour éviter des fautes de frappe, il vaut donc mieux utiliser dput, en copiant/collant juste la partie entre c(...), et en rajoutant "nouveau_nom" = devant à chaque "ancien_nom".

dput(names(df))
c("ID", "resultats_info", "section")
rename(df, "etudiant_ID" = "ID", "Points" = "resultats_info", "Cours" = "section")
# A tibble: 5 × 3
  etudiant_ID Points Cours
        <int>  <dbl> <chr>
1           1     15 SHUM 
2           2     17 SOCA 
3           3      9 POLI 
4           4     13 SHUM 
5           5     12 SOCA 

Remarque: avant l’usage de rename, on employait l’indexation pour renommer les variables comme dans l’exemple ci-dessous.

names(df)[1] <- c("ID")
df
# A tibble: 5 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     1             15 SHUM   
2     2             17 SOCA   
3     3              9 POLI   
4     4             13 SHUM   
5     5             12 SOCA   

7.2 Procédure

Voyons un exemple sur une base de données plus conséquente. Dans hdv2003, nous souhaitons donnez des noms plus explicites à certaines variables.

  1. On commence par demander le nom des variables
dput(names(hdv2003))
c("id", "age", "sexe", "nivetud", "poids", "occup", "qualif", 
"freres.soeurs", "clso", "relig", "trav.imp", "trav.satisf", 
"hard.rock", "lecture.bd", "peche.chasse", "cuisine", "bricol", 
"cinema", "sport", "heures.tv")
  1. On les renomme, et on écrase pas sa base de données originale, on en crée une nouvelle.
hdv2003 %>%
    rename("niveau_etudes" = "nivetud",
           "occupation" = "occup",
           "qualification" = "qualif",
           "appartenance_classe" = "clso") %>%
    select("id":"sexe", "niveau_etudes", "occupation",
           "qualification", "appartenance_classe") ->
    hdv2003_2
  1. On vérifie le résultat
names(hdv2003_2)
[1] "id"                  "age"                 "sexe"               
[4] "niveau_etudes"       "occupation"          "qualification"      
[7] "appartenance_classe"

Astuce: Changer les noms des variables pour les rendre plus explicite, c’est très utile pour vous éviter de devoir sans cesse consulter votre dictionnaire. Mais ça peut vite devenir fastidieux dans le code si vous choisissez des noms trop longs ou trop complexes. Un équilibre entre les deux est bien nécessaire. Rappelez-vous des règles pour nommer des variables!

Exercices.

8 Statistiques descriptives: summarize et group_by

8.1 Le principe de summarize et group_by

La fonction summarize (ou summarise) vous permet d’obtenir des statistiques descriptives sous forme de tableau.

df %>% summarize(moyenne = mean(resultats_info))
# A tibble: 1 × 1
  moyenne
    <dbl>
1    13.2

Jusqu’ici, elle n’est pas spécialement intéressante. En revanche, couplée à la fonction group_by, vous pouvez obtenir ces statistiques descriptives selon les modalités d’une variable qualitative. Vous pouvez créer un nouveau data frame sur base de ces résultats. C’est bien cette fonction qui vous manquait dans la leçon sur les statistiques descriptives. Elle vous permet, entre autres, de mettre en lien une variable qualitative avec une variable quantitative.

df %>% group_by(section) %>% summarize(moyenne = mean(resultats_info))
# A tibble: 3 × 2
  section moyenne
  <chr>     <dbl>
1 POLI        9  
2 SHUM       14  
3 SOCA       14.5

Vous pouvez enchaîner les statistiques descriptives.

df %>% group_by(section) %>% summarize(moyenne = mean(resultats_info),
                                       mediane = median(resultats_info))
# A tibble: 3 × 3
  section moyenne mediane
  <chr>     <dbl>   <dbl>
1 POLI        9       9  
2 SHUM       14      14  
3 SOCA       14.5    14.5

Sur une base de données plus complexe, cela donne ceci:

hdv2003 %>% group_by(sexe) %>%
    summarize(age.mediane = median(age, na.rm = TRUE),
              heures.tv.moyenne = mean(heures.tv, na.rm = TRUE))
# A tibble: 2 × 3
  sexe  age.mediane heures.tv.moyenne
  <fct>       <int>             <dbl>
1 Homme          48              2.22
2 Femme          47              2.27

On peut distinguer des sous-groupes par rapport à d’autres variables:

hdv2003 %>% group_by(sexe, sport) %>%
  summarize(age.mediane = median(age, na.rm = TRUE),
            heures.tv.moyenne = mean(heures.tv, na.rm = TRUE))
`summarise()` has grouped output by 'sexe'. You can override using the
`.groups` argument.
# A tibble: 4 × 4
# Groups:   sexe [2]
  sexe  sport age.mediane heures.tv.moyenne
  <fct> <fct>       <dbl>             <dbl>
1 Homme Non            54              2.41
2 Homme Oui            39              1.94
3 Femme Non            51              2.53
4 Femme Oui            39              1.72

Astuce: On peut compter le nombre d’observations pour chaque combinaison de niveaux de variables avec n() comme dans l’exemple ci-dessous.

hdv2003 %>% group_by(sexe, sport) %>%
  summarize(age.mediane = median(age, na.rm = TRUE),
            heures.tv.max = max(heures.tv, na.rm = TRUE),
            n.obs = n())
`summarise()` has grouped output by 'sexe'. You can override using the
`.groups` argument.
# A tibble: 4 × 5
# Groups:   sexe [2]
  sexe  sport age.mediane heures.tv.max n.obs
  <fct> <fct>       <dbl>         <dbl> <int>
1 Homme Non            54            10   530
2 Homme Oui            39            11   369
3 Femme Non            51            12   747
4 Femme Oui            39             9   354

Astuce: On peut compter le nombre de valeurs manquantes NA comme dans l’exemple ci-dessous.

hdv2003 %>% group_by(sexe) %>%
    summarize(age.mediane = median(age, na.rm = TRUE),
              heures.tv.moyenne = mean(heures.tv, na.rm = TRUE),
              n.obs = n(),
              heures.tv.na = sum(is.na(heures.tv)))
# A tibble: 2 × 5
  sexe  age.mediane heures.tv.moyenne n.obs heures.tv.na
  <fct>       <int>             <dbl> <int>        <int>
1 Homme          48              2.22   899            4
2 Femme          47              2.27  1101            1

Exercices.

9 Fusionner deux jeux de données: full_join

Il arrive souvent que l’on ait des bases de données distinctes mais qui concerne les mêmes individus. Ou encore d’avoir des bases de données concernant des individus différents, mais qui contiennent des variables communes.

Créons trois jeux de données pour en comprendre le principe.

df1 <- tibble(ID = 1:5,
             resultats_info = c(15, 17, 9, 13, 12),
             section = c("SHUM", "SOCA", "POLI", "SHUM", "SOCA"))
df2 <- tibble(ID = 6:10,
             resultats_info = c(12, 3, 9, 14, 18),
             section = c("SOCA", "POLI", "SHUM", "SOCA", "SOCA"))
df3 <- tibble(ID = 1:5,
             resultats_stat = c(20, 8, 11, 13, 15),
             section = c("SHUM", "SOCA", "POLI", "SHUM", "SOCA"))

Le premier jeu de données contient 5 étudiants, leur note en informatique et leur section. Le second contient 5 autres étudiants, leur note pour le même cours, et leur section. Le troisième reprend les 5 premiers étudiants, leur note en statistique, et leur section.

Voyons comment ne réaliser qu’un seul jeu de données de ces trois ensembles.

df_temp <- full_join(df1, df2)
Joining with `by = join_by(ID, resultats_info, section)`
df_final <- full_join(df_temp, df3)
Joining with `by = join_by(ID, section)`
df_final
# A tibble: 10 × 4
      ID resultats_info section resultats_stat
   <int>          <dbl> <chr>            <dbl>
 1     1             15 SHUM                20
 2     2             17 SOCA                 8
 3     3              9 POLI                11
 4     4             13 SHUM                13
 5     5             12 SOCA                15
 6     6             12 SOCA                NA
 7     7              3 POLI                NA
 8     8              9 SHUM                NA
 9     9             14 SOCA                NA
10    10             18 SOCA                NA

Tout s’est bien passé, nous avons un jeu de données complet, et des NA ont été générées pour les résultats en statistique des 5 derniers étudiants.

Astuce: une fois qu’on a compris le principe, il n’est pas nécessaire de créer les données intermédiaires df_temp, on peut utiliser le %>%!

Tant qu’on y est, on va aussi remettre en ordre les colonnes:

df1 %>%
    full_join(df2) %>% # deviens full_join(df1, df2)
    full_join(df3) %>% # full_join(full_join(df1, df2), df3)
    select(ID, section, everything()) -> 
    df_final
Joining with `by = join_by(ID, resultats_info, section)`
Joining with `by = join_by(ID, section)`
df_final
# A tibble: 10 × 4
      ID section resultats_info resultats_stat
   <int> <chr>            <dbl>          <dbl>
 1     1 SHUM                15             20
 2     2 SOCA                17              8
 3     3 POLI                 9             11
 4     4 SHUM                13             13
 5     5 SOCA                12             15
 6     6 SOCA                12             NA
 7     7 POLI                 3             NA
 8     8 SHUM                 9             NA
 9     9 SOCA                14             NA
10    10 SOCA                18             NA

Attention: Pour joindre deux jeux de données, par colonnes et lignes communes, il faut que les éléments communs soient parfaitement concordants au niveau de leur encodage.

Retentons l’expérience en introduisant une petite erreur d’encodage.

df3$section[1] # c'est un "SHUM"
[1] "SHUM"
df3$section[1] <- "SHMU" # oops..

head(df1, 1)
# A tibble: 1 × 3
     ID resultats_info section
  <int>          <dbl> <chr>  
1     1             15 SHUM   
head(df3, 1)
# A tibble: 1 × 3
     ID resultats_stat section
  <int>          <dbl> <chr>  
1     1             20 SHMU   

L’étudiant 1 est maintenant en "SHUM" dans le premier jeu de données et "SHMU" dans le troisième. Voyons ce qui se passe si on réessaye la même commande:

df1 %>%
    full_join(df2) %>% 
    full_join(df3) %>% 
    select(ID, section, everything()) -> 
    df_final_err
Joining with `by = join_by(ID, resultats_info, section)`
Joining with `by = join_by(ID, section)`
df_final_err
# A tibble: 11 × 4
      ID section resultats_info resultats_stat
   <int> <chr>            <dbl>          <dbl>
 1     1 SHUM                15             NA
 2     2 SOCA                17              8
 3     3 POLI                 9             11
 4     4 SHUM                13             13
 5     5 SOCA                12             15
 6     6 SOCA                12             NA
 7     7 POLI                 3             NA
 8     8 SHUM                 9             NA
 9     9 SOCA                14             NA
10    10 SOCA                18             NA
11     1 SHMU                NA             20

R a généré un doublon de cet étudiant: il apparaît deux fois, une première fois en "SHUM" et sa note d’informatique, et une seconde fois en "SHMU" avec sa note de statistique. Ce n’est pas le résultat attendu. Vérifiez donc bien que votre jeu de données final ne contienne pas plus d’individus que vous n’en aviez au départ.

dim(df_final_err) # On se retrouve avec 11 étudiants alors qu'on en avait que 10 à l'origine.
[1] 11  4

Pour aller plus loin: Le résultat de full_join(x, y) inclut tous les individus présents dans x ou dans y: elle opère une réunion des observations. Pour d’autres résultats, on a a disposition toute une famille de fonctions _join, par exemple: inner_join(x, y) rend uniquement les observations dans x et y (intersection); left_join(x, y) rend uniquement les observations dans x, peu importe si elles sont dans y ou pas. Tout ça fonctionne comme prévu si les colonnes en commun contiennent les mêmes données. Ce n’est pas requis, mais vous pouvez lire la section jointures dans le syllabus si besoin.


Exercices — Index du cours



  1. Encore mieux, si vous arrivez à installer devtools, vous pourrez aussi installer ViewPipeSteps↩︎