Neu im Mai 2021: Die Base R Pipe |> – zum Blogbeitrag
Was bedeutet die sonderbar anmutende Zeichenkombination %>% , die man seit ein paar Jahren häufig in R-Skripten findet? Woher kommt sie und wie können wir sie nutzen, um eleganteren und besser lesbaren R-Code zu schreiben?
R und moderne Kunst: René Magritte
R inspiriert uns mit %>% , wenigstens einen kurzen Abstecher in die moderne Kunst zu unternehmen. Eines der bekanntesten Bilder des belgischen Surrealisten René Magritte (1898 – 1967) heißt „Der Verrat der Bilder“:
Das Bild eines Gegenstandes ist nicht mit dem Gegenstand selbst zu verwechseln; einen gemalten Apfel kann ich nicht essen, und diese Pfeife („Das ist keine Pfeife“) nicht rauchen.
magrittr und %>%
Stefan Milton Bache und Hadley Wickham führten mit dem magrittr-Paket den „Pipe Operator“ %>% in die Programmiersprache R ein. Paket-Name und Logo verweisen deutlich auf den Künstler:
magrittr wird in dplyr eingebunden, sodass man in der täglichen Praxis in der Regel nicht direkt mit magrittr zu tun hat, sondern dplyr oder gleich das Metapaket tidyverse lädt mit library(dplyr) bzw. library(tidyverse) und dann den Pipe Operator nutzen kann.
Kleine Modifikation in der Bildunterschrift: Beim Paket heißt es „un“, also mit männlichem Artikel. Eine passendere deutsche Übersetzung in diesem Zusammenhang dürfte „Rohr“ sein (engl. pipe); die Idee erinnert an die alte Rohrpost:
- Nimm einen Datensatz (oder ein Daten-Objekt)
- Gib ihn mittels Rohrpost %>% an die nächste Zeile weiter
- Verkette so viele Funktionen, wie Du möchtest
Vorteil: Besser lesbarer Code; konkret: Man kann die für Base R manchmal typischen geschachtelten Klammerausdrücke auflösen, und man muss Zwischenergebnisse nicht in Objekten speichern.
Mit %>% geschachtelte Klammerausdrücke umgehen
Hier ein typischer Base-R-Code:
Der Datensatz albums (Datenquelle: tsort.info) enthält Daten über die Top 3000 Musikalben (ermittelt nach einem Punktesystem auf Basis von Chartplatzierungen). Wende ich die summary-Funktion auf die character-Variable albums$name (Albumtitel) an, erhalte ich eine recht wenig aussagekräftige Auswertung. Wandle ich die Variable hingegen in einen factor um, erhalte ich eine Häufigkeitsauszählung. Der Übersichtlichkeit halber zeige ich mit head(n=3) nur die ersten drei Ergebnisse. Vier Alben im Datensatz heißen „Unplugged“, ebenfalls vier „Up“, drei „Believe“.
Ungünstig: Der geschachtelte Klammerausdruck muss von rechts nach links gelesen werden; zudem sind die Funktion „head“ und der Parameter „n=3“ weit auseinander gerissen.
Mit dplyr’s Piping erreiche ich das gleiche Ergebnis – allerdings auf deutlich eleganterem Weg:
Hier wird intuitiv von oben nach unten gelesen. In die leeren Klammern wird jeweils das Objekt aus der vorigen Zeile eingesetzt. Ich gehe von der Variable albums$name aus, wandle sie in einen Faktor um, wende darauf die summary-Funktion an und lasse R per head(n = 3) die ersten drei Ergebnisse ausgeben.
Mit %>% Zwischenergebnisse nicht mehr in Objekten speichern
Im zweiten Beispiel zunächst wieder ein typischer Base R-Code:
Hier möchte ich Beatles-Alben heraussuchen, deren Titel möglichst weit hinten im Alphabet stehen. Zunächst wähle ich die ersten drei Variablen des Datensatz aus, dann filtere ich nach den Beatles, dann sortiere ich absteigend nach Albumtiteln und lasse die ersten sechs Ergebnisse in der Konsole ausgeben. Insgesamt neun Mal tippe ich dazu den Objektnamen test, den ich nicht weiter benötige und am Ende lösche. (Es ginge kürzer, z. B. kann man die ersten beiden Zeilen zusammenfassen und gleichzeitig Zeilen und Spalten auswählen. Ich wollte jeden Schritt einzeln demonstrieren.)
Die dplyr-Alternative mit dem Pipe Operator %>% sieht so aus:
Hier komme ich ganz ohne temporäre Objekte aus. Ausgehend vom Datensatz albums, der nicht verändert wird (es gibt keine Zuweisung, ich will lediglich eine Ausgabe in der Konsole erreichen), werden mit select() Variablen ausgewählt, mit filter() die Beatles-Alben herausgesucht, mit arrange(desc()) absteigend sortiert, und mit head() die ersten sechs Zeilen ausgegeben. Gleiches Ergebnis mit kompakterem Code, ohne die Arbeitsumgebung (Environment) mit zusätzlichen Objekten zu füllen.
Zudem erspart mir dplyr das Hantieren mit den eckigen Klammern für die Indizierung und das Nachdenken über die richtige Kommasetzung [Zeile, Spalte].
Neu im Mai 2021: Die Base R Pipe |> – zum Blogbeitrag
tidyverse vs. Base R
Der Pipe Operator %>% und generell die Pakete des tidyverse (zu denen dplyr zählt, daneben auch ggplot2, forcats, readr, stringr und andere) haben sich weitgehend in der R-Gemeinschaft durchgesetzt und erleichtern das Arbeiten mit R enorm. Es gibt allerdings auch Kritiker: Zum Beispiel …
- … der bekannte Buchautor zur Datenvisualisierung Nathan Yau (FlowingData) präferiert Base R gegenüber ggplot2;
- Norman Matloff, Autor u. a. von „The Art of R Programming“, nennt sich „TidyverseSkeptic“;
- WinVector (John Mount, Nina Zumel) sehen Aspekte des tidyverse kritisch und propagieren alternative Pakete, u. a. eine alternative Pipe mit dem wrapr-Paket.
Viel Freude mit elegantem R-Code – happy Piping %>% !
Literatur-Tipps:
R für Data Science: Daten importieren, bereinigen, umformen, modellieren und visualisieren (2. Aufl.)
R for Data Science: Import, Tidy, Transform, Visualize, and Model Data
R for Data Science (kurz: R4DS) kann man auch online lesen.
I could not find albums dataset. Please share the link
You can find the current version on tsort.info: https://tsort.info/music/faq_version_numbers.htm
I created a package that holds a slightly newer version than used in the video; the data is called now topalbums instead of albums.
You can get this pure data package like this:
# install.packages(„devtools“)
library(devtools)
install_github(„fjodor/chartmusicdata“)
It contains the following datasets from tsort.info and chart2000.com: topalbums, topsongs, charts, albums2000, songs2000. topalbums corresponds to the albums dataset in the video.
Hi Wolf, danke dir für den Post! Kurz, informativ, kreativ, und dann auch noch weiterführende Diskussionen verlinkt, klasse Beitrag.
Gruß, Jan