Menu

L'operatore pipe in R: %>% e il nativo |>

Cosa significa %>%, come funziona la pipe nativa |> di R base, le regole e i segnaposto di ciascuna e quale usare nel codice nuovo.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Il problema che le pipe risolvono

Una pipe prende il valore che la precede e lo passa alla chiamata di funzione che la segue: x |> f() significa f(x). Sembra una riscrittura banale finché non concateni tre o quattro passaggi. Senza pipe, le trasformazioni in più passaggi si annidano, e le chiamate annidate si leggono al contrario: la prima cosa che succede sta nella posizione più interna.

Per leggere round(sqrt(x), 1) parti dal centro (x), vai verso l'esterno (sqrt, poi round) e tieni traccia di quale , 1) finale appartiene a quale chiamata. Con tre livelli è fastidioso; con cinque livelli, con verbi per data frame e più argomenti ciascuno, diventa davvero difficile da seguire. L'alternativa a cui si ricorre, salvare ogni passaggio intermedio in tmp1, tmp2, tmp3, riempie lo spazio di lavoro di nomi che non servono a nessuno.

Una pipe riscrive la catena nell'ordine in cui i passaggi avvengono davvero: prendi x, poi calcolane la radice quadrata, poi arrotondala.

La pipe nativa |>

Da R 4.1 la pipe è integrata nel linguaggio stesso, senza bisogno di pacchetti. |> inserisce il valore precedente come primo argomento della chiamata successiva:

c(1, 4, 9, 16) |> sqrt() |> round(1) è esattamente round(sqrt(c(1, 4, 9, 16)), 1): R riscrive letteralmente l'una nell'altra durante l'analisi del codice, quindi il costo in esecuzione è zero. Ma ora il codice si legge come una ricetta, dall'alto in basso: i dati, poi ogni trasformazione in ordine. Funziona con qualsiasi funzione, non solo con gli strumenti per i dati: mtcars |> head(3) è R base dall'inizio alla fine.

Nota quanto le pipe si adattano bene alle funzioni il cui primo parametro è "i dati": head, sort, unique, summary e ogni verbo di dplyr sono progettati così, ed è per questo che le pipeline sembrano così naturali in R.

Le regole di |>

La pipe nativa è volutamente severa. Tre regole coprono quasi tutto:

1. Il passaggio successivo deve essere una chiamata, con le parentesi. x |> sqrt è un errore di sintassi; devi scrivere x |> sqrt():

c(1, 4, 9) |> sqrt     # Error: The pipe operator requires a function call as RHS
c(1, 4, 9) |> sqrt()   # correct

2. Il valore finisce nella posizione del primo argomento. Gli argomenti aggiuntivi che scrivi restano al loro posto dopo di esso: x |> round(1) è round(x, 1).

3. Per farlo finire altrove, usa il segnaposto _ con un argomento con nome (R 4.2+). Caso classico: lm prende prima una formula e poi i dati, quindi passargli un data frame con la pipe richiede data = _:

Il _ può comparire una sola volta, e solo come argomento con nome. Quando è troppo restrittivo, passa il valore a una funzione anonima, che può metterlo dove vuole:

La lambda è racchiusa tra parentesi e poi chiamata con (), così la regola 1 resta rispettata. Se ti ritrovi a farlo spesso in una stessa pipeline, probabilmente quel passaggio vuole diventare una funzione con un nome.

La pipe di magrittr %>%

Prima che il linguaggio avesse una pipe, ne forniva una il pacchetto magrittr, e %>% è diventato il marchio del codice tidyverse: caricare dplyr te lo dà automaticamente. Lo vedrai in quasi ogni tutorial e script dplyr scritto nell'ultimo decennio:

library(dplyr)

mtcars %>%
  filter(mpg > 30) %>%
  select(mpg, wt)

Dato che %>% viene da un pacchetto, questi esempi non si possono eseguire in una sessione R base: Error: could not find function "%>%" è esattamente ciò che ottieni se dimentichi library(dplyr) (o library(magrittr)).

Nel caso comune il comportamento è identico a |>: passare il valore precedente al primo argomento della chiamata successiva. Ma magrittr è più permissivo:

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

