R Tutorial (Teil 4): Tidyverse

Veröffentlichungsdatum

22. April 2026

1 Wie funktioniert dieses Tutorial?

Dieses Tutorial ist für alle Studierenden gedacht, die bereits erste Grundlagen in R und RStudio kennengelernt haben und nun lernen möchten, wie man mit Datensätzen in R effizient arbeitet. In der Praxis reicht es meist nicht aus, Daten nur einzulesen und anzuschauen. Bevor man statistische Analysen durchführen kann, müssen Datensätze häufig gefiltert, sortiert, zusammengefasst oder um neue Variablen ergänzt werden. Genau dafür ist das tidyverse besonders nützlich.

Im Zentrum dieses Tutorials steht das Paket dplyr, das Teil des tidyverse ist. Mit dplyr können wir Datensätze sehr übersichtlich bearbeiten. Viele Arbeitsschritte, die in base R umständlich oder schwer lesbar wirken, lassen sich mit dplyr klar strukturieren. Das Ziel dieses Tutorials ist also nicht nur, dass Sie einzelne Befehle kennenlernen, sondern vor allem, dass Sie verstehen, wie man typische Datenmanipulationen in R systematisch aufbaut.

In allen unseren Lehrveranstaltungen werden Sie früher oder später Daten filtern, Variablen auswählen, Kennwerte gruppenweise berechnen oder neue Variablen erzeugen müssen. Das tidyverse ist dafür eines der am häufigsten verwendeten Werkzeuge. Es lohnt sich also sehr, diese Arbeitsweise früh einzuüben.

In diesem Tutorial werden wir Ihnen Schritt für Schritt die wichtigsten Grundlagen erläutern. Dabei werden Sie erneut verschiedene Kästen entdecken:

HinweisHinweis

In den blauen Kästen geben wir nützliche Hinweise, die die Arbeit mit tidyverse und dplyr genauer erläutern.

TippNützlich

In den grünen Kästen behandeln wir weiterführende Themen oder praktische Ergänzungen. Diese können Sie beim ersten Lesen bei Bedarf zunächst überspringen.

WarnungVorsicht

In den gelben Kästen weisen wir auf typische Fehlerquellen und Missverständnisse hin.

VorsichtProbieren Sie es aus!

In den orangen Kästen finden Sie kleine Übungsaufgaben. Bitte führen Sie diese selbst in RStudio aus.

Wie benutzen Sie dieses Tutorial?

Wie bei den bisherigen Tutorials gilt auch hier: Man lernt R nicht nur durch Lesen, sondern vor allem durch eigenes Ausprobieren. Es ist völlig normal, dass die neue Syntax am Anfang noch ungewohnt wirkt. Wichtig ist, dass Sie die einzelnen Schritte selbst ausführen und mehrfach wiederholen.

Besonders wichtig ist in diesem Tutorial, dass Sie die Struktur der Befehle verstehen. dplyr arbeitet sehr konsistent: Viele Funktionen haben einen ähnlichen Aufbau und lassen sich gut miteinander kombinieren. Wenn Sie also die Grundidee einmal verstanden haben, werden Ihnen viele spätere Anwendungen deutlich leichter fallen.

Bitte probieren Sie alle Beispiele selbst aus. Gerade bei Datenmanipulation gilt: Verstehen kommt durch Anwenden.

2 Erste Schritte mit tidyverse und dplyr

2.1 Was ist das tidyverse?

tidyverse ist eine Sammlung von R-Paketen, die für typische Schritte der Datenanalyse entwickelt wurden. tidyverse Dazu gehören unter anderem:

  • readr zum Einlesen von Daten
  • ggplot2 zur Visualisierung
  • tidyr zum Umformen von Datensätzen
  • dplyr zur Datenmanipulation

In diesem Tutorial konzentrieren wir uns vor allem auf dplyr. dplyr

Warum ist dplyr nützlich?

Viele Datenanalysen bestehen aus wiederkehrenden Schritten:

  • wir wollen nur bestimmte Zeilen behalten,
  • nur bestimmte Spalten auswählen,
  • neue Variablen berechnen,
  • Daten sortieren,
  • oder Kennwerte innerhalb von Gruppen berechnen.

Genau für solche Schritte wurde dplyr entwickelt. Die Befehle sind so aufgebaut, dass sie möglichst gut lesbar sind.

3 Set Up

3.1 Installation und Laden der Pakete

Falls das tidyverse auf Ihrem Computer noch nicht installiert ist, können Sie es mit folgendem Befehl einmalig installieren:

install.packages("tidyverse") #zentrales Package, mit dem wir arbeiten werden und verschiedene andere Packages wie dplyr beinhaltet

install.packages("palmerpenguins") #unser Beispieldatensatz

library(tidyverse)

3.2 Unser Beispieldatensatz: penguins

In diesem Tutorial verwenden wir den öffentlichen Datensatz penguins aus dem Paket palmerpenguins. Der Datensatz enthält Messungen an Pinguinen verschiedener Arten und Inseln und eignet sich gut für erste Übungen mit dplyr.

