5.5 Chaînage des instructions
Le chaînage (ou “pipe” en anglais) permet de combiner une suite d’instructions R. Il permet une représentation facilement lisible et compréhensible d’un traitement décomposé en plusieurs étapes simples de remaniement des données.
Différents opérateurs de chaînage existent dans R. Le Tidyverse et RStudio sont en faveur de l’adoption d’un opérateur de chaînage %>%
issu du package {magrittr}. Si nous sommes sensibles au clin d’œil fait ici à un artiste belge bien connu (“ceci n’est pas un pipe”), nous n’adhérons pas à ce choix pour des raisons multiples et plutôt techniques qui n’ont pas leur place dans ce document22. Nous vous présentons ici l’un des opérateurs de chaînage du package {flow} : %>.%
. Le jeu de données sur la biométrie humaine est employé pour cette démonstration qui va comparer le remaniement d’un tableau de données avec et sans l’utilisation du chaînage.
Vous vous intéressez à l’indice de masse corporelle ou IMC (BMI en anglais) des individus de moins de 25 ans. Vous souhaitez représenter la moyenne, la médiane et le nombre d’observations de manière séparée pour les hommes et les femmes. Pour obtenir ces résultats vous devez :
- calculer le BMI,
- filtrer le tableau pour ne retenir que les individus de moins de 25 ans,
- résumer les données afin d’obtenir la moyenne et la médiane par genre,
- afficher un tableau de données avec ces résultats.
Il est très clair ici que le traitement peut être décomposé en étapes plus simples. Cela apparaît naturellement rien que dans la description de ce qui doit être fait. Sans l’utilisation de l’opérateur de chaînage, deux approches sont possibles :
- Imbriquer les instructions les unes dans les autres (très difficile à lire et à déboguer) :
knitr::kable(
summarise(
group_by(
filter(
mutate(biometry, bmi = weight / (height/100)^2),
age <= 25),
gender),
mean = mean(bmi),
median = median(bmi),
number = n()),
rows = NULL, digits = 1,
col = c("Genre", "Moyenne", "Médiane", "Observations"),
caption = "IMC d'hommes (M) et femmes (W) de 25 ans maximum."
)
Genre | Moyenne | Médiane | Observations |
---|---|---|---|
M | 22.3 | 22.1 | 97 |
W | 21.8 | 21.0 | 94 |
- Passer par des variables intermédiaires (
biometry_25
etbiometry_tab
). Les instructions sont plus lisibles, mais les variables intermédiaires “polluent” inutilement l’environnement de travail (en tout cas, si elles ne servent plus par après) :
biometry <- mutate(biometry, bmi = weight / (height/100)^2)
biometry_25 <- filter(biometry, age <= 25)
biometry_25 <- group_by(biometry_25, gender)
biometry_tab <- summarise(biometry_25,
mean = mean(bmi),
median = median(bmi),
number = n())
knitr::kable(biometry_tab, rows = NULL, digits = 1,
col = c("Genre", "Moyenne", "Médiane", "Observations"),
caption = "IMC d'hommes (M) et femmes (W) de 25 ans maximum.")
Genre | Moyenne | Médiane | Observations |
---|---|---|---|
M | 22.3 | 22.1 | 97 |
W | 21.8 | 21.0 | 94 |
- Des trois approches, la version ci-dessous avec chaînage des opérations est la plus lisible et la plus pratique23.
biometry %>.%
mutate(., bmi = weight / (height/100)^2) %>.%
filter(., age <= 25) %>.%
group_by(., gender) %>.%
summarise(.,
mean = mean(bmi),
median = median(bmi),
number = n()) %>.%
knitr::kable(., rows = NULL, digits = 1,
col = c("Genre", "Moyenne", "Médiane", "Observations"),
caption = "IMC d'hommes (M) et femmes (W) de 25 ans maximum.")
Genre | Moyenne | Médiane | Observations |
---|---|---|---|
M | 22.3 | 22.1 | 97 |
W | 21.8 | 21.0 | 94 |
Le pipe %>.%
injecte le résultat précédent dans l’instruction suivante à travers l’objet .
Ainsi, en seconde ligne mutate(.)
, .
se réfère à biometry
. A la ligne suivante, filter(.)
, le .
se réfère au résultat issu de l’opération mutate()
, et ainsi de suite. La logique d’enchaînement des opérations sur le résultat, à chaque fois, du calcul précédent est donc le fondement de cet opérateur “pipe”.
Le pipe permet d’éviter de répéter le nom des objets (version avec variables intermédiaires), ce qui alourdit inutilement le code et le rend moins agréable à la lecture. L’imbrication des fonctions dans la première version est catastrophique pour la compréhension du code car les arguments des fonctions de plus haut niveau sont repoussés loin. Par exemple, l’argument de l’appel à group_by()
(gender
) se retrouve quatre lignes plus loin. Et encore, nous avons pris soin d’indenter le code pour repérer sur un plan vertical qui appartient à qui, mais imaginez ce que cela donne si l’instruction est mise à plat sur une seule ligne ! Le code le plus clair à la lecture est définitivement celui avec chaînage des opérations. Or, un code plus lisible est plus compréhensible… et donc, moins bogué.
À vous de jouer !
Effectuez maintenant les exercices du tutoriel A05La_traitement (Traitement des données I).
BioDataScience1::run("A05La_traitement")
Réalisez le travail A05Ia_transformation.
Travail individuel pour les étudiants inscrits au cours de Science des Données Biologiques I : visualisation à l’UMONS à terminer avant le 2021-12-24 23:59:59.
Initiez votre projet GitHub Classroom
Voyez les explications dans le fichier README.md
.
Pour en savoir plus
Présentation en détail du “dot-pipe” assez proche fonctionnellement de
%>.%
du package {flow}.Section sur le pipe dans “R for Data Science” expliquant l’utilisation du pipe de magrittr (et aussi quand ne pas l’utiliser !)