Das Problem, das Pipes lösen
Eine Pipe nimmt den Wert davor und reicht ihn in den Funktionsaufruf danach - x |> f() bedeutet f(x). Das klingt nach einer trivialen Umschreibung, bis du drei oder vier Schritte verkettest. Ohne Pipe verschachteln sich mehrstufige Transformationen, und verschachtelte Aufrufe liest man rückwärts: Das Erste, was passiert, sitzt an der innersten Stelle.
Um round(sqrt(x), 1) zu lesen, beginnst du in der Mitte (x), arbeitest dich nach außen (sqrt, dann round) und behältst im Kopf, welches nachgestellte , 1) zu welchem Aufruf gehört. Drei Ebenen tief ist das lästig; fünf Ebenen tief, mit Data-Frame-Verben und je mehreren Argumenten, ist es wirklich schwer zu verfolgen. Die Alternative, zu der Leute greifen - jeden Zwischenschritt in tmp1, tmp2, tmp3 zu speichern -, verstopft den Arbeitsbereich mit Namen, die niemand braucht.
Eine Pipe schreibt die Kette in der Reihenfolge um, in der die Schritte tatsächlich passieren: nimm x, dann zieh die Wurzel, dann runde.
Die native Pipe |>
Seit R 4.1 ist die Pipe in die Sprache selbst eingebaut - kein Paket nötig. |> fügt den vorherigen Wert als erstes Argument des nächsten Aufrufs ein:
c(1, 4, 9, 16) |> sqrt() |> round(1) ist exakt round(sqrt(c(1, 4, 9, 16)), 1) - R schreibt das eine beim Parsen buchstäblich in das andere um, es gibt also null Laufzeit-Overhead. Aber nun liest sich der Code wie ein Rezept, von oben nach unten: die Daten, dann jede Transformation der Reihe nach. Das funktioniert mit jeder Funktion, nicht nur mit Datenwerkzeugen - mtcars |> head(3) ist durch und durch schlichtes Base R.
Beachte, wie gut Pipes zu Funktionen passen, deren erster Parameter "die Daten" ist: head, sort, unique, summary und jedes dplyr-Verb sind so entworfen - deshalb fühlen sich Pipelines in R so natürlich an.
Die Regeln von |>
Die native Pipe ist bewusst streng. Drei Regeln decken fast alles ab:
1. Der nächste Schritt muss ein Aufruf mit Klammern sein. x |> sqrt ist ein Syntaxfehler; du musst x |> sqrt() schreiben:
c(1, 4, 9) |> sqrt # Error: The pipe operator requires a function call as RHS
c(1, 4, 9) |> sqrt() # correct
2. Der Wert landet an erster Argumentposition. Zusätzliche Argumente, die du schreibst, bleiben dahinter stehen - x |> round(1) ist round(x, 1).
3. Um woanders zu landen, nutze den Platzhalter _ mit einem benannten Argument (R 4.2+). Klassischer Fall: lm nimmt zuerst eine Formel und dann die Daten, das Piping eines Data Frames hinein braucht also data = _:
Das _ darf einmal und nur als benanntes Argument auftauchen. Wenn das zu einschränkend ist, pipe in eine anonyme Funktion - die darf den Wert hinsetzen, wohin sie will:
Das Lambda wird in Klammern gesetzt und dann mit () aufgerufen - so bleibt Regel 1 erfüllt. Wenn du das in einer Pipeline häufig tust, will dieser Schritt vermutlich eine benannte Funktion werden.
Die magrittr-Pipe %>%
Bevor die Sprache eine Pipe hatte, lieferte das Paket magrittr eine, und %>% wurde zum Erkennungszeichen von Tidyverse-Code - dplyr zu laden gibt es dir automatisch. Du siehst es in nahezu jedem dplyr-Tutorial und jedem Skript des letzten Jahrzehnts:
library(dplyr)
mtcars %>%
filter(mpg > 30) %>%
select(mpg, wt)
Weil %>% aus einem Paket kommt, sind diese Beispiele in einer nackten R-Sitzung nicht ausführbar - Error: could not find function "%>%" ist genau das, was du bekommst, wenn du library(dplyr) (oder library(magrittr)) vergisst.
Für den üblichen Fall ist das Verhalten identisch zu |>: den vorherigen Wert in das erste Argument des nächsten Aufrufs geben. Aber magrittr ist großzügiger:
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
|> vs. %>%: die Unterschiede, die zählen
Für eine Erstes-Argument-Pipeline - die überwiegende Mehrheit echten Codes - sind die beiden austauschbar. Die Unterschiede liegen an den Rändern:
- Verfügbarkeit:
|>ist Base R ab 4.1;%>%braucht magrittr (meist über dplyr). - Klammern:
|>verlangtsqrt();%>%akzeptiert nacktessqrt. - Platzhalter:
|>nutzt_, einmal, nur als benanntes Argument (ab 4.2);%>%nutzt., überall, beliebig oft, auch in verschachtelten Aufrufen. - Geschwindigkeit:
|>wird beim Parsen in den verschachtelten Aufruf umgeschrieben - kostenlos.%>%ist ein regulärer Funktionsaufruf mit kleinem (praktisch vernachlässigbarem) Overhead.
Nichts davon macht %>% falsch - es ist kampferprobt und etwas flexibler. Aber alles, was es über |> hinaus tut, lässt sich mit einem Lambda erledigen, und die Base-Pipe braucht keine Abhängigkeit.
Welche solltest du nutzen?
Für neuen Code: nimm |>. Es ist Teil der Sprache, funktioniert überall, wo R 4.1+ läuft, braucht kein Paket, und seine Strenge liest sich als Feature - Pipelines bleiben einfach oder werden zu benannten Funktionen umgebaut.
Aber du musst %>% flüssig lesen können, denn Tidyverse-Codebasen, Stack-Overflow-Antworten und die meisten seit 2014 geschriebenen R-Bücher sind voll davon. Teams mit bestehendem dplyr-Stil behalten oft %>% der Konsistenz halber, und das ist eine vertretbare Entscheidung - die schlechteste Wahl ist, beide Operatoren in einer Datei zu mischen. Was auch immer du schreibst, eine Konvention gilt für beides: Setz in langen Pipelines jeden Schritt in eine eigene Zeile mit der Pipe am Zeilenende, damit sich das Rezept als Schrittliste liest.
Derselbe Geschmack gilt hier wie bei der apply-Familie: Pipes sind für lineare, Schritt-nach-Schritt-Transformationen. Verzweigt sich eine Berechnung oder braucht sie Zwischenergebnisse zweimal, weise lieber eine gut benannte Variable zu, statt alles durch eine Kette zu zwingen.
Was du mitnimmst
- Eine Pipe reicht den vorherigen Wert in den nächsten Aufruf:
x |> f() |> g()istg(f(x)), geschrieben in der Reihenfolge des Geschehens. |>ist Base R (ab 4.1): verlangt Klammern, zielt auf das erste Argument, Platzhalter_mit einem benannten Argument (ab 4.2).%>%ist magrittr/Tidyverse: nackte Namen erlaubt, Platzhalter.überall - aber es braucht ein Paket.- Bevorzuge
|>in neuem Code; lies%>%überall sonst ohne mit der Wimper zu zucken. - Pipes passen zu linearen Rezepten; verzweigte Logik verdient benannte Variablen.
Als Nächstes: Pakete - woher %>%, dplyr und die anderen 20.000 Erweiterungen von R tatsächlich kommen.
Häufig gestellte Fragen
Was bedeutet %>% in R?
%>% ist die magrittr-Pipe: Sie nimmt den Wert davor und reicht ihn als erstes Argument in den Aufruf danach, sodass x %>% f() %>% g() gleich g(f(x)) bedeutet. Sie stammt aus dem Paket magrittr und wird mit dplyr und dem Tidyverse automatisch geladen - sie ist nicht Teil von Base R.
Brauche ich ein Paket, um |> in R zu nutzen?
Nein. |> ist die native Pipe, seit Version 4.1 in Base R eingebaut - sie funktioniert in einer schlichten R-Sitzung ohne jede Installation. %>% ist die, die ein Paket braucht (magrittr oder etwas, das es re-exportiert, wie dplyr).
Was ist der Unterschied zwischen |> und %>% in R?
Beide reichen den vorherigen Wert in den nächsten Aufruf. |> ist Base R, verlangt explizite Klammern (x |> sqrt()) und nutzt den Platzhalter _ nur mit einem benannten Argument. %>% braucht magrittr, akzeptiert nackte Funktionsnamen (x %>% sqrt), und sein Platzhalter . darf überall und beliebig oft stehen. Für den üblichen Erstes-Argument-Fall ist das Verhalten identisch.