Hinweis

Hinweis

Gerade bei neuen Datensätzen ist es sinnvoll, sich zuerst einen Überblick zu verschaffen: Welche Variablen gibt es? Welche Datentypen liegen vor? Gibt es fehlende Werte?

Datensätze explorieren

Wir schauen uns den Datensatz zunächst an:

library(palmerpenguins)

Attaching package: 'palmerpenguins'
The following objects are masked from 'package:datasets':

    penguins, penguins_raw
penguins
# A tibble: 344 × 8
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 2 more variables: sex <fct>, year <int>

Für einen ersten Überblick sind auch die folgenden Befehle hilfreich:

str(penguins)
tibble [344 × 8] (S3: tbl_df/tbl/data.frame)
 $ species          : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ island           : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
 $ bill_length_mm   : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
 $ bill_depth_mm    : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
 $ flipper_length_mm: int [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
 $ body_mass_g      : int [1:344] 3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
 $ sex              : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
 $ year             : int [1:344] 2007 2007 2007 2007 2007 2007 2007 2007 2007 2007 ...
head(penguins)
# A tibble: 6 × 8
  species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
  <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
1 Adelie  Torgersen           39.1          18.7               181        3750
2 Adelie  Torgersen           39.5          17.4               186        3800
3 Adelie  Torgersen           40.3          18                 195        3250
4 Adelie  Torgersen           NA            NA                  NA          NA
5 Adelie  Torgersen           36.7          19.3               193        3450
6 Adelie  Torgersen           39.3          20.6               190        3650
# ℹ 2 more variables: sex <fct>, year <int>
summary(penguins)
      species          island    bill_length_mm  bill_depth_mm  
 Adelie   :152   Biscoe   :168   Min.   :32.10   Min.   :13.10  
 Chinstrap: 68   Dream    :124   1st Qu.:39.23   1st Qu.:15.60  
 Gentoo   :124   Torgersen: 52   Median :44.45   Median :17.30  
                                 Mean   :43.92   Mean   :17.15  
                                 3rd Qu.:48.50   3rd Qu.:18.70  
                                 Max.   :59.60   Max.   :21.50  
                                 NA's   :2       NA's   :2      
 flipper_length_mm  body_mass_g       sex           year     
 Min.   :172.0     Min.   :2700   female:165   Min.   :2007  
 1st Qu.:190.0     1st Qu.:3550   male  :168   1st Qu.:2007  
 Median :197.0     Median :4050   NA's  : 11   Median :2008  
 Mean   :200.9     Mean   :4202                Mean   :2008  
 3rd Qu.:213.0     3rd Qu.:4750                3rd Qu.:2009  
 Max.   :231.0     Max.   :6300                Max.   :2009  
 NA's   :2         NA's   :2                                 
library(summarytools)

#Wichtig: Dieser Output erscheint bei Ihnen im "Viewer"
view(dfSummary(penguins))

Mehr Infos zum Datensatz: link: penguins

4 dplyr basics

4.1 Der Pipe-Operator (“Dann”-Operator)

Ein zentrales Element des tidyverse ist der sogenannte Pipe-Operator:

%>%

Die Grundidee ist, dass das Ergebnis eines Schritts direkt an den nächsten Schritt weitergegeben wird.

Man kann %>% häufig lesen als “und dann…”

Datensatz %>% # und dann
  erster_Schritt() %>% # und dann
  zweiter_Schritt() %>% # und dann
  dritter_Schritt()

Durch diesen Pipe-Operator wird der Code oft deutlich lesbarer als bei stark verschachtelte Funktionen.

Warnung

Vorsicht

Die Pipe steht immer am Ende einer Zeile, nicht am Anfang der nächsten. Also richtig:

penguins %>%
  head()

und nicht:

penguins
%>% head()
VorsichtProbieren Sie es aus!

Lesen Sie das tidyverse oder dplyr

Führen Sie den folgenden Code aus:

penguins %>%
  summary()

Ein wichtiger Punkt: dplyr überschreibt per default nicht bestehende Objekte, wenn “Dann”-Schritte zum Einsatz kommen.

HinweisHinweis

Der Pipe-Operator wird zwar angewandt und wir sehen die modifizierten Informationen, aber bei allen Schritten bleibt das Start-Objekt (z.B. Datensatz) unverändert.

Wenn wir mehreren “Dann”-Schritten das modifizierte Objekt abspeichern wollen, müssen wir dies wie gewohnt über eine Zuweisung machen.

# Mit diesem Code wird uns zwar der aufbereitete Datensatz angezeigt, 
# aber das Objekt "Datensatz" bleibt in unserer Umgebung völlig angetastet
datensatz %>%
  aufbereitungschritt_01 %>%
  aufbereitungschritt_02 %>%
  aufbereitungschritt_03 

# Wollen wir den modifizierten Datensatz "speichern" können wir das 
# wie gewohnt tun:

datensatz_clean <- datensatz %>%
  aufbereitungschritt_01 %>%
  aufbereitungschritt_02 %>%
  aufbereitungschritt_03 

# In der Regel sollte man nicht ein Objekt in sich selber überschreiben.
# Das hier wäre ein "Anti-Pattern":

# datensatz <- datensatz %>%
#   aufbereitungschritt_01

4.2 Zeilen filtern

Mit filter() können wir Zeilen eines Datensatzes auswählen, die bestimmte Bedingungen erfüllen.

Wenn wir zum Beispiel nur die Pinguine der Art Adelie auswählen wollen, schreiben wir:

penguins %>%
  filter(species == "Adelie")
# A tibble: 152 × 8
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 142 more rows
# ℹ 2 more variables: sex <fct>, year <int>

Hier wird jede Zeile darauf geprüft, ob in der Spalte species der Eintrag “Adelie” steht. Achtung: Die Spaltennamen werdenby dplyr innerhalb der Klammer ohne das übliche penguins$... angegeben.

Mehrere Bedingungen

Wir können auch mehrere Bedingungen gleichzeitig verwenden, mit Komma getrennt. Zum Beispiel:

penguins %>%
  filter(species == "Adelie", island == "Dream")
# A tibble: 56 × 8
   species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>           <dbl>         <dbl>             <int>       <int>
 1 Adelie  Dream            39.5          16.7               178        3250
 2 Adelie  Dream            37.2          18.1               178        3900
 3 Adelie  Dream            39.5          17.8               188        3300
 4 Adelie  Dream            40.9          18.9               184        3900
 5 Adelie  Dream            36.4          17                 195        3325
 6 Adelie  Dream            39.2          21.1               196        4150
 7 Adelie  Dream            38.8          20                 190        3950
 8 Adelie  Dream            42.2          18.5               180        3550
 9 Adelie  Dream            37.6          19.3               181        3300
10 Adelie  Dream            39.8          19.1               184        4650
# ℹ 46 more rows
# ℹ 2 more variables: sex <fct>, year <int>

Das bedeutet: Behalte nur Zeilen, bei denen species gleich "Adelie" und gleichzeitig island gleich "Dream" ist. Für eine ODER Verknüpfung können wir den |Operator verwenden.

Wir können auch numerische Bedingungen verwenden:

penguins %>%
  filter(body_mass_g > 5000)
# A tibble: 61 × 8
   species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>           <dbl>         <dbl>             <int>       <int>
 1 Gentoo  Biscoe           50            16.3               230        5700
 2 Gentoo  Biscoe           50            15.2               218        5700
 3 Gentoo  Biscoe           47.6          14.5               215        5400
 4 Gentoo  Biscoe           46.7          15.3               219        5200
 5 Gentoo  Biscoe           46.8          15.4               215        5150
 6 Gentoo  Biscoe           49            16.1               216        5550
 7 Gentoo  Biscoe           48.4          14.6               213        5850
 8 Gentoo  Biscoe           49.3          15.7               217        5850
 9 Gentoo  Biscoe           49.2          15.2               221        6300
10 Gentoo  Biscoe           48.7          15.1               222        5350
# ℹ 51 more rows
# ℹ 2 more variables: sex <fct>, year <int>
WarnungVorsicht

Für logische Vergleiche in R verwenden wir == und nicht =.

== prüft, ob zwei Werte gleich sind, = weist einem Argument oder Objekt einen Wert zu.

Das ist derselbe Unterschied wie in den vorherigen Tutorials.

VorsichtProbieren Sie es aus!
  • Wählen Sie alle Pinguine aus, die auf der Insel "Biscoe" beobachtet wurden.
  • Wählen Sie alle Pinguine aus, deren Schnabellänge (bill_length_mm) größer als 45 mm ist.
  • Wählen Sie alle Pinguine aus, die zur Art "Gentoo" gehören und schwerer als 5500 g sind.
Lösung
penguins %>%
  filter(island == "Biscoe")
# A tibble: 168 × 8
   species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>           <dbl>         <dbl>             <int>       <int>
 1 Adelie  Biscoe           37.8          18.3               174        3400
 2 Adelie  Biscoe           37.7          18.7               180        3600
 3 Adelie  Biscoe           35.9          19.2               189        3800
 4 Adelie  Biscoe           38.2          18.1               185        3950
 5 Adelie  Biscoe           38.8          17.2               180        3800
 6 Adelie  Biscoe           35.3          18.9               187        3800
 7 Adelie  Biscoe           40.6          18.6               183        3550
 8 Adelie  Biscoe           40.5          17.9               187        3200
 9 Adelie  Biscoe           37.9          18.6               172        3150
10 Adelie  Biscoe           40.5          18.9               180        3950
# ℹ 158 more rows
# ℹ 2 more variables: sex <fct>, year <int>
penguins %>%
  filter(bill_length_mm > 45)
# A tibble: 165 × 8
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           46            21.5               194        4200
 2 Adelie  Torgersen           45.8          18.9               197        4150
 3 Adelie  Biscoe              45.6          20.3               191        4600
 4 Gentoo  Biscoe              46.1          13.2               211        4500
 5 Gentoo  Biscoe              50            16.3               230        5700
 6 Gentoo  Biscoe              48.7          14.1               210        4450
 7 Gentoo  Biscoe              50            15.2               218        5700
 8 Gentoo  Biscoe              47.6          14.5               215        5400
 9 Gentoo  Biscoe              46.5          13.5               210        4550
10 Gentoo  Biscoe              45.4          14.6               211        4800
# ℹ 155 more rows
# ℹ 2 more variables: sex <fct>, year <int>
penguins %>%
  filter(species == "Gentoo", body_mass_g > 5500)
# A tibble: 28 × 8
   species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>           <dbl>         <dbl>             <int>       <int>
 1 Gentoo  Biscoe           50            16.3               230        5700
 2 Gentoo  Biscoe           50            15.2               218        5700
 3 Gentoo  Biscoe           49            16.1               216        5550
 4 Gentoo  Biscoe           48.4          14.6               213        5850
 5 Gentoo  Biscoe           49.3          15.7               217        5850
 6 Gentoo  Biscoe           49.2          15.2               221        6300
 7 Gentoo  Biscoe           50.2          14.3               218        5700
 8 Gentoo  Biscoe           47.8          15                 215        5650
 9 Gentoo  Biscoe           50            15.3               220        5550
10 Gentoo  Biscoe           59.6          17                 230        6050
# ℹ 18 more rows
# ℹ 2 more variables: sex <fct>, year <int>

4.3 Spalten auswählen

Mit select() können wir festlegen, welche Spalten eines Datensatzes wir behalten möchten.

Wenn wir zum Beispiel nur Art, Insel und Körpermasse einschließen wollen:

penguins %>%
  select(species, island, body_mass_g)
# A tibble: 344 × 3
   species island    body_mass_g
   <fct>   <fct>           <int>
 1 Adelie  Torgersen        3750
 2 Adelie  Torgersen        3800
 3 Adelie  Torgersen        3250
 4 Adelie  Torgersen          NA
 5 Adelie  Torgersen        3450
 6 Adelie  Torgersen        3650
 7 Adelie  Torgersen        3625
 8 Adelie  Torgersen        4675
 9 Adelie  Torgersen        3475
10 Adelie  Torgersen        4250
# ℹ 334 more rows

Das Ergebnis ist ein Datensatz mit denselben Zeilen wie vorher, aber nur den ausgewählten Spalten.

Wir können mit einem Minuszeichen auch Spalten ausschließen:

penguins %>%
  select(-year)
# A tibble: 344 × 7
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 1 more variable: sex <fct>
HinweisHinweis

filter() arbeitet auf Zeilen, select() arbeitet auf Spalten. Diesen Unterschied sollten Sie sich von Anfang an gut einprägen.

HinweisHinweis: Auswahl von Spalten anhand von Namenseigenschaften oder dem Datentyp

Eine große Hilfe können mitunter Sprachmuster bei der Auswahl sein: Wähle alle Variablen, die…

  • mit einem bestimmten Präfix starten (starts_with())
  • mit einem bestimmt Suffix enden (ends_with())
  • eine bestimmte Abfolge beinhalten (contains())

oder auch für bestimmte Datentypen…

  • z.B. wähle alle numerischen Variablen (where(is.numeric))
VorsichtProbieren Sie es aus!
  1. Wählen Sie nur die Variablen species, sex und body_mass_g aus.

  2. Entfernen Sie die Variable year aus dem Datensatz.

  3. Wählen Sie nur bill_length_mm, bill_depth_mm und flipper_length_mm aus.

Lösung
penguins %>%
  select(species, sex, body_mass_g)
# A tibble: 344 × 3
   species sex    body_mass_g
   <fct>   <fct>        <int>
 1 Adelie  male          3750
 2 Adelie  female        3800
 3 Adelie  female        3250
 4 Adelie  <NA>            NA
 5 Adelie  female        3450
 6 Adelie  male          3650
 7 Adelie  female        3625
 8 Adelie  male          4675
 9 Adelie  <NA>          3475
10 Adelie  <NA>          4250
# ℹ 334 more rows
penguins %>%
  select(-year)
# A tibble: 344 × 7
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 1 more variable: sex <fct>
penguins %>%
  select(bill_length_mm, bill_depth_mm, flipper_length_mm)
# A tibble: 344 × 3
   bill_length_mm bill_depth_mm flipper_length_mm
            <dbl>         <dbl>             <int>
 1           39.1          18.7               181
 2           39.5          17.4               186
 3           40.3          18                 195
 4           NA            NA                  NA
 5           36.7          19.3               193
 6           39.3          20.6               190
 7           38.9          17.8               181
 8           39.2          19.6               195
 9           34.1          18.1               193
10           42            20.2               190
# ℹ 334 more rows

4.4 Neue Variablen erstellen

Mit mutate() können wir neue Variablen erzeugen oder vorhandene Variablen überschreiben. Wenn es neue Variablennamen sind, werden diese als neue Spalten rechts angehängt.

Angenommen, wir möchten die Körpermasse zusätzlich in Kilogramm statt in Gramm ausdrücken:

penguins %>%
  mutate(
    body_mass_kg = body_mass_g / 1000,
    flipper_length_cm = flipper_length_mm / 10
  )
# A tibble: 344 × 10
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 4 more variables: sex <fct>, year <int>, body_mass_kg <dbl>,
#   flipper_length_cm <dbl>
WarnungVorsicht

Wenn Sie in mutate() einen Namen verwenden, der bereits als Spalte existiert, wird die alte Spalte überschrieben. Das kann sinnvoll sein, passiert aber auch leicht aus Versehen.

Und auch hier greift das Standardverhalten, dass “Dann”-Schritte nicht automatisch gespeichert werden.

VorsichtProbieren Sie es aus!
  1. Erstellen Sie eine neue Variable bill_length_cm, in der die Schnabellänge in Zentimetern statt Millimetern gespeichert ist.

  2. Erstellen Sie eine neue Variable heavy_penguin, die TRUE ist, wenn body_mass_g größer als 5000 ist, und sonst FALSE.

Lösung
penguins %>%
  mutate(bill_length_cm = bill_length_mm / 10)
# A tibble: 344 × 9
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 3 more variables: sex <fct>, year <int>, bill_length_cm <dbl>
penguins %>%
  mutate(heavy_penguin = body_mass_g > 5000)
# A tibble: 344 × 9
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 3 more variables: sex <fct>, year <int>, heavy_penguin <lgl>

4.5 Kennwerte berechnen

Mit summarise() berechnen wir Kennwerte über einen gesamten Datensatz. Dies liegt vor, wann immer wir aus vielen Werten einen Wert berechnen wollen (z.B. aus vielen Größenangaben einen Mittelwert oder Standardabweichung).

Zum Beispiel den Mittelwert der Körpermasse:

penguins %>%
  summarise(mean_body_mass = mean(body_mass_g, na.rm = TRUE))
# A tibble: 1 × 1
  mean_body_mass
           <dbl>
1          4202.

Hier ist na.rm = TRUE wichtig, weil im Datensatz fehlende Werte (NA) vorkommen. Sie sehen auch, dass der resultierende Datensatz enorm geschrumpft ist: Er hat nun nur noch eine Zeile! Also alle Werte über alle Zeilen wurden durch summarise() zu einem einzigen Wert zusammengefasst.

Wir können auch mehrere Kennwerte gleichzeitig berechnen:

penguins %>%
  summarise(
    mean_body_mass = mean(body_mass_g, na.rm = TRUE),
    sd_body_mass = sd(body_mass_g, na.rm = TRUE),
    n = n()
  )
# A tibble: 1 × 3
  mean_body_mass sd_body_mass     n
           <dbl>        <dbl> <int>
1          4202.         802.   344
HinweisHinweis

Die Funktion n() zählt, wie viele Zeilen aktuell im Datensatz enthalten sind. Sie ist besonders in Kombination mit summarise() und group_by() (siehe unten) nützlich.

WarnungVorsicht

Vergisst man bei Variablen mit fehlenden Werten das Argument na.rm = TRUE, erhält man oft selbst als Ergebnis wieder nur NA.

VorsichtProbieren Sie es aus!

Berechnen Sie den Mittelwert der Schnabentiefe (bill_depth_mm). Berechnen Sie den Mittelwert und die Standardabweichung der Flügellänge (flipper_length_mm).

Lösung
penguins %>%
  summarise(mean_bill_depth = mean(bill_depth_mm, na.rm = TRUE))
# A tibble: 1 × 1
  mean_bill_depth
            <dbl>
1            17.2
penguins %>%
  summarise(
    mean_flipper = mean(flipper_length_mm, na.rm = TRUE),
    sd_flipper = sd(flipper_length_mm, na.rm = TRUE)
  )
# A tibble: 1 × 2
  mean_flipper sd_flipper
         <dbl>      <dbl>
1         201.       14.1

4.6 Gruppieren von Daten

Oft möchten wir Kennwerte nicht für den gesamten Datensatz, sondern getrennt nach Gruppen berechnen. Dafür verwenden wir group_by().

Wenn wir zum Beispiel den Mittelwert der Körpermasse getrennt nach Art berechnen möchten:

penguins %>%
  group_by(species) %>%
  summarise(mean_body_mass = mean(body_mass_g, na.rm = TRUE))
# A tibble: 3 × 2
  species   mean_body_mass
  <fct>              <dbl>
1 Adelie             3701.
2 Chinstrap          3733.
3 Gentoo             5076.

Jetzt erhalten wir für jede Art einen eigenen Mittelwert, und entsprechend hat der Datensatz nun 3 Zeilen.

Mehrere Gruppierungsvariablen: Wir können auch nach mehreren Variablen gleichzeitig gruppieren. Dann werden alle vorhandenen Kombinationen von Gruppierungsvariablen analysiert:

penguins %>%
  group_by(species, sex) %>%
  summarise(mean_body_mass = mean(body_mass_g, na.rm = TRUE))
`summarise()` has regrouped the output.
ℹ Summaries were computed grouped by species and sex.
ℹ Output is grouped by species.
ℹ Use `summarise(.groups = "drop_last")` to silence this message.
ℹ Use `summarise(.by = c(species, sex))` for per-operation grouping
  (`?dplyr::dplyr_by`) instead.
# A tibble: 8 × 3
# Groups:   species [3]
  species   sex    mean_body_mass
  <fct>     <fct>           <dbl>
1 Adelie    female          3369.
2 Adelie    male            4043.
3 Adelie    <NA>            3540 
4 Chinstrap female          3527.
5 Chinstrap male            3939.
6 Gentoo    female          4680.
7 Gentoo    male            5485.
8 Gentoo    <NA>            4588.
HinweisHinweis

group_by() allein verändert zunächst nur die Struktur des Datensatzes für nachfolgende Befehle. Den eigentlichen Effekt sehen wir meist erst bei summarise(), mutate() oder ähnlichen Funktionen. Den Hinweis “ℹ Output is grouped by species.” etc. können Sie ignorieren.

VorsichtProbieren Sie es aus!
  1. Berechnen Sie die mittlere Flügellänge getrennt nach Art.

  2. Berechnen Sie die Anzahl der Beobachtungen getrennt nach Insel.

  3. Berechnen Sie die mittlere Schnabellänge getrennt nach Art und Insel.

Lösung
penguins %>%
  group_by(species) %>%
  summarise(mean_flipper = mean(flipper_length_mm, na.rm = TRUE))
# A tibble: 3 × 2
  species   mean_flipper
  <fct>            <dbl>
1 Adelie            190.
2 Chinstrap         196.
3 Gentoo            217.
penguins %>%
  group_by(island) %>%
  summarise(n = n())
# A tibble: 3 × 2
  island        n
  <fct>     <int>
1 Biscoe      168
2 Dream       124
3 Torgersen    52
penguins %>%
  group_by(species, island) %>%
  summarise(mean_bill_length = mean(bill_length_mm, na.rm = TRUE))
`summarise()` has regrouped the output.
ℹ Summaries were computed grouped by species and island.
ℹ Output is grouped by species.
ℹ Use `summarise(.groups = "drop_last")` to silence this message.
ℹ Use `summarise(.by = c(species, island))` for per-operation grouping
  (`?dplyr::dplyr_by`) instead.
# A tibble: 5 × 3
# Groups:   species [3]
  species   island    mean_bill_length
  <fct>     <fct>                <dbl>
1 Adelie    Biscoe                39.0
2 Adelie    Dream                 38.5
3 Adelie    Torgersen             39.0
4 Chinstrap Dream                 48.8
5 Gentoo    Biscoe                47.5

4.7 Across() and the power of organized labeling

Bisher haben wir Funktionen wie mutate() oder summarise() immer auf eine einzelne Variable angewendet. In der Praxis möchten wir jedoch häufig dieselbe Operation auf mehrere Variablen gleichzeitig anwenden.

Ein typisches Beispiel:

  • Mittelwerte für mehrere Variablen berechnen
  • mehrere Variablen transformieren (z.B. standardisieren)
  • fehlende Werte gleichzeitig behandeln

Genau dafür gibt es in dplyr die Funktion: across()

Die Funktion across() hat zwei zentrale Bestandteile:

  1. Welche Variablen sollen betroffen sein?
  2. Welche Funktion soll angewendet werden?

Ein Beispiel:

penguins %>%
  summarise(
    across(c(bill_length_mm, bill_depth_mm), ~ mean(.x, na.rm = TRUE))
  )
# A tibble: 1 × 2
  bill_length_mm bill_depth_mm
           <dbl>         <dbl>
1           43.9          17.2

Die Tilde ~ bedeutet hier so viel wie: “Wende die Funktion rechts auf die Variable links an”. Wir können auch mehrere Variablen über Sprach- oder Datentyp-Funktionen auswählen…

penguins %>%
  summarise(
    across(where(is.numeric),~ mean(.x, na.rm = TRUE))
  )
# A tibble: 1 × 5
  bill_length_mm bill_depth_mm flipper_length_mm body_mass_g  year
           <dbl>         <dbl>             <dbl>       <dbl> <dbl>
1           43.9          17.2              201.       4202. 2008.
penguins %>%
  summarise(
    across(ends_with("_mm"), ~ mean(.x, na.rm = TRUE))
  )
# A tibble: 1 × 3
  bill_length_mm bill_depth_mm flipper_length_mm
           <dbl>         <dbl>             <dbl>
1           43.9          17.2              201.
VorsichtProbieren Sie es aus!
  1. Berechnen Sie den Mittelwert für alle numerischen Variablen im Datensatz.

  2. Berechnen Sie die Standardabweichung (sd) für alle numerischen Variablen.

  3. Erstellen Sie neue Variablen, in denen alle Längenangaben (bill_length_mm, bill_depth_mm, flipper_length_mm) in cm umgerechnet werden.

  4. Berechnen Sie die Mittelwerte aller numerischen Variablen getrennt nach species.

Lösung
penguins %>%
  summarise(across(where(is.numeric), mean, na.rm = TRUE))
Warning: There was 1 warning in `summarise()`.
ℹ In argument: `across(where(is.numeric), mean, na.rm = TRUE)`.
Caused by warning:
! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
Supply arguments directly to `.fns` through an anonymous function instead.

  # Previously
  across(a:b, mean, na.rm = TRUE)

  # Now
  across(a:b, \(x) mean(x, na.rm = TRUE))
# A tibble: 1 × 5
  bill_length_mm bill_depth_mm flipper_length_mm body_mass_g  year
           <dbl>         <dbl>             <dbl>       <dbl> <dbl>
1           43.9          17.2              201.       4202. 2008.
penguins %>%
  summarise(across(where(is.numeric), sd, na.rm = TRUE))
# A tibble: 1 × 5
  bill_length_mm bill_depth_mm flipper_length_mm body_mass_g  year
           <dbl>         <dbl>             <dbl>       <dbl> <dbl>
1           5.46          1.97              14.1        802. 0.818
penguins %>%
  mutate(across(c(bill_length_mm, bill_depth_mm, flipper_length_mm), ~ .x / 10, .names = "{.col}_cm"))
# A tibble: 344 × 11
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 5 more variables: sex <fct>, year <int>, bill_length_mm_cm <dbl>,
#   bill_depth_mm_cm <dbl>, flipper_length_mm_cm <dbl>
penguins %>%
  group_by(species) %>%
  summarise(across(where(is.numeric), list(~ mean(.x,na.rm=T), ~ sd(.x,na.rm=T))))
# A tibble: 3 × 11
  species   bill_length_mm_1 bill_length_mm_2 bill_depth_mm_1 bill_depth_mm_2
  <fct>                <dbl>            <dbl>           <dbl>           <dbl>
1 Adelie                38.8             2.66            18.3           1.22 
2 Chinstrap             48.8             3.34            18.4           1.14 
3 Gentoo                47.5             3.08            15.0           0.981
# ℹ 6 more variables: flipper_length_mm_1 <dbl>, flipper_length_mm_2 <dbl>,
#   body_mass_g_1 <dbl>, body_mass_g_2 <dbl>, year_1 <dbl>, year_2 <dbl>

4.8 Sortieren

Mit arrange() können wir einen Datensatz nach einer oder mehreren Variablen sortieren.

Zum Beispiel nach Körpermasse:

# aufsteigend
penguins %>%
  arrange(body_mass_g)
# A tibble: 344 × 8
   species   island   bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>     <fct>             <dbl>         <dbl>             <int>       <int>
 1 Chinstrap Dream              46.9          16.6               192        2700
 2 Adelie    Biscoe             36.5          16.6               181        2850
 3 Adelie    Biscoe             36.4          17.1               184        2850
 4 Adelie    Biscoe             34.5          18.1               187        2900
 5 Adelie    Dream              33.1          16.1               178        2900
 6 Adelie    Torgers…           38.6          17                 188        2900
 7 Chinstrap Dream              43.2          16.6               187        2900
 8 Adelie    Biscoe             37.9          18.6               193        2925
 9 Adelie    Dream              37.5          18.9               179        2975
10 Adelie    Dream              37            16.9               185        3000
# ℹ 334 more rows
# ℹ 2 more variables: sex <fct>, year <int>
# absteigend
penguins %>%
  arrange(desc(body_mass_g))
# A tibble: 344 × 8
   species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>           <dbl>         <dbl>             <int>       <int>
 1 Gentoo  Biscoe           49.2          15.2               221        6300
 2 Gentoo  Biscoe           59.6          17                 230        6050
 3 Gentoo  Biscoe           51.1          16.3               220        6000
 4 Gentoo  Biscoe           48.8          16.2               222        6000
 5 Gentoo  Biscoe           45.2          16.4               223        5950
 6 Gentoo  Biscoe           49.8          15.9               229        5950
 7 Gentoo  Biscoe           48.4          14.6               213        5850
 8 Gentoo  Biscoe           49.3          15.7               217        5850
 9 Gentoo  Biscoe           55.1          16                 230        5850
10 Gentoo  Biscoe           49.5          16.2               229        5800
# ℹ 334 more rows
# ℹ 2 more variables: sex <fct>, year <int>
VorsichtProbieren Sie es aus!
  1. Sortieren Sie den Datensatz nach bill_length_mm.
  2. Sortieren Sie den Datensatz absteigend nach flipper_length_mm.
Lösung
penguins %>%
  arrange(bill_length_mm)
# A tibble: 344 × 8
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Dream               32.1          15.5               188        3050
 2 Adelie  Dream               33.1          16.1               178        2900
 3 Adelie  Torgersen           33.5          19                 190        3600
 4 Adelie  Dream               34            17.1               185        3400
 5 Adelie  Torgersen           34.1          18.1               193        3475
 6 Adelie  Torgersen           34.4          18.4               184        3325
 7 Adelie  Biscoe              34.5          18.1               187        2900
 8 Adelie  Torgersen           34.6          21.1               198        4400
 9 Adelie  Torgersen           34.6          17.2               189        3200
10 Adelie  Biscoe              35            17.9               190        3450
# ℹ 334 more rows
# ℹ 2 more variables: sex <fct>, year <int>
penguins %>%
  arrange(desc(flipper_length_mm))
# A tibble: 344 × 8
   species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>           <dbl>         <dbl>             <int>       <int>
 1 Gentoo  Biscoe           54.3          15.7               231        5650
 2 Gentoo  Biscoe           50            16.3               230        5700
 3 Gentoo  Biscoe           59.6          17                 230        6050
 4 Gentoo  Biscoe           49.8          16.8               230        5700
 5 Gentoo  Biscoe           48.6          16                 230        5800
 6 Gentoo  Biscoe           52.1          17                 230        5550
 7 Gentoo  Biscoe           51.5          16.3               230        5500
 8 Gentoo  Biscoe           55.1          16                 230        5850
 9 Gentoo  Biscoe           49.5          16.2               229        5800
10 Gentoo  Biscoe           49.8          15.9               229        5950
# ℹ 334 more rows
# ℹ 2 more variables: sex <fct>, year <int>

5 Übung: Ein komplexes Beispiel

Bisher haben wir die wichtigsten dplyr-Funktionen einzeln kennengelernt. In der Praxis werden diese Funktionen jedoch fast nie isoliert verwendet. Stattdessen kombiniert man meist mehrere Arbeitsschritte in einer gemeinsamen Analyse-Pipeline.

Im folgenden Beispiel verbinden wir:

  • filter()
  • select()
  • mutate()
  • across()
  • summarise()
  • arrange()

Angenommen, wir interessieren uns nur für Pinguine der Arten Adelie und Gentoo. Außerdem möchten wir nur einige ausgewählte Variablen betrachten, die Längenangaben zusätzlich in Zentimeter umrechnen, anschließend pro Art Mittelwerte berechnen und das Ergebnis nach der mittleren Körpermasse sortieren.

Der vollständige Code sieht dann so aus:

penguins %>%
  filter(species %in% c("Adelie", "Gentoo")) %>%
  select(species, island, bill_length_mm, bill_depth_mm, flipper_length_mm, body_mass_g) %>%
  mutate(
    across(
      c(bill_length_mm, bill_depth_mm, flipper_length_mm),
      ~ .x / 10,
      .names = "{.col}_cm"
    )
  ) %>%
  group_by(species) %>%
  summarise(
    across(
      c(body_mass_g, bill_length_mm_cm, bill_depth_mm_cm, flipper_length_mm_cm),
      mean,
      na.rm = TRUE
    )
  ) %>%
  arrange(desc(body_mass_g))
VorsichtProbieren Sie es aus!

Verändern Sie das Beispiel auf folgende Weise:

  1. Betrachten Sie statt “Adelie” und “Gentoo” die Arten “Adelie” und “Chinstrap”.
  2. Berechnen Sie statt der Mittelwerte die Standardabweichungen.
  3. Sortieren Sie das Ergebnis nach der mittleren bzw. standardabweichungsbasierten flipper_length_mm_cm.
Lösung
penguins %>%
  filter(species %in% c("Adelie", "Chinstrap")) %>%
  select(species, island, bill_length_mm, bill_depth_mm, flipper_length_mm, body_mass_g) %>%
  mutate(
    across(
      c(bill_length_mm, bill_depth_mm, flipper_length_mm),
      ~ .x / 10,
      .names = "{.col}_cm"
    )
  ) %>%
  group_by(species) %>%
  summarise(
    across(
      c(body_mass_g, bill_length_mm_cm, bill_depth_mm_cm, flipper_length_mm_cm),
      sd,
      na.rm = TRUE
    )
  ) %>%
  arrange(desc(flipper_length_mm_cm))
# A tibble: 2 × 5
  species   body_mass_g bill_length_mm_cm bill_depth_mm_cm flipper_length_mm_cm
  <fct>           <dbl>             <dbl>            <dbl>                <dbl>
1 Chinstrap        384.             0.334            0.114                0.713
2 Adelie           459.             0.266            0.122                0.654

6 Vieles mehr…

Wie Sie in diesen Beispielfunktionen gesehen haben, sind dplyr und tidyverse ein sehr mächtige Pakete.

Wir können entsprechend unmöglich alle Varianten hervorheben, möchten hier aber noch einige Tipps geben:

Für Grafiken möchten wir Ihnen zudem die folgenden Materialien empfehlen: