2  R Grundlagen

Wir laden zunächst das tidyverse-Paket, das wir in allen Sitzungen benötigen.

library(tidyverse)

2.1 Objekte in R

Ein Objekt ist in R einfach etwas, das wir speichern möchten, damit wir später wieder darauf zugreifen können. Das können einzelne Zahlen sein, eine Sammlung von Zahlen oder Texten, bis hin zu einem Datensatz oder einer Funktion. Die Objekte (z.B. Datensätze) können in R mit = oder <- zugewiesen werden. Nach der Zuordnung fungiert der Objektname quasi als “Platzhalter” für das dahinterliegende Objekt. Sie werden im sog. Environment gespeichert und stehen so für die aktuelle R-Sitzung zur Verfügung, können also auch an späterer Stelle im Skript wieder aufgerufen und weiterverwendet werden.

Hier legen wir fest, dass das Objekt meine_zahl ab jetzt für 9 steht.

meine_zahl <- 9

In der Darstellung auf dieser Webseite erscheint zukünftig immer der Output direkt unter dem Code, zu erkennen an den vorangestellten eckigen Klammern [] mit Zahlen darin. In RStudio erscheint dieser Output dann in der R-Konsole, also unten links, nicht im R-Skript, also oben links. In der Konsole erscheint zudem auch noch einmal der Code aus dem R-Skript.

In diesem Beispiel ist der Output logischerweise 9.

# hier sollte 9 erscheinen
meine_zahl
[1] 9
Kommentare

Neben Befehlen können wir im R-Skript auch Kommentare hinterlassen (wie im obigen Code in der ersten Zeile). Diese beginnen immer mit einer #. Alles, was danach folgt, wird von R als reiner Kommentar erkannt und nicht mit ausgeführt. Das Arbeiten mit Kommentaren ist in R sehr empfehlenswert, denn es ermöglicht uns, Erläuterungen für andere (und unsere zukünftigen Ichs) zu hinterlassen, die den Code verständlicher machen.

Zudem können Kommentare auch für die Strukturierung des Skriptes verwendet werden. Man kann beispielsweise - wenn man die Hausaufgaben löst - jeweils die einzelnen Aufgaben von einander abtrennen, indem man #Aufgabe 1, #Aufgabe 2 usw. vor den jeweiligen Lösungscode schreibt (siehe hierzu auch weiter unten). Oft werden wir auch Antwortsätze formulieren, in denen z.B. das Ergebnis einer Auswertung interpretiert wird. Auch diese müssen im Skript dann mit # beginnen, damit R nicht versucht, sie als Code auszuführen und entsprechende Fehlermeldungen produziert.

Im Allgemeinen arbeiten wir mit sog. Tibbles (manchmal auch Dataframe genannt). Diese können verschiedene Arten von Daten (Zahlen, Text) enthalten. Normalerweise lesen wir diese Daten aus externen Dateien ein (vgl. Daten ein- und ausgeben), aber man kann sie auch mit dem Befehl tibble() direkt erstellen, was wir im folgenden einmal machen:

soziodemographie <- tibble(
  alter = c(32, 54, 26),
  groesse = c(1.67, 1.82, 1.76),
  name = c("Maria Musterfrau", "Peter Mustermann", "Kim Muster"),
  ist_verheiratet = c(FALSE, TRUE, FALSE),
  geschlecht = c("Weiblich", "Männlich", NA)
)
soziodemographie
# A tibble: 3 × 5
  alter groesse name             ist_verheiratet geschlecht
  <dbl>   <dbl> <chr>            <lgl>           <chr>     
1    32    1.67 Maria Musterfrau FALSE           Weiblich  
2    54    1.82 Peter Mustermann TRUE            Männlich  
3    26    1.76 Kim Muster       FALSE           <NA>      

Das Objekt soziodemographie enthält 3 Zeilen und 5 Spalten. Die Spalte bezeichnen wir auch als Variable des Datensatzes, die Zeile als Fall oder Analyseeinheit. Wir sehen, dass alter und groesse numerische Variablen sind (Typ dbl = double precision number), die Spalten name und geschlecht haben den Typ Text (chr = character), und ist_verheiratet ist eine Wahr/Falsch-Variable (Typ lgl = logical). Diese haben nur 2 mögliche Ausprägungen, nämlich TRUE und FALSE. Fehlende Werte werden mit NA markiert, hier für die Variable geschlecht in Zeile 3. Damit teilen wir R mit, dass keine gültige Information in dieser Zelle vorliegt.

Wir können beliebig viel dieser Objekte gleichzeitig in R verarbeiten, daher müssen wir immer spezifizieren, mit welcher Variable aus welchem Objekt wir gerade etwas tun möchten. Wir können auf einzelne Spalten eines Tibbles am einfachsten mit dem Dollarzeichen Tibble$Spalte zugreifen.

soziodemographie$name
[1] "Maria Musterfrau" "Peter Mustermann" "Kim Muster"      

2.2 Funktionen

