dplyrDernière mise à jour: 2025-11-28 09:43:
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")
dplyrdplyr?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.
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).
filterfilterLe 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
Voyons maintenant un exemple plus réaliste. Essayons d’extraire un sous-ensemble de hdv2003, ne reprenant que les femmes retraitées:
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.
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"
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.
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.
selectselectLe 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
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 :
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"
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)
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:
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"
select:hdv2003_4 <- select(hdv2003, sexe, age, heures.tv, everything())
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!
%>% (then)%>%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.
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.
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")
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")
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".
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!
arrangearrangeLe 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
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.
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"
arrange, en utilisant le pipe pour s’exercer.hdv2003 %>% arrange(hard.rock, -age) -> hdv2003_ordre
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.
mutate et transmuteCes 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")
renamerenameParfois 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
Voyons un exemple sur une base de données plus conséquente. Dans hdv2003, nous souhaitons donnez des noms plus explicites à certaines 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")
hdv2003 %>%
rename("niveau_etudes" = "nivetud",
"occupation" = "occup",
"qualification" = "qualif",
"appartenance_classe" = "clso") %>%
select("id":"sexe", "niveau_etudes", "occupation",
"qualification", "appartenance_classe") ->
hdv2003_2
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!
summarize et group_bysummarize et group_byLa 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
full_joinIl 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.
Encore mieux, si vous arrivez à installer devtools, vous pourrez aussi installer ViewPipeSteps↩︎