4  Daten umformen

4.1 Lernsteuerung

4.1.1 Standort im Lernpfad

Abb. Abbildung 1.2 zeigt den Standort dieses Kapitels im Lernpfad und gibt damit einen Überblick über das Thema dieses Kapitels im Kontext aller Kapitel.

4.1.2 Lernziele

  • Sie können folgende Verben des Datenjudo anwenden: arrange, filter, select, summarise, group_by, mutate.
  • Sie können R-Befehle mit der “Pfeife” verketten.

4.1.3 Benötigte R-Pakete

library(tidyverse)
library(easystats)

4.1.4 Benötigte Daten

mariokart <- read.csv("https://vincentarelbundock.github.io/Rdatasets/csv/openintro/mariokart.csv")

Übungsaufgabe 4.1 Importieren Sie den Datensatz mariokart. Probieren Sie alle vorgestellten Verben des Datenjudos aus, zunächst in dem Sie einfach den R-Code kopieren und selber ausführen.\(\square\)

4.1.5 Frag den Bot

👨‍🏫 Erkläre einem Fünfjährigen, warum Statistik nützlich ist.

🤖 Okay, hör zu. Stell dir vor, du hast viele Spielzeugautos und du willst herausfinden, welches Auto am meisten bespielt wird. Du kannst jeden Tag zählen, wie oft du jedes Auto benutzt hast. Aber das wäre sehr mühsam und könnte schnell durcheinander gebracht werden. Stattdessen kannst du mit Statistik eine einfachere Methode finden. Du kannst zum Beispiel eine Liste machen und jedes Auto mit einer bestimmten Farbe markieren. Dann kannst du jeden Tag einfach zählen, wie oft du jede Farbe verwendet hast. Am Ende kannst du herausfinden, welches Auto am meisten bespielt wurde, indem du siehst, welche Farbe am meisten verwendet wurde. Das ist ein Beispiel dafür, wie Statistik uns dabei helfen kann, Muster in großen Datensätzen zu finden und Entscheidungen zu treffen, ohne alles einzeln zu zählen.

Quelle: ChatGTP 3.5, 2023-02-09

4.1.6 Zum Einstieg

Vielleicht fordert Sie die Lehrkraft zu einem Einstiegsquiz auf, etwas mittels der Plattform antworte.jetzt.1 Alternativ überlegen Sie sich selber 10 Quiz-Aufgaben zum Stoff des letzten Kapitels.

4.2 Datenjudo

Definition 4.1 (Datenjudo) Mit Datenjudo meint man den Prozess der Aufbereitens, Umformens oder Zusammenfassen von Daten, sowohl für einzelne Beobachtungen (Zeilen einer Datentabelle) oder Variablen (Spalten einer Datentabelle) oder einer ganzen Datentabelle. \(\square\)

4.2.1 Die Wahrheit über Data Science

Denkt man an Data Science, stellt man sich coole Leute vor (in San Francisco oder Berlin), die an abgefahrenen Berechnungen mit hoch komplexen statistischen Modellen für gigantische Datenmengen basteln. Tatsächlich besteht ein großer Teil der Arbeit aus dem Aufbereiten von Daten.

4.2.2 Praxisbezug: Aus dem Alltag des Data Scientisten

Laut dem Harvard Business Review allerdings, verbringen diese Leute “80%” ihrer Zeit mit dem Aufbereiten von Daten (Bowne-Anderson, 2018).2 Ja: mit uncoolen Tätigkeiten wie Tippfehler aus Datensätzen entfernen oder die Daten überhaupt nutzbar und verständlich zu machen.

Das zeigt zumindest, dass das Aufbereiten von Daten a) wichtig ist und b) dass man allein damit schon weit kommen kann. Eine gute Nachricht ist (vielleicht), dass das Aufbereiten von Daten keine aufwändige Mathematik verlangt, stattdessen muss man ein paar Handgriffe und Kniffe kennen. Daher passt der Begriff Datenjudo vielleicht ganz gut. Kümmern wir uns also um das Aufbereiten bzw. Umformen von Daten, um das Datenjudo. 🔢🤹 \(\square\)

Beispiel 4.1 Beispiele für typische Tätigkeiten des Datenjudos sind:

  • Zeilen filtern (z. B. nur Studentis des Studiengangs X)
  • Zeilen sortieren (z. B. Studenten mit guten Noten in den oberen Zeilen)
  • Spalten wählen (z. B. 100 weitere Produkte ausblenden)
  • Spalten in eine Zahl zusammenfassen (z. B. Notenschnitt der 1. Klausur)
  • Tabelle gruppieren (z. B. Analyse getrennt nach Standorten)
  • Werte aus einer Spalte verändern oder neue Spalte bilden (z. B. Punkte in Prozent-Richtige umrechnen).
  • \(\square\)

4.2.3 Mach’s einfach

Es gibt einen (einfachen) Trick, wie man umfangreiche Datenaufbereitung elegant geregelt kriegt, klingt fast zu schön, um wahr zu sein (s. Abbildung 4.1).

Abbildung 4.1: Mach’s einfach. Made at imgflip.com, Meme Generator

Der Trick besteht darin, komplexe Operationen in mehrere einfache Teilschritte zu zergliedern3. Man könnte vom “Lego-Prinzip” sprechen, s. Abbildung 4.2. Im linken Teil von Abbildung 4.2 sieht man ein (recht) komplexes Gebilde. Zerlegt man es aber in seine Einzelteile, so sind es deutlich einfachere geometrische Objekte wie Dreiecke oder Quadrate (rechter Teil des Diagramms).

Abbildung 4.2: Das Lego-Prinzip

Damit Sie es selber einfach machen können, müssen Sie selber Hand anlegen. Importieren Sie daher den Datensatz mariokart, z.B. so:

mariokart <- read.csv("https://vincentarelbundock.github.io/Rdatasets/csv/openintro/mariokart.csv")