Funktionen in R dienen dazu, bestimmte Operationen mit möglichst wenig Code auszuführen. In R gibt es sehr viele Funktionen, die entweder bereits in base R enthalten sind oder durch die Verwendung von Zusatzpaketen genutzt werden können. Funktionen in R sind folgendermaßen aufgebaut: funktionsname(funktionsargument1 = Wert1, ggf. auch funktionsargument2 = Wert2,...).

Ein einfaches Beispiel ist die Berechnung der Quadratwurzel sqrt(x) (engl. Abkürzung für square root). In diesem Fall muss lediglich der Funktion die Zahl übergeben werden, aus der wir die Quadratwurzel ziehen wollen.

sqrt(4)
[1] 2

Vorne steht also immer der Name der Funktion. Der Name ist oftmals so gewählt, dass er Hinweise gibt, was die Funktion genau macht - wie im Beispiel, in dem sqrt() für square root steht. Anschließend folgt eine Klammer, die auch wieder geschlossen werden muss. Danach folgen die Funktionsargumente. Wie viele und welche das sind, ist von Funktion zu Funktion unterschiedlich. Oft reicht - wie im Beispiel oben - ein Funktionsargument. Das erste (und teilweise auch einzige) Funktionsargument ist meistens der Platzhalter für das Objekt, auf das die Funktion angewendet werden soll. Dem kann - wie im obigen Beispiel - lediglich ein einziger Zahlenwert übergeben werden, eine Spalte mit mehreren Werten oder sogar ein ganzer Tibble.

Im folgenden Beispiel übergeben wir die Variable alter aus dem Tibble soziodemographie. Deshalb wird nun aus allen drei Altersangaben (siehe oben) die Quadratwurzel gezogen.

sqrt(soziodemographie$alter)
[1] 5.656854 7.348469 5.099020

Dann können weitere Funktionsargumente dazukommen, die genauer festlegen, wie die Funktion auf dieses Objekt angewandt werden soll. Die Funktion sqrt besitzt keine weiteren Funtkionsargumente, weil es nur eine Möglichkeit gibt, die Quadratwurzel einer Zahl zu berechnen.

Ein anderes Beispiel ist die Funktion round(), die eine Zahl rundet. Dieses Mal runden wir die Werte aus der Spalte groesse auf ganze Zahlen ohne Nachkommastelle, was der Standard bei der Funktion round() ist.

round(soziodemographie$groesse)
[1] 2 2 2

Was ist nun, wenn wir auf eine oder zwei Nachkommastellen runden wollen? Hierfür gibt es das zusätzliche Funktionsargument digits, mit dem die Anzahl Dezimalstellen angegeben werden kann. Wie oben erwähnt, ist der Standardwert hierfür 0, wir können es aber leicht überschreiben:

round(soziodemographie$groesse, digits = 1)
[1] 1.7 1.8 1.8

Bei Funktionen ist jedem Funktionsargument eine Position zugewiesen, bei round() kommt zuerst x, dann digits. Deshalb ist es theoretisch nicht notwendig, x und digits zu nutzen, es funktioniert auch so (sog. positionsweise Übergabe), die uns etwas Tipparbeit spart:

round(soziodemographie$groesse, 0)
[1] 2 2 2

Hier ist allerdings Vorsicht geboten. Vertauschen wir die Reihenfolge, werden die falschen Werte an das Funktionsargument übergeben. Deshalb werden wir im Seminar immer so vorgehen, dass wir x nicht ausschreiben, da dies immer gleich ist und auch immer an erster Position steht. Alle anderen Funktionsargumente nennen wir aber, um Fehler zu vermeiden.

Bisher erhalten wir lediglich das Ergebnis der Funktion in der R-Konsole. Es wird aber nicht als Objekt angelegt, erscheint also nicht rechts oben. Das hat zur Folge, dass wir mit dem Ergebnis nicht weiterarbeiten können. Das ist nur möglich, wenn wir das Ergebnis in einem eigenen Objekt abspeichern:

gerundete_groessen <- round(soziodemographie$groesse, digits = 1)
gerundete_groessen # dieses neue Objekt könnten wir nun im Folgenden weiterverwenden.
[1] 1.7 1.8 1.8

Achtung! Wir haben gerade ein neues Objekt gerundete_groessen im Environment erstellt. Unser ursprünglicher Tibble soziodemographie ist hiervon nicht betroffen, d.h. er enthält weiterhin nur die 4 bekannten Spalten, inkl. der ursprünglichen Größenvariable. Wie wir Datensätze gezielt verändern können, sehen wir in Daten transformieren.

2.3 Pipes

Die sogenannte Pipe, |> macht R-Code für Menschen lesbarer. Sie ist eine Aufbauform für Code und sorgt für seine Strukturierung. Funktionen werden in R (wie in den meisten Programmiersprachen) von innen nach außen ausgeführt, d.h. die innerste Funktion zuerst. Dies führt zu sehr verschachtelten Konstruktionen, die schwer nachzuvollziehen sind:

round(sum(gerundete_groessen))
[1] 5

Die Pipe arbeitet hingegen von links nach rechts bzw. von oben nach unten und übergibt das Ergebnis der darüberliegenden Funktion an die darunterliegende Funktion. Damit wird der R-Code leichter lesbar und nachvollziehbar.

gerundete_groessen |>
  sum() |>
  round()
[1] 5

Hier: Nutze das Objekt mit den gerunden Größen, dann summiere die Zahlen auf, dann runde das Ergebnis. Wie auf einem Fließband wird der Zwischenstand von Funktion zu Funktion weitergereicht, ohne dass wir die Zwischenergebnisse in ein temporäres Objekt speichern müssen.

Arbeiten wir mit Pipes, bleiben die Klammern hinter den Funktionen leer, wenn es nur um x, also das Objekt, auf das die Funktion angewendet werden soll, geht. Denn für x wird immer der Stand von der vorherigen Zeile weitergereicht. Nur wenn wir noch zusätzliche Funktionsargumente benötigen, müssen diese in der Klammer angegeben werden. Hierzu werden wir im Laufe des Seminars noch Beispiele sehen.

Wollen wir die Pipe tippen, können wir den Shortcut STRG+SHIFT+M bzw. CMD+SHIFT+M verwenden.

Native vs. Tidyverse Pipe

|> ist die empfohlene Schreibweise für eine Pipe. Bevor diese in R (Version 4.2) verfügbar war, wurde die Tidyverse-Pipe %>% als Operator verwendet, für die wir aber das entsprechende Paket laden mussten. Beide haben prinzipiell dieselbe Funktionsweise. Wir werden im Seminar durchgängig die Standardvariante verwenden. In älteren Unterlagen findet sich aber oft noch die Tidyverse-Variante.

Wenn wir den obigen Shortcut verwenden, kann es sein, dass die Tidyverse-Pipe erscheint.Hier findet sich eine Anleitung, wie wir das ändern können.

Für die Datenanalyse mit R sind Pipes besonders sinnvoll, weil sich mit ihnen die einzelnen Arbeitsschritte (Daten einlesen, Daten transformieren, Daten analysieren, Ergebnisse aufbereiten) sehr übersichtlich in einem R-Skript darstellen lassen. So ist gewährleistet, dass die Analysen reproduzierbar und verständlich sind. Sinngemäß empfehlen wir den nachfolgenden Workflow sowohl für die Hausaufgaben als auch für eigene Auswertungen. Die nachfolgend dargestellten Funktionen, wie etwa daten_transformieren(), sind lediglich Platzhalter für in R existierende Funktionen, welche wir später kennenlernen. Sinngemäß empfehlen wir folgenden Workflow sowohl für die Hausaufgaben als auch für eigene Auswertungen:

# Vorbereitung
d <- rohdaten_einlesen() |>
  daten_transformieren() |> # Dies ist ein Platzhalter für eine in R existierende Funktion
  daten_filtern()

# Aufgabe 1
d |>
  daten_analysieren() |>
  ergebnisse_aufbereiten()

# Aufgabe 2
d |>
  daten_analysieren() |>
  ergebnisse_aufbereiten()

Das R-Skript und die Rohdaten reichen damit aus, um alle Schritte nachvollziehen und replizieren zu können. Pipes können beliebig lang sein, und werden immer komplett ausgeführt. Daher ist es manchmal sinnvoll, einen Zwischenstand, den wir mehrfach verwenden möchten, als eigenes Objekt zu speichern, wie im Beispiel der aus den Rohdaten aufbereitete Datensatz d.

2.4 Hilfe

RStudio bietet zahlreiche Hilfestellungen für Funktionen. So zeigt das Programm Informationen über die Funktion automatisch an, sobald wir mit der Eingabe beginnen. Desweiteren kann die Funktion vervollständigt werden mit der Verwendung der tab-Taste. Sollte eine Funktion gänzlich unbekannt sein, kann mit ? + Funktionsname die Dokumentation aufgerufen werden.

?sum

In der Hilfe von sum() können wir etwa unter Arguments nachlesen, dass es noch ein Funktionsargument, na.rm, gibt. Zudem finden wir unter Examples noch einige Beispielanwendungen der Funktion.

2.5 Glossar

Funktion Definition
round() Runden von Zahlenwerten
sqrt() Berechnen der Quadratwurzel
sum() Berechnung der Summe
summarise() Aggregieren einer oder mehrerer Variablen eines Dataframes/Tibbles zu einer oder mehreren neuen Variablen

2.6 Hausaufgabe

  1. Legen Sie einen Tibble an, in dem es folgende Spalten/Vektoren gibt:
  1. Wochentag (Ausprägungen: Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag)
  2. Sonnenstunden pro Tag (Ausprägungen: 1, 4, 10, 2, 5, 7, 0)
  3. Niederschlagsmenge pro Tag (Ausprägungen: 100, 0, 0, 0, 49, [Wetterstation war defekt, konnte nicht gemessen werden - was tragen Sie ein?], 4)
  1. Wie viele Sonnenstunden gab es insgesamt in dieser Woche? Bitte nutzen Sie eine passende Funktion und die Spalte aus dem Tibble, um die Frage zu beantworten.

  2. Und wie hoch war die Niederschlagsmenge insgesamt in dieser Woche? Bitte nutzen Sie eine passende Funktion und die Spalte aus dem Tibble, um die Frage zu beantworten.