── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 4.0.0 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.1
✔ purrr 1.0.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Attaching package: 'magrittr'
The following object is masked from 'package:purrr':
set_names
The following object is masked from 'package:tidyr':
extract
17 Ceci n’est pas qu’un opérateur : %>%
et magrittr
17.1 L’opérateur pipe %>%
L’opérateur pipe permet de passer, de gauche à droite, le résultat d’une fonction à une seconde fonction, puis à une troisième. Ce pipe est détaillé dans un chapitre mais sa compréhension devrait être intuitive.
Comparez par exemple ces deux lignes pourtant strictement équivalentes :
{r} library(magrittr) plot(sqrt(sample(seq(1:100), 6))) # road to burnout seq(1:100) %>% sample(6) %>% sqrt() %>% plot() # let’s breathe
Les packages du R moderne, en premier lieu ceux du tidyverse en ont fait une idée centrale de leur design et il est peu dire que nous autres mortel·le·s en profitons tous les jours.
Il est peu dire que cet opérateur1 a révolutionné R, lorsqu’il y a été importé, d’abord dans le package magrittr
sous sa forme %>%
. Il est désormais inclus dans le R de “base” sous sa forme |>
mais nous n’utiliserons que la version historique %>%
, que je trouve plus lisible, plus facile à taper (<Maj> + <Ctrl/Cmd> + <M>
dans RStudio) et parce que les années aidant, je deviens conservateur.
L’idée du pipe est issue de la composition de fonctions en mathématiques. Plutôt que d’écrire :
h(g(f(x)))
on peut déplier cet emboitement de fonctions et écrire (h ∘ g ∘ f)(x)
En langage R, plutôt que d’écrire h(g(f(x)))
on écrira : x %>% f() %>% g() %>% h()
2. Cette écriture est non seulement plus lisible mais elle se lit également de gauche à droite, dans le sens conventionnel de notre partie du monde.
17.2 %>%
vs |>
D’abord introduit par le package magrittr
le forward pipe est désormais dans le R “de base” depuis la version 4.1.0.
Le pipe |>
tend désormais à être préféré à %>%
comme on le lira sur le (blog du tidyverse)[, très complet)[https://www.tidyverse.org/blog/2023/04/base-vs-magrittr-pipe/]. Ce dernier mérite néanmoins, avec ses autres copains de magrittr
, d’être détaillé.
Si la vignette de magrittr
est très bien faite (vignette("magrittr"
), j’en livre ici une introduction rapide.
17.3 %>%
Dans les grandes lignes, %>%
et |>
fonctionnent de la même façon. J’ai tendance à continuer d’utiliser %>%
qui est chargé automatiquement par le tidyverse. Pour les autres opérateurs, ou si vous n’utilisez pas le tidyverse vous pouvez en disposer avec :
Venons en aux faits.
runif(100) %>% mean()
[1] 0.5606005
17.4 Le .
pour customiser le forward
Par défaut, %>%
injecte ce qui sort de la fonction à sa gauche comme premier argument de la fonction à sa droite.
La plupart du temps, notamment avec le tidyverse, cela fonctionne à merveille. Mais pensez à plot
ou à lm
, l’argument data
est le second, pas le premier (qui est une formula
).
maggrittr
prévoir ce cas et vous pouvez spécifier un atterrissage alternatif avec .
:
%>%
iris as_tibble() %>%
select(pl=Petal.Length, pw=Petal.Width) %>%
lm(pw~pl, data=.)
Call:
lm(formula = pw ~ pl, data = .)
Coefficients:
(Intercept) pl
-0.3631 0.4158
17.5 %T>%
Le “tee” pipe est lui utile quand l’une des fonctions est un cul de sac, typiquement un print
ou un plot
.
L’idée est, dans un seul pipe, de plotter (ou printer) mais de continuer avec l’objet de départ. En d’autres termes de créer en un point du pipe, une bifurcation dont l’une des branche est un cul de sac, et l’autre continue.
Un exemple vaut mille mots :
%>%
iris as_tibble() %>%
select(pl=Petal.Length, pw=Petal.Width) %T>%
print() %>% # here we print
plot(pw~pl, data=.) # and we come back to the object returned by `select`
# A tibble: 150 × 2
pl pw
<dbl> <dbl>
1 1.4 0.2
2 1.4 0.2
3 1.3 0.2
4 1.5 0.2
5 1.4 0.2
6 1.7 0.4
7 1.4 0.3
8 1.5 0.2
9 1.4 0.2
10 1.5 0.1
# ℹ 140 more rows
17.6 `%$%
Cette variante a pour but d’“exposer” le nom des éléments d’une liste, souvent à des fins d’extraction :
%>%
iris as_tibble() %>%
slice(1:5) %>%
select(pl=Petal.Length, pw=Petal.Width) %$% pl
[1] 1.4 1.4 1.3 1.5 1.4
Vous pouvez remplacer avantageusement ce pipe par dplyr::pull
qui est quelque part encore plus conforme à l’esprit “dplyr/tidyverse” :
%>%
iris as_tibble() %>%
slice(1:5) %>%
select(pl=Petal.Length, pw=Petal.Width) %>%
pull(pw)
[1] 0.2 0.2 0.2 0.2 0.2
17.7 %<>%
Dernier opérateur de cette liste, un peu passé de mode mais que je mentionne ici par souci de complétude. Cet opérateur pipe
part d’un objet, lui fait faire tout le chemin au sein d’un pipe et réassigne l’objet de départ avec l’objet retourné par le pipe :
<- iris %>% as_tibble()
x x
# A tibble: 150 × 5
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
<dbl> <dbl> <dbl> <dbl> <fct>
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
# ℹ 140 more rows
%<>% select(1:2)
x x
# A tibble: 150 × 2
Sepal.Length Sepal.Width
<dbl> <dbl>
1 5.1 3.5
2 4.9 3
3 4.7 3.2
4 4.6 3.1
5 5 3.6
6 5.4 3.9
7 4.6 3.4
8 5 3.4
9 4.4 2.9
10 4.9 3.1
# ℹ 140 more rows
Cet opérateur est critique pour un motif recevable : l’assignation est une opération tellement importante qu’elle devrait être plus visible qu’un seul caractère (%>%
versus %<>%
).