glimpse(mariokart)
## Rows: 143
## Columns: 13
## $ rownames    <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,…
## $ id          <dbl> 150377422259, 260483376854, 320432342985, 280405224677, 17…
## $ duration    <int> 3, 7, 3, 3, 1, 3, 1, 1, 3, 7, 1, 1, 1, 1, 7, 7, 3, 3, 1, 7…
## $ n_bids      <int> 20, 13, 16, 18, 20, 19, 13, 15, 29, 8, 15, 15, 13, 16, 6, …
## $ cond        <chr> "new", "used", "new", "new", "new", "new", "used", "new", …
## $ start_pr    <dbl> 0.99, 0.99, 0.99, 0.99, 0.01, 0.99, 0.01, 1.00, 0.99, 19.9…
## $ ship_pr     <dbl> 4.00, 3.99, 3.50, 0.00, 0.00, 4.00, 0.00, 2.99, 4.00, 4.00…
## $ total_pr    <dbl> 51.55, 37.04, 45.50, 44.00, 71.00, 45.00, 37.02, 53.99, 47…
## $ ship_sp     <chr> "standard", "firstClass", "firstClass", "standard", "media…
## $ seller_rate <int> 1580, 365, 998, 7, 820, 270144, 7284, 4858, 27, 201, 4858,…
## $ stock_photo <chr> "yes", "yes", "no", "yes", "yes", "yes", "yes", "yes", "ye…
## $ wheels      <int> 1, 1, 1, 1, 2, 0, 0, 2, 1, 1, 2, 2, 2, 2, 1, 0, 1, 1, 2, 2…
## $ title       <chr> "~~ Wii MARIO KART &amp; WHEEL ~ NINTENDO Wii ~ BRAND NEW …

Beispiel 4.2 Sie arbeiten immer noch bei dem großen Online-Auktionshaus. Mittlerweile haben Sie sich den Ruf des “Datenguru” erworben. Vielleicht weil Sie behauptet haben, Data Science sei zu 80% Datenjudo, das hat irgendwie Eindruck geschindet… Naja, jedenfalls müssen Sie jetzt mal zeigen, dass Sie nicht nur schlaue Sprüche draufhaben, sondern auch die Daten ordentlich abbürsten können. Sie analysieren dafür im Folgenden den Datensatz mariokart. Na, dann los.\(\square\)

4.3 Die Verben des Datenjudos

Im R-Paket {dplyr}, das wiederum Teil des R-Pakets {tidyverse} ist, gibt es eine Reihe von R-Befehlen, die das Datenjudo in eine Handvoll einfacher Verben runterbrechen.4 Die wichtigsten Verben des Datenjudos schauen wir uns im Folgenden an.

Wir betrachten dazu im Folgenden einen einfachen (Spielzeug-)Datensatz, an dem wir zunächst die Verben des Datenjudos vorstellen, s. Tabelle 4.1.

Tabelle 4.1: Ein einfacher Datensatz von schlichtem Gemüt
id name gruppe note
1 Anni A 2.7
2 Berti A 2.7
3 Charli B 1.7
Wichtig

Die Verben des Datenjudos wohnen im Paket {dyplr}, welches gestartet wird, wenn Sie library(tidyverse) eingeben. Falls Sie vergessen , das Paket {tidyverse} zu starten, dann funktionieren diese Befehle nicht.\(\square\)

Hinweis

Zur Erinnerung: In RStudio können Sie per Klick auf das kleine Tabellen-Icon im Bereich Environment die Tabellenansicht einer Tabelle öffnen, s. Kapitel 3.8.5. \(\square\)

4.3.1 Tabelle sortieren: arrange

Sortieren der Zeilen ist eine einfache, aber häufige Tätigkeit des Datenjudos, s. Abbildung 4.3.

Abbildung 4.3: Sinnbild für das Sortieren einer Tabelle mit arrange()

Beispiel 4.3 (Was sind die höchsten Preise?) Sie wollen mal locker anfangen. Daher stellen Sie sich folgende Frage: Was sind denn eigentlich die höchsten Preise, für die das Spiel Mariokart über den Online-Ladentisch geht? Die Spalte des Verkaufspreis heißt offenbar total_pr (s. Tabelle mariokart). In Excel kann die Spalte, nach der man die Tabelle sortieren möchte, einfach anklicken. Ob das in R auch so einfach geht?

arrange(mariokart, total_pr)

Übersetzen wir die R-Syntax ins Deutsche:

Hey R,
arrangiere (sortiere) `mariokart` nach der Spalte `total_pr`

Gar nicht so schwer.\(\square\)

Übrigens wird in arrange() per Voreinstellung aufsteigend sortiert. Setzt man ein Minus vor der zu sortierenden Spalte, wird umgekehrt, also absteigend sortiert:

mario_sortiert <- arrange(mariokart, -total_pr)

Übungsaufgabe 4.2 Sortieren Sie die Mariokart-Daten absteigend nach der Anzahl der beigelegten Lenkräder.\(\square\)

4.3.2 Zeilen filtern: filter

4.3.2.1 Nur bestimmte Zeilen behalten

Zeilen filtern bedeutet, dass man nur bestimmte Zeilen (Beobachtungen) behalten möchte, die restlichen Zeilen brauchen wir nicht, weg mit ihnen. Wir haben also ein Filterkriterium im Kopf, anhand dessen wir die Tabelle filern, s. Abbildung 4.4.

Abbildung 4.4: Sinnbild für das Filtern einer Tabelle mit filter()

Beispiel 4.4 (Ob ein Foto für den Verkaufspreis nützlich ist?) Als nächstes kommt Ihnen die Idee, mal zu schauen, ob Auktionen mit Photo der Ware einen höheren Verkaufspreis erzielen als Auktionen ohne Photo.

mariokart_neu <- filter(mariokart, stock_photo == "yes")
mariokart_neu

Sie filtern also die Tabelle so, dass nur diese Auktionen im Datensatz verbleiben, welche ein Photo haben, mit anderen Worten, Auktionen (Beobachtungen) bei denen gilt: stock_photo == TRUE.\(\square\)

4.3.2.2 Komplexeres Filtern

Angestachelt von Ihren Erfolgen möchten Sie jetzt komplexere Hypothesen prüfen: Ob wohl Auktionen von neuen Spielen und zwar mit Photo einen höheren Preis erzielen als die übrigen Auktionen?