|> contro %>%: le differenze che contano

In una pipeline sul primo argomento, cioè la stragrande maggioranza del codice reale, le due sono intercambiabili. Le differenze stanno ai margini:

  • Disponibilità: |> è R base 4.1+; %>% ha bisogno di magrittr (di solito tramite dplyr).
  • Parentesi: |> pretende sqrt(); %>% accetta sqrt da solo.
  • Segnaposto: |> usa _, una volta, solo come argomento con nome (4.2+); %>% usa ., ovunque, quante volte vuoi, anche dentro chiamate annidate.
  • Velocità: |> viene riscritto durante l'analisi nella chiamata annidata, a costo zero. %>% è una normale chiamata di funzione con un piccolo costo aggiuntivo (in pratica trascurabile).

Niente di tutto questo rende %>% sbagliato: è collaudato e leggermente più flessibile. Ma tutto ciò che fa in più rispetto a |> si può fare con una lambda, e la pipe di base non ha bisogno di dipendenze.

Quale usare?

Per il codice nuovo: usa |>. Fa parte del linguaggio, funziona ovunque giri R 4.1+, non ha bisogno di pacchetti, e la sua severità è un pregio: le pipeline restano semplici oppure vengono riorganizzate in funzioni con un nome.

Ma devi saper leggere %>% con scioltezza, perché le basi di codice tidyverse, le risposte su Stack Overflow e la maggior parte dei libri su R scritti dal 2014 ne sono pieni. I team con uno stile dplyr già consolidato spesso tengono %>% per coerenza, ed è una scelta ragionevole: la scelta peggiore è mescolare i due operatori nello stesso file. Qualunque dei due scrivi, una convenzione vale per entrambi: nelle pipeline lunghe, metti ogni passaggio sulla sua riga con la pipe a fine riga, così la ricetta si legge come un elenco di passaggi.

Lo stesso gusto vale qui come per la famiglia apply: le pipe servono per trasformazioni lineari, un passaggio dopo l'altro. Quando un calcolo si ramifica o ha bisogno due volte di risultati intermedi, assegna una variabile con un buon nome invece di forzare tutto in un'unica catena.

Cosa ti porti a casa

  • Una pipe passa il valore precedente alla chiamata successiva: x |> f() |> g() è g(f(x)), scritto nell'ordine in cui avviene.
  • |> è R base (4.1+): richiede le parentesi, punta al primo argomento, segnaposto _ con un argomento con nome (4.2+).
  • %>% è magrittr/tidyverse: nomi senza parentesi ammessi, segnaposto . ovunque, ma ha bisogno di un pacchetto.
  • Preferisci |> nel codice nuovo; leggi %>% ovunque senza battere ciglio.
  • Le pipe sono adatte alle ricette lineari; la logica che si ramifica merita variabili con un nome.

Prossimo passo: i pacchetti, da dove vengono davvero %>%, dplyr e le altre 20,000 estensioni di R.

Domande frequenti

Cosa significa %>% in R?

%>% è la pipe di magrittr: prende il valore che la precede e lo passa come primo argomento della chiamata che la segue, quindi x %>% f() %>% g() significa g(f(x)). Viene dal pacchetto magrittr e si carica automaticamente con dplyr e il tidyverse: non fa parte di R base.

Serve un pacchetto per usare |> in R?

No. |> è la pipe nativa, integrata in R base dalla versione 4.1: funziona in una normale sessione R senza installare niente. È %>% che ha bisogno di un pacchetto (magrittr, o qualsiasi pacchetto che lo riesporta, come dplyr).

Qual è la differenza tra |> e %>% in R?

Entrambe passano il valore precedente alla chiamata successiva. |> è di R base, richiede parentesi esplicite (x |> sqrt()) e usa il segnaposto _ solo con un argomento con nome. %>% ha bisogno di magrittr, accetta nomi di funzione senza parentesi (x %>% sqrt) e il suo segnaposto . può andare ovunque, quante volte vuoi. Nel caso comune del primo argomento il comportamento è identico.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA