Le problème que les pipes résolvent
Un pipe prend la valeur qui le précède et l'injecte dans l'appel de fonction qui le suit - x |> f() signifie f(x). Ça a l'air d'une réécriture triviale jusqu'à ce que tu enchaînes trois ou quatre étapes. Sans pipe, les transformations à plusieurs étapes s'imbriquent, et les appels imbriqués se lisent à l'envers : la première chose qui se passe est à la position la plus interne.
Pour lire round(sqrt(x), 1), tu pars du milieu (x), tu remontes vers l'extérieur (sqrt, puis round), et tu gardes en tête quel , 1) final appartient à quel appel. À trois niveaux de profondeur c'est agaçant ; à cinq niveaux, avec des verbes de data frame et plusieurs arguments chacun, c'est réellement difficile à suivre. L'alternative vers laquelle on se tourne - stocker chaque étape intermédiaire dans tmp1, tmp2, tmp3 - encombre l'espace de travail de noms dont personne n'a besoin.
Un pipe réécrit la chaîne dans l'ordre où les étapes se produisent réellement : prends x, puis prends sa racine carrée, puis arrondis-la.
Le pipe natif |>
Depuis R 4.1, le pipe est intégré au langage lui-même - aucun package requis. |> insère la valeur précédente comme premier argument de l'appel suivant :
c(1, 4, 9, 16) |> sqrt() |> round(1) est exactement round(sqrt(c(1, 4, 9, 16)), 1) - R réécrit littéralement l'un en l'autre pendant l'analyse syntaxique, donc il n'y a aucun surcoût à l'exécution. Mais maintenant le code se lit comme une recette, de haut en bas : les données, puis chaque transformation dans l'ordre. Ça marche avec n'importe quelle fonction, pas seulement les outils de données - mtcars |> head(3) est du R de base de bout en bout.
Remarque à quel point les pipes conviennent aux fonctions dont le premier paramètre est « les données » : head, sort, unique, summary et chaque verbe dplyr sont tous conçus ainsi, et c'est pourquoi les pipelines paraissent si naturels en R.
Les règles de |>
Le pipe natif est délibérément strict. Trois règles couvrent presque tout :
1. L'étape suivante doit être un appel, avec des parenthèses. x |> sqrt est une erreur de syntaxe ; tu dois écrire x |> sqrt() :
c(1, 4, 9) |> sqrt # Error: The pipe operator requires a function call as RHS
c(1, 4, 9) |> sqrt() # correct
2. La valeur atterrit en position de premier argument. Les arguments supplémentaires que tu écris restent en place après elle - x |> round(1) c'est round(x, 1).
3. Pour atterrir ailleurs, utilise le placeholder _ avec un argument nommé (R 4.2+). Cas classique : lm prend une formule en premier et les données en second, donc y injecter un data frame demande data = _ :
Le _ peut apparaître une fois, et seulement comme argument nommé. Quand c'est trop restrictif, injecte dans une fonction anonyme - elle peut placer la valeur où elle veut :
La lambda est entourée de parenthèses puis appelée avec () - ce qui satisfait la règle 1. Si tu fais ça souvent dans un même pipeline, cette étape veut probablement devenir une fonction nommée.
Le pipe magrittr %>%
Avant que le langage ait un pipe, le package magrittr en fournissait un, et %>% est devenu la signature du code tidyverse - charger dplyr te le donne automatiquement. Tu le verras dans presque tous les tutoriels dplyr et les scripts écrits ces dix dernières années :
library(dplyr)
mtcars %>%
filter(mpg > 30) %>%
select(mpg, wt)
Comme %>% vient d'un package, ces exemples ne sont pas exécutables dans une session R nue - Error: could not find function "%>%" est exactement ce que tu obtiens si tu oublies library(dplyr) (ou library(magrittr)).
Le comportement dans le cas courant est identique à |> : injecter la valeur précédente dans le premier argument de l'appel suivant. Mais magrittr est plus permissif :
c(1, 4, 9) %>% sqrt # bare function name - no parentheses needed
mtcars %>% lm(mpg ~ wt, data = .) # . placeholder, usable anywhere
c(-2, 3) %>% { max(abs(.)) } # . can even appear several times, in nested calls
|> ou %>% : les différences qui comptent
Pour un pipeline sur le premier argument - c'est-à-dire l'immense majorité du vrai code - les deux sont interchangeables. Les différences vivent dans les marges :
- Disponibilité :
|>est du R de base 4.1+ ;%>%a besoin de magrittr (généralement via dplyr). - Parenthèses :
|>exigesqrt();%>%acceptesqrtnu. - Placeholder :
|>utilise_, une seule fois, uniquement comme argument nommé (4.2+) ;%>%utilise., n'importe où, autant de fois qu'on veut, y compris dans des appels imbriqués. - Vitesse :
|>est réécrit à l'analyse en l'appel imbriqué - coût nul.%>%est un appel de fonction ordinaire avec un petit surcoût (négligeable en pratique).
Rien ici ne rend %>% incorrect - il est éprouvé et légèrement plus souple. Mais tout ce qu'il fait au-delà de |> peut se faire avec une lambda, et le pipe de base ne demande aucune dépendance.
Lequel utiliser ?
Pour du code neuf : utilise |>. Il fait partie du langage, marche partout où tourne R 4.1+, ne demande aucun package, et sa rigueur se lit comme un atout - les pipelines restent simples ou sont refactorés en fonctions nommées.
Mais tu dois pouvoir lire %>% couramment, parce que les bases de code tidyverse, les réponses Stack Overflow et la plupart des livres R écrits depuis 2014 en sont pleins. Les équipes ayant déjà un style dplyr gardent souvent %>% par cohérence, et c'est un choix raisonnable - le pire choix est de mélanger les deux opérateurs dans un même fichier. Quel que soit celui que tu écris, une convention se transpose : dans les longs pipelines, mets chaque étape sur sa propre ligne avec le pipe en fin de ligne, pour que la recette se lise comme une liste d'étapes.
Le même goût s'applique ici que pour la famille apply : les pipes sont faits pour des transformations linéaires, étape après étape. Quand un calcul se ramifie ou a besoin deux fois d'un résultat intermédiaire, affecte une variable bien nommée plutôt que de tout forcer dans une seule chaîne.
Ce que tu retiens
- Un pipe injecte la valeur précédente dans l'appel suivant :
x |> f() |> g()c'estg(f(x)), écrit dans l'ordre où ça se passe. |>est du R de base (4.1+) : exige des parenthèses, vise le premier argument, placeholder_avec un argument nommé (4.2+).%>%est magrittr/tidyverse : noms nus autorisés, placeholder.n'importe où - mais il a besoin d'un package.- Préfère
|>dans du code neuf ; lis%>%partout ailleurs sans sourciller. - Les pipes conviennent aux recettes linéaires ; la logique qui se ramifie mérite des variables nommées.
Prochaine étape : les packages - d'où viennent réellement %>%, dplyr et les 20 000 autres extensions de R.
Questions fréquentes
Que signifie %>% en R ?
%>% est le pipe de magrittr : il prend la valeur qui le précède et l'injecte comme premier argument de l'appel qui le suit, donc x %>% f() %>% g() signifie g(f(x)). Il vient du package magrittr et est chargé automatiquement avec dplyr et le tidyverse - il ne fait pas partie de R de base.
Ai-je besoin d'un package pour utiliser |> en R ?
Non. |> est le pipe natif, intégré à R de base depuis la version 4.1 - il fonctionne dans une session R nue sans rien d'installé. C'est %>% qui nécessite un package (magrittr, ou tout ce qui le réexporte, comme dplyr).
Quelle est la différence entre |> et %>% en R ?
Les deux injectent la valeur précédente dans l'appel suivant. |> est du R de base, exige des parenthèses explicites (x |> sqrt()), et n'utilise le placeholder _ qu'avec un argument nommé. %>% a besoin de magrittr, accepte les noms de fonctions nus (x %>% sqrt), et son placeholder . peut aller n'importe où, autant de fois qu'on veut. Le comportement est identique pour le cas courant du premier argument.