Anders gesagt haben Sie zwei Filterkriterien im Blick: Neuheit cond und Photo stock_photo. Nur diejenigen Auktionen, die sowohl Neuheit als auch Photo erfüllen, möchten Sie näher untersuchen (Filtern mit dem logischen UND):

mario_filter1 <- filter(mariokart, stock_photo == "yes" & cond == "new")
mario_filter1

Hm. Was ist mit den Auktionen, die entweder über ein Photo verfügen oder auch neu sind, oder beides (Filtern mit dem logischen ODER)?

mario_filter2 <- filter(mariokart, stock_photo == "yes" | cond == "new")
mario_filter2

💡Zur Erinnerung: Logische Operatoren sind in Kapitel 3.9 erläutert.

Hier könnte man noch viele interessante Hypothesen prüfen, denken Sie sich und tun das auch …

Übungsaufgabe 4.3 Filtern Sie die Spiele mit nur einem Lenkrad und ohne Versandkosten.\(\square\)

Übungsaufgabe 4.4 Filtern Sie die Spiele mit nur einem Lenkrad, die einen überdurchschnittlichen Verkaufspreis erzielen. Tipp: Nutzen Sie die Funktion describe_distribution(name_der_tabelle), um den Mittelwert einer Variable des Datensatzes zu erfahren (diese Funktion wohnt im R-Paket easystats). \(\square\)

4.3.3 Spalten auswählen mit select

Eine Tabelle mit vielen Spalten kann schnell unübersichtlich werden. Da lohnt es sich, eine alte goldene Regel zu beachten: Mache die Dinge so einfach wie möglich, aber nicht einfacher. Wählen wir also nur die Spalten aus, die uns interessieren und entfernen wir die restlichen, s. Abbildung 4.5.

Abbildung 4.5: Sinnbild für das Auswählen von Spalten mit select()

Beispiel 4.5 (Fokus auf nur zwei Spalten) Ob wohl gebrauchte Spiele deutlich geringere Preise erzielen im Vergleich zu neuwertigen Spielen? Sie entschließen sich, mal ein Stündchen auf die relevanten Daten zu starren.

mario_select1 <- select(mariokart, cond, total_pr)
mario_select1

Aha (?)\(\square\)

Der Befehl select erwartet als Input eine Tabelle und gibt (als Output) eine Tabelle zurück - genau wie die meisten anderen Befehle des Datenjudos. Auch wenn Sie nur eine Spalte auswählen, bleibt es eine Tabelle, eben eine Tabelle mit nur einer Spalte.

select erlaubt Komfort; Sie können Spalten auf mehrere Arten auswählen, z.B.

select(mariokart, 1, 2)  # Spalte 1 und 2
select(mariokart, 2:5)  #  Spalten 2 *bis* 5 
select(mariokart, -1)  # Alle Spalte *aber nicht* Spalte 1

Übungsaufgabe 4.5 Wählen Sie die Spalten total_pr, cond sowie die zweite Spalte der Tabelle mariokart aus!5 \(\square\)

Vertiefte Informationen zum Auswählen von Spalten mit select findet sich hier.6

4.3.4 Spalten zu einer Zahl zusammenfassen mit summarise

So eine lange Spalte mit Zahlen – mal ehrlich: wer blickt da schon durch? Viel besser wäre es doch, die Spalte total_pr zu einer Zahl zusammenzufassen, das ist doch viel handlicher. Kurz entschlossen fassen Sie die Spalte total_pr, den Verkaufspreis, zum Mittelwert zusammen, s. Abbildung 4.6.

Abbildung 4.6: Spalten zu einer einzelnen Zahl zusammenfassen mit summaris()

Beispiel 4.6 (Was ist der mittlere Verkaufspreis?) Mit summarise, s. Listing 4.1, können wir den mittleren Verkaufspreis der Mariokart-Spiele berechnen.

Listing 4.1: Die R-Funktion summarise fasst einen Vektor z u einer Zahl zusammen
mariokart_mittelwert <- summarise(mariokart, preis_mw = mean(total_pr))
mariokart_mittelwert

Aha! Etwa 50€ erzielt so eine Auktion im Schnitt.\(\square\)

Übersetzen wir Listing 4.1 vom Errischen ins Deutsche:

🧑‍🎓 Hey R, fasse die Zeilen von total_pr aus mariokart zu einer Zahl zusammen, und zwar mit Hilfe des Mittelwerts. Die resultierende Tabelle nennen wir mariokart_mittelwert, sehr kreativ. Und die resultierende Spalte, die einzige inmariokart_mittelwert, nennen wirpreis_mw`.

Ein bisschen abstrakter gesprochen, fasst summarise also eine Spalte zu einer (einzelnen) Zahl zusammen, s. Gleichung 4.1.7 Auf welche Art zusammengefasst werden soll, z.B. anhand des Mittelwerts oder Maximalwerts, muss noch zusätzlich innerhalb von summarise angegeben werden.

\[\begin{array}{|c|} \hline \\ \hline \\ \\ \\ \\ \hline \end{array} \qquad \rightarrow \qquad \begin{array}{|c|} \hline \\ \hline \end{array} \tag{4.1}\]

Übungsaufgabe 4.6 Identifizieren Sie den höchsten Kaufpreis eines Mariokart-Spiels!8 \(\square\)

4.3.5 Tabelle gruppieren

Es ist ja gut und schön, zu wissen, was so ein Spiel im Schnitt kostet. Aber viel interessanter wäre es doch, denken Sie sich, zu wissen, ob die neuen Spiele im Schnitt mehr kosten als die alten? Ob R Ihnen so etwas ausrechnen kann?

🤖 Ich tue fast alles für dich. 🧡

Also gut, R, dann gruppiere die Tabelle, s. Abbildung 4.7.

Abbildung 4.7: Gruppieren von Datensätzen mit group_by()

Durch das Gruppieren wird die Tabelle in “Teiltabellen” - entsprechend der Gruppen - aufgeteilt. Das sieht man der R-Tabelle aber nicht wirklich an. Aber alle nachfolgenden Berechnungen werden für jede Teiltabelle einzeln ausgeführt.

Beispiel 4.7 (Mittlerer Preis pro Gruppe) Gruppieren alleine liefert Ihnen zwei (oder mehrere) Teiltabellen, etwa neue Spiele (Gruppe 1, new) vs. gebrauchte Spiele (Gruppe 2, used). Mit anderen Worten: Wir gruppieren anhand der Variable cond.

mariokart_gruppiert <- group_by(mariokart, cond)

Wenn Sie die neue Tabelle betrachte, sehen Sie wenig Aufregendes, nur einen Hinweis, dass die Tabelle gruppiert ist. Jetzt können Sie an jeder Teiltabelle Ihre weiteren Berechnungen vornehmen, etwa die Berechnung des mittleren Verkaufspreises.

summarise(mariokart_gruppiert, preis_mw = mean(total_pr))

Langsam fühlen Sie sich als Datenchecker … 🥷 🦹‍♀ \(\square\)

Übungsaufgabe 4.7  

Berechnen Sie den mittleren und maximalen Verkaufspreis getrennt für Spiele mit und ohne Foto!

mariokart_gruppiert_foto <- group_by(mariokart, stock_photo)
mariokart_verkaufspreis_foto <- summarise(mariokart_gruppiert_foto,
                                          total_pr_avg = mean(total_pr),
                                          total_pr_max = max(total_pr))

mariokart_verkaufspreis_foto

4.3.6 Spalten verändern mit mutate

Immer mal wieder möchte man Spalten verändern, bzw. deren Werte umrechnen, s. Abbildung 4.8.

Abbildung 4.8: Spalten verändern/neu berechnen mit mutate()

Beispiel 4.8 Der Hersteller des Computerspiels Mariokart kommt aus Japan; daher erscheint es Ihnen opportun für ein anstehendes Meeting mit dem Hersteller die Verkaufspreise von Dollar in japanische Yen umzurechnen. Nach etwas Googeln finden Sie einen Umrechnungskurs von 1:133.

mariokart2 <- mutate(mariokart, total_pr_yen = total_pr * 133)
mariokart2 <- select(mariokart2, total_pr_yen, total_pr)
mariokart2

Sicherlich werden Sie Ihre Gesprächspartner schwer beeindrucken.\(\square\)

Mit mutate berechnen Sie eine Spalte x (in einer Tabelle) neu. Die Funktion, die Sie in mutate benennen wird für jede Zeile der Spalte x angewendet.

Beispiel 4.9 (Beispiele für Funktionen für mutate) mutate eignet sich, z.B. um Spalten zu addieren, zu multiplizieren oder sonstwie zu transformieren (z.B. den Logarithmus anwenden oder den Mittelwert der Spalte von jeder Zeile abziehen). \(\square\)

Übungsaufgabe 4.8  

Rechnen Sie die Dauer der Auktionen von Tagen in Wochen um.

mariokart_duration_wochen <- 
  mutate(mariokart, duration_week = duration / 7)

mariokart_duration_wochen <-
   select(mariokart_duration_wochen, duration, duration_week)
mariokart_duration_wochen

Übungsaufgabe 4.9  

Rechnen Sie wieder die Dauer der Auktionen von Tagen in Wochen um, aber runden Sie die Wochen auf ganze Wochen.

mariokart_duration_wochen <- 
  mutate(mariokart, duration_week = duration / 7)

mariokart_duration_wochen_gerundet <-
  mutate(mariokart_duration_wochen, duration_week_gerundet = round(duration_week, digits = 0))

mariokart_duration_wochen_schmal <-
  select(mariokart_duration_wochen_gerundet, duration, duration_week, duration_week_gerundet)
mariokart_duration_wochen_schmal

🧟‍♀️ Statistik, wann braucht man schon sowas!?

👨‍🏫 Eigentlich nur dann, wenn man die Fakten gut verstehen will, sonst nicht.

4.3.7 Zeilen zählen mit count

Arbeitet man mit nominalskalierten Daten, ist (fast) alles, was man tun kann, das Zeilen zählen.9

Man könnte z.B. fragen, wie viele neue und wie viele alte Spiele in der Tabelle (Dataframe) mariokart vorhanden sind.

Beispiel 4.10 Nach der letzten Präsentation Ihrer Analyse hat Ihre Chefin gestöhnt: “Oh nein, alles so kompliziert. Statistik! Himmel hilf! Kann man das nicht einfacher machen?” Anstelle von irgendwelchen komplizierten Berechnungen (Mittelwert?) möchten Sie ihr beim nächsten Treffen nur zeigen, wie viele Computerspiele neu und wie viele gebraucht sind (in Ihrem Datensatz). Schlichte Häufigkeiten also. Hoffentlich ist Ihre Chefin nicht wieder überfordert…

mariocart_counted <- count(mariokart, cond)
mariocart_counted

Aha! Es gibt mehr gebrauchte als neue Spiele.\(\square\)

Jetzt könnte man noch den Anteil (engl. proportion) ergänzen: Welcher Anteil (der 143 Spiele in mariokart) ist neu, welcher gebraucht?

mutate(mariocart_counted, Anteil = n / sum(n))

Übungsaufgabe 4.10 Zählen Sie Sie, wie viele Auktionen ein Foto enthalten.10 \(\square\)

Übungsaufgabe 4.11 Zählen Sie Sie, wie viele Auktionen ein Foto enthalten – innerhalb der gebrauchten Spiele und innerhalb der neuen Spiele. Anders gesagt: Teilen Sie den Datensatz sowohl nach Zustand als auch nach Foto auf und zählen Sie jeweils, wie viele Spiele/Auktionen in die jeweilige Gruppe gehören.11 \(\square\)

4.3.8 Fazit: Verben am Fließband

die Befehle (“Verben”) des Tidyverse sind jeweils für einzelne, typische Aufgaben des Datenaufbereitens (“Datenjudo”) zuständig.

Typischerweise erwarten diese Befehle eine Tabelle (▥) als Input und liefern eine Tabelle aus Output zurück, s. Abbildung 4.9.

flowchart LR
  A["▥"] --> B[tidyverse-Befehl] --> C["▥"] 
Abbildung 4.9: Tidyverse-Befehle erwarten normalerweise eine Tabelle (tibble) als Input und geben auch eine Tabelle zurück als Output

4.4 Die Pfeife

🚬 👈 Das ist keine Pfeife, wie René Magritte 1929 in seinem berühmten Bild schrieb, s. Abbildung 4.10.12

(a) Das ist keine Pfeife. Sondern ein Bild einer Pfeife.

%>%

|>

Abbildung 4.10: So sieht die Pfeife in R aus13. Links: Ein Bild einer Pfeife. Mitte und Rechts: Die zwei R-Symbole für eine “Pfeife” (pipe).

4.4.1 Russische Puppen

Computerbefehle, und im Speziellen R-Befehle kann man “aufeinander” – oder vielmehr: ineinander – stapeln, so ähnlich wie eine russische Puppe (vgl. Kapitel 3.7.3). Schauen wir uns das in einem Beispiel an. Dazu definieren wir zuerst einen Vektor x aus drei Zahlen:

x <- c(1, 2, 3)

Und dann kommt unser verschachtelter Befehl:

sum(x - mean(x))
## [1] 0

Wie schon erwähnt, arbeitet R so einen “verschachtelten” Befehl von innen nach außen ab:

Start: sum(x - mean(x))

  ⬇️ 

Schritt 1: sum(x - 2)

  ⬇️ 

Schritt 2: sum(-1, 0, 1)

  ⬇️ 

Schritt 3: 0. Fertig. Puh. Kompliziert.

Soweit kann man noch einigermaßen folgen. Aber das Verschachteln kann man noch extremer machen, dann wird’s wild. Schauen Sie sich mal folgende (Pseudo-)Syntax an:14

Listing 4.2: Eine wild verschachtelte Sequenz von R-Befehlen
fasse_zusammen(gruppiere(wähle_spalten(filter_zeilen(meine_daten))))

🤯

4.4.2 Die Pfeife zur Rettung

Listing 4.2 ist schon harter Tobak, was für echte Fans. Wäre es nicht einfacher, man könnte Listing 4.2 wie folgt schreiben:

Nimm "meine_daten" *und dann*
  filter gewünschte Zeilen *und dann*
  wähle gewünschte Spalten *und dann*
  teile in Subgruppen *und dann*
  fasse sie zusammen.

Definition 4.2 (Pfeife) “Und dann” heißt auf Errisch %>% oder |>. Man nennt diesen Befehl “Pfeife” (engl. pipe). \(\square\)

Hinweis

Der Befehl %>% verknüpft Befehle. Der Shortcut für diesen Befehl ist Strg-Shift-M. Die Pfeife %>% “wohnt” im Paket {tidyverse}.15

Mittlerweile16 ist auch im Standard-R eine Pfeife eingebaut. die sieht so aus: |>. Die eingebaute Pfeife funktioniert praktisch gleich zur anderen Pfeife %>%, hat aber den Vorteil, dass Sie nicht {tidyverse} starten müssen. Da wir {tidyverse} aber sowieso praktisch immer starten werden, bringt es uns keinen Vorteil, die neuere Pfeife des Standard-R |> zu verwenden.17

flowchart TD
  A["meine Daten 🗳"] --filter_zeilen-->B["▥"] 
  B --wähle_spalten--> C["▥"]
  C --gruppiere--> D["▥"]
  D --fasse_zusammen--> E["▥ Fertig. 🤩"]

Abbildung 4.11: Illustration für eine Pfeifensequenz, es geht vorwärts wie am Fließband.

Und jetzt kommt’s: So eine Art von Befehls-Verkettung gibt es in R. Schauen Sie sich mal Listing 4.3 an:

Listing 4.3: Eine Pfeifen-Befehlssequenz (Pseudo-Syntax)
meine_daten %>%
  filter_gewünschte_zeilen() %>%
  wähle_gewünschte_spalten() %>%
  gruppiere() %>%
  fasse_zusammen() 

So eine Pfeifen-Befehlsequenz ist ein wie ein Fließband, an dem es mehrere Arbeitsstationen gibt, s. Abbildung 4.11. Unser Datensatz wird am Fließband von Station zu Station weitergereicht und an jeder Stelle weiterverarbeitet.

So könnte Ihre “Pfeifen-Sequenz” aussehen:

# Hey R:
mariokart %>%   # nimm die Tabelle "mariokart" und dann...
  filter(total_pr < 100) %>%  # filter nur die günstigen Spiele und dann...
  select(cond, total_pr) %>%  # wähle die zwei Spalten und dann ...
  group_by(cond) %>%  # gruppiere die Tabelle nach Zustand des Spiels und dann ...
  summarise(total_pr_mean = mean(total_pr))  # fasse beide Gruppen nach dem mittleren Preis zusammen
Wichtig

Die Syntax filter(mariokart, total_pr < 100) und die Syntax mariokart |> filter(total_pr < 100) sind identisch.

Allgemeiner: d |> f(x) = f(d, x).

4.5 Beispiele für Forschungsfragen

Übungsaufgabe 4.12 Bevor Sie die Lösungen der folgenden Fallbeispiele lesen, versuchen Sie die Aufgaben selber zu lösen. Ja, ich weiß, es ist hart, nicht gleich auf die Lösungen zu schauen! \(\square\)

Sie arbeiten als Diener strategischer Assistent der Geschäftsführerin und sind für Faktenchecks und andere Daten-Aufgaben zuständig. Heute sollen Sie zeigen, was Sie können (Schluck).

4.5.1 Forschungsfrage 1

️👩 Ich würde von Ihnen gerne wissen, was das teuerste Spiel ist, aber jeweils für neue und gebrauchte Spiele. Aber nur für Spiele, die mit Foto verkauft wurden!

mariokart %>% 
  filter(stock_photo == "yes") %>% 
  group_by(cond) %>% 
  summarise(total_pr_max = max(total_pr))

Die Funktion max liefert den größten Wert eines Vektors zurück:

x <- c(1, 2, 10)
max(x)
## [1] 10

4.5.2 Forschungsfrage 2

️👩️ Ich würde gerne die mittlere Versandpauschale wissen, aber getrennt nach Anzahl der Lenkräder, die dem Spiel beigelegt sind. Und ich will nur Gruppen berücksichtigen, die aus mindestens 10 Spielen bestehen!

Wenn wir die Anzahl der Spiele zählen in Abhängigkeit der beigelegten Lenkräder (wheels), bekommen wir eine Tabelle mit zwei Spalten: wheels und n. n zählt, wie viele Spiele (Zeilen) in der jeweiligen Gruppe (“Teiltabelle”) von wheels sind.

mariokart %>%
  count(wheels)

Aus dieser Tabellet sehen wir, dass 3 oder 4 Lenkräder nur selten (2 bzw. 1 Mal) beigelegt wurden und wir solche Spiele herausfiltern sollten, bevor wir den Mittelwert der Versankosten ausrechnen:

mariokart %>%
  filter(wheels < 3) %>% 
  group_by(wheels) %>% 
  summarise(mittlere_versandkosten = mean(ship_pr),
            anzahl_spiele = n())

Die Funktion n() gibt die Anzahl der Zeilen pro Teiltabelle zurück.

4.5.3 Forschungsfrage 3

️👩️ Ich würde gerne den Verkaufspreis in Yen wissen, nicht in Euro. Dann rechne mal den mittleren Verkaufspreis aus und ziehe 10% ab, die wir als Provision unseren Verkäufern zahlen müssen.

mariokart %>% 
  select(total_pr) %>% 
  mutate(total_pr_yen = total_pr * 133) %>% 
  summarise(preis_yen_mw = mean(total_pr_yen),
            preis_yen_mw_minus_10proz = preis_yen_mw - 0.1*preis_yen_mw)

Wie man sieht kann man in summarise auch mehr als eine Berechnung einstellen. In diesem Fall haben wir zwei Berechnungen angestellt: Einmal den Mittelwert und einmal den Mittelwert minus 10% (des Mittelwerts).

Übungsaufgabe 4.13 (Do It Yourself) Denken Sie sich selber ähnliche Forschungsfragen aus. Stellen Sie diese einer vertrauenswürdigen Kommilitonen bzw. einem vertrauenswürdigen Kommilitonen. DIY! Schauen Sie, ob Ihre Aufgabe richtig gelöst wird. \(\square\)

4.6 Praxisbezug

Die Covid19-Epidemie hatte weltweit massive Auswirkungen; auch psychologischer Art wie Vereinsamung, Angst oder Depression. Eine Studie, die die psychologischen Auswirkungen von Mulukom et al. (2020), die unter der Projekt-ID tsjnb bei der Open Science Foundation (OSF), <https://osf.io/tsjnb/>, angemeldet ist. Die Daten wurden mit R ausgewertet. Beispielhaft ist unter https://osf.io/4b9p2 die R-Syntax zu sehen, die die Autoren zur Datenaufbereitung verwendet haben. Einen guten Teil dieser Syntax kennen Sie aus diesem Kapitel. Diese Studie ist, neben einigen vergleichbaren, ein schönes Beispiel, wie Forschung und Praxis ineinander greifen können: Angewandte Forschung als Beitrag zur Lösung eines akuten Problems, der Corona-Pandemie.

4.7 Wie man mit Statistik lügt

Ein (leider) immer mal wieder zu beobachtender “Trick”, um Daten zu frisieren ist, nur die Daten zu berichten, die einem in den Kram passen.

Beispiel 4.11 Ei Analysti 🧑‍ möchte zeigen, dass der Verkaufspreis von Mariokart-Spielen “viel zu niedrig” ist. Es muss ein höherer Wert rauskommen, findet dis Analysti. Der mittlere Verkaufspreis (im Datensatz mariokart) liegt bei 50 Euro.

🧑‍ Kann man den Wert nicht … “kreativ verbessern”? Ein paar Statistik-Tricks anwenden?

Um dieses Ziel zu erreichen, teilt dis Analysti den Datensatz in Gruppen nach Anzahl der dem Spiel beigelegten Lenkräder (wheels). Dann wird der Mittelwert pro Gruppe berechnet.

mariokart_wheels <- 
mariokart %>% 
  group_by(wheels) %>% 
  summarise(pr_mean = mean(total_pr),
            count_n = n())  # n() gibt die Anzahl der Zeilen pro Gruppe an

mariokart_wheels

Schließlich berechnet unser Analysti den ungewichteten Mittelwert über diese 5 Gruppen:

mariokart_wheels %>% 
  summarise(mean(pr_mean))

Und das Ergebnis lautet: 56 Euro! Das ist doch schon etwas “besser” als 50 Euro.

Natürlich ist es falsch und irreführend, hier einen ungewichteten Mittelwert zu berechnen. Der gewichtete Mittelwert würde wiederum zum korrekten Ergebnis, 50 Euro, führen.\(\square\)

4.8 Fallstudien

4.8.1 Die Pinguine

Abbildung 4.12: Possierlich: Die Pinguine

Übungsaufgabe 4.14 Machen Sie sich zunächst mit dem Pinguin-Datensatz vertraut. Fokussieren Sie sich auf die Zielvariable Gewicht. \(\square\)

Die folgende Datenapp ermöglicht Ihnen, die Verteilung des Körpergewichts zu betrachten, wobei sie die Pinguin-Spezies filtern können sowie eine Mindestlänge des Schnabels verlangen können.

Bearbeiten Sie die Fallstudie zu Pinguinen von Allison Horst.18 Sie können die Teile auslassen, die Themen beinhalten, die nicht in diesem Kapitel vorgestellt wurden.

4.8.2 Weitere Fallstudien

Diese Fallstudie hat die Analyse von Flugverspätungen zum Thema.

Studie COVIDiSTTRESS https://osf.io/z39us/

The COVIDiSTRESS global survey is an international collaborative undertaking for data gathering on human experiences, behavior and attitudes during the COVID-19 pandemic. In particular, the survey focuses on psychological stress, compliance with behavioral guidelines to slow the spread of Coronavirus, and trust in governmental institutions and their preventive measures, but multiple further items and scales are included for descriptive statistics, further analysis and comparative mapping between participating countries. Round one data collection was concluded May 30. 2020. To gather comparable data swiftly from across the globe, when the Coronavirus started making a critical impact on societies and individuals, the collaboration and survey was constructed as an urgent collaborative process. Individual contributors and groups in the COVIDiSTRESS network (see below) conducted translations to each language and shared online links by their own best means in each country.

Die Daten stehen unter https://osf.io/z39us zur freien Verfügung. Sie können diese echten Daten eigenständig analysieren. Diese Datei beinhaltet die finalen, aufbereiteten Daten. Achtung: Die Datei ist recht groß, ca. 90 MB.

4.9 Aufgaben

ChatGPT

Nutzen Sie einen Chat-Bot wie ChatGPT, um sich Hilfe für die R-Syntax geben zu lassen. \(\square\)

Die Webseite datenwerk.netlify.app stellt eine Reihe von einschlägigen Übungsaufgaben bereit. Sie können die Suchfunktion der Webseite nutzen, um die Aufgaben mit den folgenden Namen zu suchen:

  1. wrangle3

  2. wrangle4

  3. wrangle5

  4. wrangle7

  5. wrangle9

  6. wrangle10

  7. tidydata1

  8. affairs-dplyr

  9. dplyr-uebersetzen

  10. haeufigkeit01

  11. mariokart-mean1

  12. mariokart-mean2

  13. mariokart-mean3

  14. mariokart-mean4

  15. mariokart-max1

  16. mariokart-max2

  17. filter01

  18. affairs-dplyr

  19. summarise01

  20. summarise02

  21. mutate01

  22. wrangle3

4.10 Vertiefung

4.10.1 Tidydatatutor

Die Verben des Datenjudos werden beim “Tidydatatutor” anschaulich illustriert.19

4.10.2 Fortgeschrittenes R

Hinweis

In weiterführendem Material werden Sie immer wieder auf Inhalte treffen, die Sie noch nicht kennen, die etwa noch nicht im Unterricht behandelt wurden. Seien Sie unbesorgt: In der Regel können Sie diese Inhalte einfach auslassen, ohne den Anschluss zu verlieren. Einfach ignorieren. 😄

Häufig ist es nützlich, die Werte einer Variablen umzukodieren, z.B. “weiblich” in “w” oder in 0. Eine gute Möglichkeit, dies in R umzusetzen, bietet der Befehl case_when(); der Befehl wohnt im Tidyverse. Hier - und an vielen weiteren Stellen im Internet - finden Sie ein Tutorium.20. Im Datenwerk finden Sie dazu Übungen, etwa mutate03

4.10.3 Hilfe?! Erbie!

R will nicht, so wie Sie wollen? Sie haben das Gefühl, R verweigert störrisch den Dienst, vermutlich rein aus Boshaftigkeit, rein um Sie zu ärgern? Ausführliches Googeln und ChatGPT befragen hat keine Lösung gebracht? Kurz, Sie brauchen die Hilfe eines kundigen Menschens?21

Hier finden Sie eine Anleitung, wie man seinen Hilfeschrei so formuliert (ruft), dass er nicht nur gehört, sondern auch verstanden wird und einen anderen Menschen veranlasst und ermöglicht Ihnen zu helfen.22

Also: Sie müssen Ihr Problem nachvollziehbar aber prägnant formulieren. Das nennt man auch ein ERBie, ein einfaches, reproduzierbare Beispiel Ihres Problems mit (R-)Syntax:

  • einfach: die einfachste Syntax, die Ihr Problem bzw. die Fehlermeldung produziert. Es bietet sich an, einen einfachen, allgemein bekannten Datensatz zu verwenden, etwa mtcars
  • reproduzierbar: Code (z.B. als Textdatei oder in einem Post), der die Fehlermeldung entstehen lässt

Beispiel 4.12 (Beispiel für ein Erbie) Problem: Ich verstehe nicht, warum eine Fehlermeldung kommt

Ziel: Ich möchte die Automatikautos filtern (am = 0)

Was ich schon versucht habe: Ich habe folgende Posts gelesen …, aber ohne Erfolg

Erbie:

data(mtcars)
library(dplyr)  # nicht "tidyverse", denn "dplyr" reicht

mtcars %>% 
  filter(am = 0)  # den kürzesten Code, der Ihren Fehler entstehen lässt!
## Error in `filter()`:
## ! We detected a named input.
## ℹ This usually means that you've used `=` instead of `==`.
## ℹ Did you mean `am == 0`?

sessionInfo()  # gibt Infos zur R-Version etc. aus
## R version 4.2.1 (2022-06-23)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Big Sur ... 10.16
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices datasets  utils     methods   base     
## 
## other attached packages:
##  [1] see_0.8.3          report_0.5.8       parameters_0.21.6  performance_0.11.0
##  [5] modelbased_0.8.7   insight_0.19.10    effectsize_0.8.7   datawizard_0.10.0 
##  [9] correlation_0.8.4  bayestestR_0.13.2  easystats_0.7.1    lubridate_1.9.3   
## [13] forcats_1.0.0      stringr_1.5.1      dplyr_1.1.4        purrr_1.0.2       
## [17] readr_2.1.5        tidyr_1.3.1        tibble_3.2.1       tidyverse_2.0.0   
## [21] gt_0.10.0          patchwork_1.2.0    png_0.1-8          ggpubr_0.6.0      
## [25] ggplot2_3.5.0      knitr_1.45        
## 
## loaded via a namespace (and not attached):
##  [1] digest_0.6.35     utf8_1.2.4        R6_2.5.1          backports_1.4.1  
##  [5] evaluate_0.23     pillar_1.9.0      rlang_1.1.3       rstudioapi_0.15.0
##  [9] car_3.1-2         rmarkdown_2.26    htmlwidgets_1.6.4 munsell_0.5.0    
## [13] broom_1.0.5       compiler_4.2.1    xfun_0.42         pkgconfig_2.0.3  
## [17] htmltools_0.5.7   tidyselect_1.2.0  codetools_0.2-19  fansi_1.0.6      
## [21] tzdb_0.4.0        withr_3.0.0       grid_4.2.1        jsonlite_1.8.8   
## [25] gtable_0.3.4      lifecycle_1.0.4   magrittr_2.0.3    scales_1.3.0     
## [29] cli_3.6.2         stringi_1.8.3     carData_3.0-5     renv_1.0.2       
## [33] ggsignif_0.6.4    xml2_1.3.6        generics_0.1.3    vctrs_0.6.5      
## [37] tools_4.2.1       glue_1.7.0        hms_1.1.3         abind_1.4-5      
## [41] fastmap_1.1.1     yaml_2.3.8        timechange_0.2.0  colorspace_2.1-0 
## [45] rstatix_0.7.2

Mit dem Paket {reprex} kann man sich R-Syntax schön formuliert ausgeben lassen. Das ist perfekt, um den Code dann in einem Forum (oder Mail) einzustellen. Dafür müssen Sie nur den Code auswählen, Strg-C drücken und dann reprex::reprex ausführen. Mit Strg-V können Sie die schön formatierte Syntax (sowie die Ausgabe, auch schön formatiert) dann irgendwohin pasten.

via GIFER

Tipp

Posten Sie Ihr Erbie bei https://gist.github.com/ als “public gist”. Hier ist ein Beispiel.\(\square\)

4.10.4 Zertifikate und Online-Kurse

Sie können zu den Inhalten dieses Kapitels Zertifikate erwerben (teilweise kostenlos), indem Sie einen Online-Kurs absolvieren, bei z.B. folgenden Anbietern23:

4.11 Exkurs

Dall-E 2 ist eine KI, die “realistische Bilder und Kunst aus einer Beschreibung in natürlicher Sprache” erstellt.24

👨‍🏫 I’d like a mixture between robot und professor, in oil painting

🤖 … s. Abbildung 4.13

Abbildung 4.13: Bild erzeugt von künstlicher Intelligenz, Quelle: DALL-E 2, 2023-02-09
Hinweis

Der Nutzen künstlicher Intelligenz für die Datenanalyse ist natürlich breiter: Wenn Sie sich z.B. über die Syntax eines bestimmten Befehls (oder allgemeiner: Vorhabens) nicht sicher sind, fragen Sie sich doch mal einen Bot wie ChatGPT.

4.12 Literaturhinweise

Sauer (2019), Kap. 7, gibt eine Einführung in die Datenaufbereitung (mit Hilfe von R), ähnlich zu den Inhalten dieses Kapitels. Mehr in die Tiefe des “Datenjudo” führen Wickham & Grolemund (2018); der Autor Hadley Wickham ist die Leitfigur in der R-Community schlechthin. Kap. 5 behandelt (etwas ausführlicher) die Themen dieses Kapitels. Er ist einer der Hauptautoren von den beliebten R-Paketen dplyr und ggplot2.

Wer sich tiefer in das Datenjudo mit dem Tidyverse einarbeiten möchte, dem sei z.B. dieser Kurs empfohlen.25

4.13 Literatur


  1. https://antworte.jetzt/↩︎

  2. https://hbr.org/2018/08/what-data-scientists-really-do-according-to-35-data-scientists↩︎

  3. Genau darin besteht das Wesen einer Analyse: die Zerlegung eines Objekts in seine Bestandteile.↩︎

  4. Falls Sie das R-Paket {tidyverse} noch nicht installiert haben sollten, wäre jetzt ein guter Zeitpunkt dafür.↩︎

  5. select(mariokart, total_pr, cond, 2)↩︎

  6. https://tidyr.tidyverse.org/reference/tidyr_tidy_select.html↩︎

  7. Eine Alternative, um eine Spalte zu einer Zahl zusammenzufassen, bietet der “Dollar-Operator” ($): mean(mariokart$total_pr). Der Dollar-Operator trennt hier die Tabelle von der Spalte: tibble$spalte. Im Gegensatz zu den Verben des Tidyverse (die immer einer Tabelle zurückliefern), liefert der Dollar-Operator einen Vektor (Spalte) zurück. (Diese wird von mean dann zu einer einzelnen Zahl zusammengefasst.)↩︎

  8. summarise(mariokart, hoechster_preis = max(total_pr))↩︎

  9. Ja, das ist traurig.↩︎

  10. count(mariokart, stock_photo)↩︎

  11. count(mariokart, stock_photo, cond)↩︎

  12. Vgl. https://en.wikipedia.org/wiki/The_Treachery_of_Images↩︎

  13. Jaja, das ist keine Pfeife, sondern ein Symbol einer Pfeife…↩︎

  14. Ein beliebter Fehler ist es übrigens, nicht die richtige Zahl an schließenden Klammern hinzuschreiben, z.B. fasse_zusammen(gruppiere(wähle_spalten(filter_zeilen(meine_daten)))) FALSCHE ZAHL AN KLAMMERN.↩︎

  15. Genauer gesagt im Paket {magrittr}, welches aber under the hood von {tidyverse} geladen wird. Also nichts, um dass Sie sich kümmern müssten.↩︎

  16. Seit R 4.1↩︎

  17. Aber auch keinen Nachteil. Unter Tools > Global Options… können Sie einstellen, dass der Shortcut Strg-Shift-M die eingebaute Pfeife verwendet.↩︎

  18. https://allisonhorst.shinyapps.io/dplyr-learnr/#section-welcome↩︎

  19. <(https://tidydatatutor.com>↩︎

  20. https://www.statology.org/dplyr-case_when/↩︎

  21. https://www.youtube.com/watch?v=2Q_ZzBGPdqE↩︎

  22. https://data-se.netlify.app/2022/01/31/erbie-einfache-reproduzierbare-beispiele-ihres-problems-mit-r-syntax/↩︎

  23. Das ist keine Werbung für spezifische Anbieter und kein umfassender Überblick und keine Kaufempfehlung.↩︎

  24. <Dall-E 2>↩︎

  25. https://www.datacamp.com/courses/introduction-to-the-tidyverse↩︎