Setup
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.3 ✓ purrr 0.3.4
## ✓ tibble 3.1.2 ✓ dplyr 1.0.6
## ✓ tidyr 1.1.3 ✓ stringr 1.4.0
## ✓ readr 1.4.0 ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
Einige Funktionen in R sind vektorisiert, andere nicht
Einige Funktionen in R sind vektorisiert: sie führen ihren Dienst für jedes Element eines Vektors aus. Erzeugen wir dazu ein paar Daten:
x = c(1,2,3)
y = c(1,2,4)
x + y
## [1] 2 4 7
Wie man sieht, wird die Funktion “Summe” elementweise ausgeführt: Für jedes Paar von (x,y) wird die Summe berechnet.
Leide sind nicht alle Funktionen vektorisiert:
sum(x,y)
## [1] 13
Klappt auch nicht:
sum(c(x,y))
## [1] 13
Beispiel für vektorisierte Funktionen
Inflix-Funktionen scheinen alle vektorisiert zu sein:
x - y
## [1] 0 0 -1
x == y
## [1] TRUE TRUE FALSE
x * y
## [1] 1 4 12
x %% y # Rest (Modulo)
## [1] 0 0 3
Beispiel für nicht-vektorisierte Funktionen
Die Liste ist lang, wichtige Beispiele sind:
- mean
- sum
- identical
- …
Typischer Anwendungsfall
Ein typischer Anwendungsfall sind zeilenweise Berechnungen, etwa der Mittelwert pro Zeile einer Tabelle.
Vektorisieren für Mittelwerte und Summen
Möchte man sum
und mean
vektorisiert über Zeilen einer Tabelle anwenden, so gibt es Helferfuntionen, nämlich rowSums
und rowMeans
.
Dazu brauchen wir zuerst eine Tabelle:
d <- tibble(
x = c(1,2,3),
y = c(1,2,4)
)
knitr::kable(d)
x | y |
---|---|
1 | 1 |
2 | 2 |
3 | 4 |
rowSums(d)
## [1] 2 4 7
rowMeans(d)
## [1] 1.0 2.0 3.5
Vektorisieren mit dem Tidyverse
Mittels Tidyverse-Methoden gibt es eine Reihe von Ansätzen.
map
d %>%
map2_dbl(.x = x,
.y = y,
.f = ~ mean(c(.x,.y)))
## [1] 1.0 2.0 3.5
map
wendet eine Funktion auf jedes Element eines Vektors oder einer Liste an. Im Standard wird eine Liste zurückgegeben.
map_dbl
gibt einen Wert vom Typ dbl
(Gleitkommazahl) zurück. map2
übergibt jeweils ein Element von zwei Vektoren x und y an eine Funktionen f: f(x, y) für jedes Elementenpaar von x und y, d.h. (\(x_1\), \(y_1\)), )\(x_2,y_2\)), ….
rowwise
rowwise
teilt die Tabelle in zeilenweise Gruppen ein: jede Zeile wird eine eigene Gruppe, also Teil-Tabelle. Dann wird mean
etc. wieder funktionieren.
d %>%
rowwise() %>%
mutate(row_mean = mean(c(x,y)),
row_identical = identical(x,y))
## # A tibble: 3 x 4
## # Rowwise:
## x y row_mean row_identical
## <dbl> <dbl> <dbl> <lgl>
## 1 1 1 1 TRUE
## 2 2 2 2 TRUE
## 3 3 4 3.5 FALSE
map
mit mutate
Möchte man das Ergebnis von map
in eine Tabelle einbinden, so macht man das wie gewohnt mit mutate
:
d %>%
mutate(row_mean = map2_dbl(.x = x,
.y = y,
.f = ~ mean(c(.x,.y))) )
## # A tibble: 3 x 3
## x y row_mean
## <dbl> <dbl> <dbl>
## 1 1 1 1
## 2 2 2 2
## 3 3 4 3.5
mutate
und rowMeans
Auch das geht, wenn auch etwas umständlich, da rowMeans
eine Tabelle als Input erwartet:
d %>%
mutate(row_mean = rowMeans(select(., x,y)))
## # A tibble: 3 x 3
## x y row_mean
## <dbl> <dbl> <dbl>
## 1 1 1 1
## 2 2 2 2
## 3 3 4 3.5
Oder so:
d %>%
mutate(row_mean = rowMeans(across(.cols = c(x, y))))
## # A tibble: 3 x 3
## x y row_mean
## <dbl> <dbl> <dbl>
## 1 1 1 1
## 2 2 2 2
## 3 3 4 3.5
Das funktioniert, da across einen Tibble (d.h. eine Tabelle) zurückliefert.
Mit Infix-Funktionen
d %>%
mutate(row_mean = (x + y)/2)
## # A tibble: 3 x 3
## x y row_mean
## <dbl> <dbl> <dbl>
## 1 1 1 1
## 2 2 2 2
## 3 3 4 3.5
Weiterführende Infos
Hier findet sich eine gute Einführung in zeilenweise Operationen im Tidyverse.