In einem Psychologiestudium (aber natürlich auch in anderen Fächern) spielen in vielfältiger Art und Weise empirische Daten eine wichtige Rolle und in diesem Zusammenhang natürlich auch deren statistische Auswertung. Dazu werden Sie ein Verständnis für diese Verfahren und deren theoretischen Grundlagen erlangen, aber Sie werden auch eigene empirische Daten erheben, auswerten und dokumentieren (z.B. in den typischen Experimentalpsychologischen Praktika oder auch in Qualifikationsarbeiten). Zwar stehen zur Auswertung der Daten eine ganze Reihe von Programen zur Verfügung, aktuell hat sich in vielen Bereichen allerdings das frei verfügbare Programm R samt einiger Erweiterungen durchgesetzt.
Diese Seite bietet Ihnen eine Einführung in die Arbeit mit R, beginnend mit der Installation über einige grundlegende Aspekte bis zu Themen wie Programmkontrolle und einfachen statistischen Verfahren. Theoretische Kenntnisse der Verfahren werden hier nicht vermittelt, sondern vorausgesetzt (und anderweitig vermittelt).
Autor:innen dieser Seite: An den Inhalten dieser Seite haben mitgearbeitet: Eva Röttger, Markus Janczyk und Kilian Gloy. Der Inhalt dieser Seite wird in der Lehre in den Studiengängen Psychologie von der AG Forschungsmethoden und Kognitive Psychologie an der Universität Bremen verwendet, steht aber allen Interessierten zur Verfügung. Rückmeldungen/Fehler/Vorschläge können gesendet werden an randolph@uni-bremen.
Versionshistory:
Dieses erste Kapitel gibt Ihnen grundlegende Informationen zur Arbeit mit R (und RStudio) sowie zu deren Installation. Im Anschluss werden grundlegende Funktionalitäten behandelt. Die nächsten Kapitel greifen dann spezifische Aspekte der Arbeit mit R detaillierter auf.
R ist sehr flexibles und umfangreiches kostenloses OpenSource Statistikpaket, welches sich mittlerweile großer Verbreitung erfreut. OpenSource heißt in diesem Kontext, dass der Code frei verfügbar und einsehbar ist und im Prinzip auch jede Person eigene Ergänzungen programmieren kann.
Dabei ist R an sich ein eher unscheinbares Programm welches von vielen Anwender:innen nicht einmal wirklich geöffnet werden wird. Und wenn doch: die grafische Benutzeroberfläche ist enorm minimalistisch und bietet nur wenig Komfort. Daher gibt es eine ganze Reihe von Zusatzprogrammen, die auf R aufsetzen, aber den Komfort und das Arbeiten mit R enorm angenehmer gestalten. Wir werden hier mit dem Programm RStudio arbeiten.
Ein weiteres wichtiges Konzept bei der Arbeit mit R sind Pakete (oder Englisch: packages). Pakete stellen weitere Funktionen, Verfahren, Methoden, … zur Verfügung, und werden von verschiedenen Personen aus der R-Community geschrieben. Die Flexibilität und Mächtigkeit von R rührt auch daher, dass es für (fast) alle Probleme und Problemchen irgendwo ein Paket gibt, mit dem eine Lösung schnell gefunden ist, sobald das Paket identifiziert und installiert ist.
Nun ist R im Wesentlichen eine (Programmier-)Sprache und dies mag auf den ersten Blick abschreckend sein. Wie für alle Sprachen gilt aber auch hier: Mit beständiger Übung können Sie R meistern! Insofern können wir Sie nur ermutigen, tatsächlich mit R herumzuspielen, Dinge zu programmieren, andere Lösungswege zu suchen, und so weiter. Den meisten Personen macht die Arbeit mit R dann sogar Spaß!
Um R und RStudio zu nutzen, müssen beide Software-Pakete zunächst installiert werden. Der erste Schritt ist daher die Installation von R an sich. Auf der Homepage des R-Projekts (https://cran.r-project.org/) finden Sie dazu sämtliche benötigten Dateien und weitere Informationen, Pakete und auch Anleitungen (die wir hier erst einmal nicht benötigen).
Auf der Homepage wählen Sie zunächst das richtige Betriebssystem aus (Windows, Mac OS X oder Linux) und werden dann einen Schritt weitergeführt. Windowsnutzer:innen wählen auf der nächsten Seite base aus und können dann die aktuelle Version von R herunterladen und installieren. Ähnlich funktioniert die Installation bei den anderen Betriebssystemen. Falls Sie R schon installiert haben, nutzen Sie diesen Moment, um ein Update auf die neueste Version zu machen.
Der nächste Schritt ist dann die Installation von RStudio. Auf der entsprechenden Homepage https://www.rstudio.com/ wählen Sie die RStudio Desktop Version, laden sich den entsprechenden Installer für das richtige Betriebsystem herunter und folgen dann den üblichen Anweisungen bei der Installation von Software.
Haben Sie R und RStudio installiert, dann ist der erste wichtige Schritt zur Datenauswertung geschafft. Zum Ausprobieren können Sie nun RStudio starten (oder auch zum Vergleich einfach R einmal starten…).
RStudio sollte sich in etwa wie in der folgenden Abbildung dargestellt öffnen. Neben der Menüleiste ganz oben besteht RStudio i.d.R. aus vier verschiedenen Fenstern:
Geben Sie in der Konsole zum Probieren nun einfach 2+2
ein und bestätigen dies mit Return, dann führt R den Befehl aus und gibt
das Ergebnis in der Konsole aus:
2+2
## [1] 4
Längeren Code sollten Sie aber immer in ein R-Skript schreiben, dort
speichern und auch von dort ausführen. Geben Sie nun in das R-Skript
einfach 2+2
ein. Um diesen Code auszuführen, klicken Sie in
die Zeile, die Sie ausführen möchten und drücken dann
STRG-RETURN
(oder Sie klicken dann oben rechts auf
Run). Mehrere Zeilen werden ausgeführt, wenn diese zuerst
markiert werden und dann STRG-RETURN
gedrückt wird, das
gesamte Skript kann mit STRG-SHIFT-RETURN
ausgeführt und
alle vom Anfang bis zur aktuellen Cursorposition wird mit
STRG-ALT-B
ausgeführt.
In der Regel evaluiert R eine Zeile als einen Befehl. Falls Sie aber
in einer Zeile z.B. eine Klammer öffnen oder andere Strukturen beginnen,
führt R auch die nächste(n) Zeile(n) aus, bis der ganze Befehl
abgeschlossen ist. Mehrere Befehle in einer Zeile müssen durch ein
Semikolon ;
getrennt werden.
R verfügt über eine umfangreiche Hilfefunktion. Wenn Sie bspw. wissen
möchten, wie die Funktion mean()
funktioniert (die einen
Mittelwert berechnet), können Sie in die Konsole
?mean
eingeben. Darauf erscheint die entsprechende Hilfeübersicht zur
Funktion im Fenster rechts unten. Fällt Ihnen der genaue Name einer
Funktion nicht ein, oder wollen Sie wissen, was es evtl. noch für
interessante und nützliche Funktionen zu einem Thema gibt, kann der
Befehl apropos()
helfen. Wollen Sie z.B. alle Funktionen
auflisten, die irgendwas mit mean
zu haben, geschieht dies
mit:
apropos("mean")
## [1] ".colMeans" ".rowMeans" "colMeans" "kmeans"
## [5] "mean" "mean.Date" "mean.default" "mean.difftime"
## [9] "mean.POSIXct" "mean.POSIXlt" "rowMeans" "weighted.mean"
Ein letzter wichtiger Punkt ist der Hashtag (#). In R-Codes zeigt dieses Zeichen einen Kommentar an, d.h. die Zeile wird ab dem Hashtag nicht weiter ausgeführt, sondern dient nur der Beschreibung und Kommentierung der entsprechenden Stelle. Gerade in längeren Skripten ist es sehr hilfreich sich von Anfang an anzugewöhnen Code zu kommentieren, um ihn auch später noch gut verstehen zu können:
(3+6)/2 # Hier werden die Zahlen 3 und 6 addiert und dann durch 2 dividiert
## [1] 4.5
Wir werden auf dieser Seite diejenigen Inhalte behandeln, die uns grundlegend befähigen die üblichen statistischen Verfahren durchzuführen, die in den Statistikveranstaltungen behandelt werden. Das heißt auch, dass wir nützliche Funktionen von R auch auslassen werden. Zu Bedenken ist grundätzlich: Für quasi alle Fragestellungen und Probleme gibt es in R mehrere Lösungswege. Lassen Sie sich nicht davon abschrecken, wenn andere Autor:innen andere Wege vorschlagen! Was für Sie funktioniert hängt sehr von Ihrem persönlichen Arbeits- und Programmierstil ab.
Auf (fast) alle Fragen zu R gibt es im Internet eine Antwort. Wenn
eine einfache google-Suche zu unspezifisch ist, helfen die Seiten
https://rseek.org/
oder
https://stackoverflow.com/questions/tagged/r
vielleicht
weiter.
Es gibt natürlich eine ganze Reihe Bücher, sowohl als PDFs im Internet als auch in gedruckter Form. Einführungen in die wichtigsten Funktionen bieten z.B. die Bücher von Luhmann (2020) und Wollschläger (2021). Wer sich mehr mit Programmierung in R auseinandersetzen und fortgeschrittene Verfahren kennenlernen möchte, findet z.B. bei Ligges (2008) und Wollschläger (2020) gute Einstiege.
R wird direkt mit einer ganzen Reihe an Paketen installiert. Darüber
hinaus gibt es aber unzählige weitere Pakete für spezielle Verfahren und
Zwecke, die vor der Nutzung erst einmal installiert werden müssen. Dies
können Sie in RStudio mit dem Menü Tools - Install Packages
tun. Geben Sie hier einfach den Namen des gewünschten Paketes ein,
machen einen Haken bei Install dependencies und klicken dann
auf Install. Den Rest macht RStudio (idealerweise) selbst.
Geben Sie nun als Paketnamen schoRsch
ein und installieren
dieses. Alternativ können Sie auch die Konsole nutzen:
install.packages(pkgs = "schoRsch", dependencies = TRUE)
Wenn Sie schoRsch
erfolgreich installiert haben, können
Sie nun die Hilfe aufrufen, um zu schauen, was das Paket an Funktionen
bietet:
?schoRsch
Allerdings werden Sie mit großer Wahrscheinlichkeit eine
Fehlermeldung erhalten: Das Paket ist zwar installiert, aber noch nicht
geladen. Dies ist eine Eigenart von R, die verhindert, dass der Speicher
mit nicht-genutzten Paketen belastet wird. Geladen werden Pakete
meistens zu Beginn eines Skripts mit dem Befehl library()
,
also hier mit:
library(schoRsch)
Nun können Sie die Hilfefunktion erneut ausprobieren und anschauen.
Wie bereits erwähnt ist R i.W. eine Programmiersprache für Statistik. R kann zwar auch noch viel mehr (z.B. ist dieses Dokument direkt in R geschrieben worden), aber statistische Auswertung und Programmierung ist das, wozu R meistens verwendet wird. Da viele R-Neulinge bisher eher weniger mit Programmiersprachen zu tun gehabt haben, ist eine gewisse Skepsis zu Beginn nachvollziehbar. Sie brauchen allerdings keine Angst haben. Das Beste was Sie tun können ist: Probieren Sie selber aus!
Wir werden hier auch mit Code zu tun bekommen, der sich über mehrere
Zeilen erstreckt. Ein wichtiger Punkt (der aber oft für
Verständnisprobleme sorgt) ist: R beginnt mit der Ausführung in der
ersten (auszuführenden) Zeile und geht dann im Wesentlichen seriell vor,
d.h., danach wird die zweite Zeile ausgeführt, usw. Wir werden aber auch
Möglichkeiten kennenlernen, Zeilen zu überspringen (z.B. mit sog.
if
-Abfragen) und Zeilen zu wiederholen (z.B. mit sog.
for
-Schleifen). Beginnen werden wir nun allerdings mit der
wichtigen Unterscheidung von Variablen und Funktionen.
Eine wichtige erste Unterscheidung für die Arbeit mit R (und anderen Programmiersprachen) ist die Unterscheidung von Variablen und Funktionen.
Variablen haben ihren Namen daher, dass ihr Wert variabel ist. Eine Variable kann also z.B. mal den Wert 5 annehmen und dann den Wert 3. Für jede Variable müssen wir uns in R einen Namen ausdenken, der – zumindest wenn die Codes länger werden – möglichst gut beschreibt, was in der Variablen gespeichert wird.
R (und andere Programmiersprachen auch) unterscheidet mehrere Typen von Variablen, wobei der Typ u.a. darüber entscheidet, was wir mit einer Variablen später machen können. Oft arbeiten wir in R mit numerischen Variablen. Um eine Variable mit dem Namen \(a\) anzulegen und ihr den Wert 5 zuzuweisen, können wir in ein Skript (oder in die Konsole) schreiben:
a <- 5 # weist der Variablen a den Wert 5 zu
a # gibt den Wert der Variablen a aus
## [1] 5
(b = 5) # Zuweisungen gehen auch mit "=" und Klammern drumherum geben den Ausdruck direkt aus
## [1] 5
Die Variable a
sollte nun auch in der Variablenumgebung
auftauchen. Eine Übersicht über alle derzeit aktiven Variablen (und
Funktionen) erhalten Sie mit:
ls()
## [1] "a" "b"
und mit
rm(list = "a") # oder auch nur: rm(a)
kann die Variable auch aus der Umgebung gelöscht werden. Wenn Sie alle Variablen gleichzeitig löschen wollen, so geht dies mit:
rm(list = ls()) # ls() liefert alle derzeit aktiven Variablen und Funktionen zurück
Haben Sie das Paket schoRsch
installiert und geladen,
können Sie alternativ einfach eingeben:
clear()
Mit numerischen Variablen können Rechenoperationen durchgeführt
werden. Wir weisen den Variablen a
und b
nun
neue Werte zu, addieren diese, weisen das Ergebnis einer Variablen
c
zu und geben den Wert von c
aus:
a <- 5
b <- 7
c <- a + b
c
## [1] 12
Neben numerischen Variablen gibt es in R auch andere Variablentypen. Im Gegensatz zu manchen anderen Programmiersprachen (z.B. C++), müssen Sie R nicht vorab mitteilen, um was für einen Typ es sich handelt; R “überlegt” sich selber den Variablentyp. Die zwei für uns wichtigsten weiteren Variablentypen sind Bool-Variablen und Strings.
Bool-Variablen können nur zwei Werte annehmen,
nämlich wahr oder falsch. In R schreibt man dafür
üblicherweise TRUE
bzw. FALSE
oder abgekürzt
T
bzw. F
(es würde auch gehen: 1 bzw. 0).
Wollen Sie einer Bool-Variablen den Wert TRUE
zuweisen, so
geschieht dies ganz einfach durch:
Statistik.ist.toll <- TRUE # zuweisen
Statistik.ist.toll # Wert ausgeben
## [1] TRUE
Strings hingegen sind Zeichenfolgen und werden bei R dadurch kenntlich gemacht, dass sie in Anführungsstrichen geschrieben werden. Natürlich können Strings auch Zahlen beinhalten, sie sind dann aber eben Zeichenfolgen und mit ihnen kann nicht mehr gerechnet werden:
a <- 5 # numerische Variable
b <- "7" # b ist ein String
a + b # Fehler!
## Error in a + b: nicht-numerisches Argument für binären Operator
Natürlich kann R auch mit Strings diverse Dinge durchführen; wir werden im aktuellen Kontext aber wenig Gebrauch von derartigen Möglichkeiten machen.
Funktionen sind – etwas vereinfacht – Verfahren, die auf Variablen
angewendet werden können und i.d.R. wieder Werte “zurückgeben”. Den
meisten Funktionen werden in runden Klamern sog. Parameter
übergeben, die einerseits der Funktion sagen, auf welche Variablen sie
angewendet werden sollen und andererseits die genaue Arbeitsweise einer
Funktion noch bestimmen. All diese Dinge, die in Klammern “übergeben”
werden heißen Argumente. Um auszuprobieren, wie Funktionen
verwendet werden, testen wir nun als erstes die Funktion
mean()
, die einen Mittelwert berechnet. Wenn Sie die Hilfe
zu mean()
aufrufen, sehen Sie, dass die Funktion auf jeden
Fall ein Argument namens x
benötigt. Wir können daher z.B.
den Mittelwert der gerade angelegten Variablen a
berechnen
mit:
a <- 5
mean(x = a)
## [1] 5
Wenig überraschend ist der Mittelwert von 5 dann auch 5. Wenn Argumente in der richtigen Reihenfolge übergeben werden, kann der Name in der Zuweisung auch weggelassen werden. Darüber hinaus haben manchen Argumente Standardwerte: Sofern nichts anderes angegeben ist, geht R von diesen Werten aus. Die beiden folgenden Eingaben sind daher äquivalent:
mean(x = a, trim = 0, na.rm = FALSE) # mit mehreren Argumenten, aber ihren Standardwerten
mean(a) # ...die daher auch weggelassen werden können
Als zweites testen wir nun noch die Funktion
as.numeric()
, die aus einer String-Variablen eine
numerische Variable macht:
b <- "7" # String-Variable
b # b ausgeben -> 7 in Anführungsstrichen, weil String
## [1] "7"
c <- as.numeric(b) # wandelt b in numerische Variable um, speichert diese in c
c # c ausgeben -> nicht mehr in Anführungsstrichen, da nun numerische Variable
## [1] 7
c + 2 # = 9 -> mit c kann auch wieder gerechnet werden
## [1] 9
Funktionen der Form as.XXX()
dienen dazu, eine Variable
eines Typs in eine Variable anderen Typs zu verwandeln. Zusätzlich gibt
es noch Funktionen der Form is.XXX()
, die testen, ob eine
Variable diesen bestimmten Typs ist:
a <- "5"
is.numeric(a)
## [1] FALSE
is.numeric(as.numeric(a))
## [1] TRUE
In der letzten Zeile des letzten Codes sind zwei Funktionen
verschachtelt: R führt dann zunächst die innere Funktion aus
(as.numeric()
) und übergibt deren Ergebnis an die äußere
Funktion (is.numeric()
). Prinzipiell sind beliebig viele
solcher Verschachtelungen möglich - man verliert aber auch schnell den
Überblick und sollte nicht zuviele Verschachtelungen nutzen, sondern
ggf. mehrere Zeilen verwenden.
Wir werden im Laufe der Zeit eine ganze Reihe von Funktionen kennenlernen und verwenden und Funktionen können auch selber geschrieben werden, z.B. um immer wiederkehrende Verarbeitungen zu vereinfachen und damit Code zu reduzieren. Auch was Variablen angeht werden wir noch weitere Datenformen kennenlernen, mit denen wir die Arbeit in R vereinfachen können. Die wichtigsten davon werden Vektoren, Matrizen, DataFrames und Listen sein.
Ein wichtiger Punkt zum Schluss: R ist case sensitive, d.h.,
achtet auf Groß- und Kleinschreibung. Wenn Sie z.B. eine Variable
a
eingeführt haben, dann können Sie diese nicht mit
A
abrufen:
a <- 2
A
## Error in eval(expr, envir, enclos): Objekt 'A' nicht gefunden
Eine einfache Verwendung von R ist, das Programm als eine Art großen
Taschenrechner zu nutzen. R kann natürlich deutlich mehr, aber wir
fangen hier mit einfachen Rechenoperationen an, die auch immer wieder
eine Rolle spielen werden. Zunächst legen wir wieder zwei Variablen
a
und b
an und verwenden die Grundrechenarten
und Klammersetzung für Berechnungen:
a <- 9
b <- 3
a + b # Addition
## [1] 12
a - b # Subtraktion
## [1] 6
a * b # Multiplikation
## [1] 27
a / b # Division
## [1] 3
(a + b) * 2 # Klammersetzung
## [1] 24
(a + b) / ( (b - 1) * b) # Klammersetzung -> (9+3)/(2*3) = 12/6 = 2
## [1] 2
Potenzrechnung und Wurzelrechnung funktionieren natürlich auch:
a^b # a hoch b
## [1] 729
sqrt(a) # Wurzel aus a (sqrt = square-root)
## [1] 3
a^(1/2) # ...oder a hoch (1/2)
## [1] 3
Den “Rest” einer Division kann man sehr leicht mit dem sog. “Modulo”-Operator bestimmen:
a %% b # 9 / 3 = 3 Rest 0
## [1] 0
b %% a # 3 / 9 = 0 Rest 3
## [1] 3
Weitere nützliche Funktionen sind:
sin(a) ; cos(a) ; tan(a) # Sinus, Cosinus und Tangens
## [1] 0.4121185
## [1] -0.9111303
## [1] -0.4523157
log(a) ; exp(a) # Logarithmus von a bzw e hoch a (e = Euler'scher Zahl)
## [1] 2.197225
## [1] 8103.084
factorial(a) # a! = 1*2*...*a
## [1] 362880
Inbesondere numerische Variablen und Werte können auch miteinander
verglichen werden. Hierbei stehen alle üblichen Operatoren zur Verfügung
und der Vergleich liefert jeweils zurück, ob die Beziehung wahr ist oder
falsch ist, d.h., die Rückgabe ist jeweils ein Wahrheitswert
TRUE
oder FALSE
(eine Bool-Variable):
a <- 4
b <- 5
a < b # kleiner als
## [1] TRUE
a <= 4 # kleiner/gleich
## [1] TRUE
a > b # größer als
## [1] FALSE
a >= 4 # größer/gleich
## [1] TRUE
a == b # gleich
## [1] FALSE
a != 2 # ungleich
## [1] TRUE
Ein letzter wichtiger Operator in diesem Kontext ist die Negation. In
R wird diese durch ein “!” erreicht. Wenn also a
den Wert
TRUE
hat, dann ist non-a
folglich ´FALSE´:
a <- TRUE # wenn a also TRUE ist....
!a # ...dann ist non-a FALSE
## [1] FALSE
Derartige Bedingungen können auch miteinander kombiniert werden, wozu
im Wesentlichen die logischen Ausdrücke und und oder
wichtig sind. Wenn zwei Bedingungenragen mit und verknüpft
sind, dann müssen beide Bedingungen TRUE
sein, damit die
Gesamtausdruck TRUE
ist. Das Zeichen dafür ist das
&
:
(5 < 10) & (5 > 10) # 5 ist nicht gleichzeitig kleiner UND größer als 10... daher FALSE
## [1] FALSE
Wenn zwei Bedingungen hingegen mit oder verknüpft sind, dann
muss nur mindestens eine der Bedingungen TRUE
sein, damit
der Gesamtausdruck TRUE
ist. Das Zeichen dafür ist das
|
:
(5 < 10) | (5 > 10) # 5 ist kleiner ODER größer als 10... daher TRUE
## [1] TRUE
Die Klammersetzung wäre in den beiden letzten Beispielen nicht unbedingt nötig, erhöht aber durchaus die Lesbarkeit.
Bisher haben wir mit einfachen Variablen gearbeitet, die
gewissermaßen aber nur das Grundgerüst komplexerer Datenstrukturen
darstellen, mit denen wir in der praktischen Tätigkeit mit R fast immer
arbeiten werden. Manche Funktionen (wie z.B. mean()
)
ergeben auch erst dann einen Sinn, wenn sie auf komplexere
Datenstrukturen angewendet werden. Wir werden mit Vektoren und Matrizen
beginnen und dann mit sog. DataFrames weitermachen, die eine typische
Datenstruktur sind, mit denen wir danach immer wieder arbeiten
werden.
Vektoren und Matrizen sind eine wichtige Grundlage für die Arbeit mit größeren Datenmenge und sind in R ganz ähnlich aufgebaut wie sie auch in der Linearen Algebra behandelt werden.
Vektoren sind gewissermaßen Zusammenfassungen einzelner Variablen und
mit ihnen lassen sich viele Dinge in R viel einfacher durchführen als
mit einzelnen Variablen. Variablen oder Werte werden mit
c()
(c
für concatenate) zu einem
Vektor verbunden:
a <- 1 ; b <- 2; c <- 3 # einzelne Variablen
vektor <- c(a, b, c) # zu einem Vektor machen (oder: vektor <- c(1,2,3))
vektor # Vektor als Ganzes ausgeben
## [1] 1 2 3
Vektoren haben oft Regelmäßigkeiten oder Wiederholungen als Elemente. Um solche Vektoren zu erstellen, bietet R eine ganze Reihe nützlicher Funktionen. Die erste wichtige Variante, einen Vektor mit z.B. den Werten \(1,2,\ldots,15\) zu erstellen, ist:
neuer.vektor <- c(1:15)
neuer.vektor # anzeigen
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Flexibler ist zudem die Verwendung der Funktion seq()
.
Dieser Funktion kann ein Start- und ein Endwert übergeben werden und
dann entweder die Schrittweite oder die angestrebte Länge des Vektors
spezifiziert werden:
neuer.vektor <- seq(from = 1, # von 1...
to = 11, # ...bis 11...
by = 2) # ...in 2er-Schritten
neuer.vektor
## [1] 1 3 5 7 9 11
# Anmerkung:
# Wir verwenden hier oft die ausführliche Schreibweise wie eben, um die Bedeutung
# der Parameter klar zu machen. Der besseren Lesbarkeit halber schreiben wir dann die
# Argumente oft untereinander. Wenn Sie sich mit Funktionen sicherer fühlen und wissen,
# in welcher Reihenfolge Argumente übergeben werden, könnten Sie hier z.B. auch einfach
# kurz schreiben:
neuer.vektor <- seq(1, 11, 2)
Das Argument length.out
kann spezifiziert werden, wenn
Start- und Endwert sowie die Anzahl der gewünschten Elemente feststeht.
R berechnet dann automatisch die passende Schrittweite:
neuer.vektor <- seq(from = 1,
to = 10,
length.out = 19) # es sollen 19 Elemente im Vektor sein
neuer.vektor
## [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0
## [16] 8.5 9.0 9.5 10.0
Wiederholungen von Elementen können schließlich mit der Funktion
rep()
realisiert werden, wobei hier zwei Fälle zu
unterscheiden sind. Wollen wir z.B. einen Vektor mit den Elementen 1, 2
und 3 vier-mal wiederholen, so müssen wir das Argument
times
spezifizieren:
vektor <- c(1,2,3)
neuer.vektor <- rep(x = vektor, # Was soll wiederholt werden?
times = 4) # Wie oft wiederholen?
# oder kurz: neuer.vektor <- rep(vektor, 4)
neuer.vektor
## [1] 1 2 3 1 2 3 1 2 3 1 2 3
Wollen wir hingegen alle Elemente des Vektors mit den Elementen 1, 2
und 3 vier-mal wiederholen, so müssen wir das Argument each
spezifizieren:
vektor <- c(1,2,3)
neuer.vektor <- rep(x = vektor, # Was soll wiederholt werden?
each = 4) # Wie oft wiederholen?
neuer.vektor
## [1] 1 1 1 1 2 2 2 2 3 3 3 3
Bis hierhin haben wir nur Vektoren numerischer Variablen erstellt. Natürlich können Sie auch einen Vektor mit Bool-Variablen oder Strings erstellen:
bool.vektor <- c(FALSE, TRUE, FALSE, FALSE, TRUE)
bool.vektor # ausgeben als ganzen Vektor
## [1] FALSE TRUE FALSE FALSE TRUE
string.vektor <- c("männlich", "weiblich", "weiblich", "divers", "männlich")
string.vektor # ausgeben als ganzen Vektor
## [1] "männlich" "weiblich" "weiblich" "divers" "männlich"
Wir können auch – und das ist oft viel wichtiger – auf einzelne Elemente eines Vektors zugreifen. Hierfür wird der Index (also die Position) in eckigen Klammern hinter dem Namen des Vektors angegeben (im Gegensatz zu z.B. C++ hat das erste Element in R den Index 1 [statt 0]):
vektor <- c(2, 4, 6)
vektor[1]
## [1] 2
Mit Vektoren zu arbeiten ist vermutlich ein wenig
gewöhnungsbedürftig; es wird sich aber schnell eine gewisse Intuition
dafür einstellen, wie flexibel R hierbei tatsächlich ist. Fangen wir
damit an herauszufinden, wieviele Elemente ein Vektor besitzt. Hierfür
gibt die Funktion length()
:
vektor <- c(1, 2, 3) # Vektor erstellen
length(vektor) # Länge des Vektors berechnen...
## [1] 3
Eine schöne Sache ist, dass wir auch viele der grundlegenden Operatoren direkt auf Vektoren anwenden können. Wenn wir z.B. zu jedem Element des Vektors 3 addieren wollen, dann geht das ganz einfach mit:
vektor <- vektor + 3
vektor
## [1] 4 5 6
Ganz ähnlich können wir auch subtrahieren, multiplizieren und andere Rechnungen auf alle Elemente des Vektors anwenden. Wenn wir stattdessen gezielt z.B. das zweite Element mit 5 multiplizieren wollen, dann müssen wir durch den Index auf dieses Element zugreifen:
vektor[2] * 5
## [1] 25
Wir können dies sogar tun und gezielt das zweite Element dann im Vektor verändern:
vektor[2] <- vektor[2] * 5
vektor
## [1] 4 25 6
Auch zwei (gleich lange) Vektoren können (elementweise) auf verschiedene Arten miteinander verrechnet werden:
a <- c(1, 2, 3)
b <- c(6, 5, 4)
a + b
## [1] 7 7 7
a * b
## [1] 6 10 12
a / b
## [1] 0.1666667 0.4000000 0.7500000
a ^ b
## [1] 1 32 81
Wie berechnen wir nun den Mittelwerte der Elemente des Vektors
vektor
? Das arithmetische Mittel ist definiert als die
Summe der Elemente dividiert durch die Anzahl der Elemente, also: \[M = \frac{1}{n} \sum_{i=1}^n x_i \] Das
heißt, wir können die (drei) Elemente von vektor aufsummieren und dann
durch die Länge des Vektors dividieren:
(vektor[1] + vektor[2] + vektor[3] ) / length(vektor)
## [1] 11.66667
Einfacher können wir allerdings auch die schon bekannte Funktion
mean()
sinnvoll verwenden: Wenn wir als Argument einen
Vektor übergeben, dann berechnet diese den Mittelwert der Elemente des
Vektors:
mean(vektor)
## [1] 11.66667
Eine weitere wichtige Funktion ist sum()
, die die Summe
aller Elemente eines Vektors liefert:
sum(vektor)
## [1] 35
Würde es die Funktion mean()
nicht geben, könnten wir
ganz einfach und flexibel (ohne vorherige Kenntnis darüber, wieviele
Elemente ein Vektor besitzt) mit sum()
und
length()
den Mittelwert berechnen als:
sum(vektor) / length(vektor)
## [1] 11.66667
R erlaubt uns sogar, logische Operatoren elementweise auf Vektoren
anzuwenden. Wir könnten z.B. daran interessiert sein festzustellen,
wieviele Elemente eines Vektors kleiner als 3 sind. Hierzu benutzen wir
zunächst einen (logischen) Operator, den wir auf den Vektor anwenden und
der uns dann einen ganzen Vektor von Bool-Variablen zurückgibt. Auf
diesen wenden wir dann sum()
an, um diese Anzahl der
TRUE
s zu zählen:
vektor <- c(4, 2, 3, 6, 5, 7, 1, 2, 4)
kleiner.als.drei <- vektor < 3 # hier der Vergleich
kleiner.als.drei # Bool-Vektor anzeigen
## [1] FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE FALSE
sum(kleiner.als.drei) # Summe der TRUEs
## [1] 3
Kürzer, aber verschachtelter, könnten wir auch schreiben:
sum(vektor < 3)
## [1] 3
Warum entspricht hier die Summe des Bool-Vektors der Anzahl der
TRUE
s in diesem Vektor? Das liegt daran, dass intern ein
TRUE
als 1 kodiert wird und ein FALSE
als
0.
Matrizen sind zunächst Erweiterungen von Vektoren, oder anders
ausgedrückt: ein Vektor ist eine Matrix mit einer Spalte bzw. Zeile.
Matrizen können aber generell mehr als eine Zeile und mehr als eine
Spalte umfassen. Während manche Dinge in R direkt als Matrix existieren,
können wir auch selber Matrizen u.a. mit der Funktion
matrix()
erstellen. Als nächstes erstellen wir zur Übung
daher die folgende Matrix: \[A=
\begin{bmatrix}
1 & 2 & 3 \\
4 & 5 & 6
\end{bmatrix}\]
Um ein Gefühl für die Verwendung (noch) unbekannter Funktionen zu
bekommen, schauen wir uns zunächst die Hilfe zur Funktion
matrix()
an:
?matrix
Hier sehen wir, dass matrix()
zunächst einen Vektor
(data = NA
) benötigt, in welchem die einzelnen Werte
vorhanden sind. Danach gibt es zwei Argumente nrow
und
ncol
, die die Anzahl der Zeilen (rows) und Spalten
(columns) bestimmen. Das vierte Argument byrow
hat den
Standardwert FALSE
: Standardmäßig werden daher die Spalten
der Matrix mit den Elementen des Vektors “aufgefüllt”. Wie dieses
Argument gesetzt werden sollte, hängt von der Reihenfolge im Datenvektor
ab (ändern Sie den Wert einmal und schauen sich an, wie die Matrix dann
ausschaut):
a <- c(1, 2, 3, 4, 5, 6) # Vektor mit den Daten
A <- matrix(data = a,
nrow = 2,
ncol = 3,
byrow = TRUE)
# oder auch kurz: A <- matrix(a, 2, 3, TRUE)
A # Matrix anzeigen
## [,1] [,2] [,3]
## [1,] 1 2 3
## [2,] 4 5 6
Die Funktion matrix()
ist vor allem nützlich, wenn
größere Datenmengen in eine Matrix strukturiert werden soll. Für
kleinere Matrizen (insbesondere mit vorgegebenen) Werten bietet sich die
Funktion rbind()
(“row bind”) an, die Vektoren
untereinander kopiert und deren Resultat daher eine Matrix ist. Die
Matrix A
kann daher auch erstellt werden mit:
A <- rbind(c(1, 2, 3), # 1. Zeile
c(4, 5, 6)) # 2. Zeile
A # Matrix anzeigen
## [,1] [,2] [,3]
## [1,] 1 2 3
## [2,] 4 5 6
Ein Vorteil dieser Variante ist, dass man sehr einfach sieht, wie die
Matrix später aussehen wird. Die Funktion cbind()
(“column
bind”) kann genutzt werden, um Vektoren nebeneinander zu kopieren:
B <- cbind(c(1, 2, 3), # 1. Spalte
c(4, 5, 6)) # 2. Spalte
B # Matrix anzeigen
## [,1] [,2]
## [1,] 1 4
## [2,] 2 5
## [3,] 3 6
Auf Matrizenelemente kann, ähnlich wie bei Vektoren, auch einzeln
zugegriffen. Der Unterschied ist nun, dass sowohl die Zeile (als erstes)
als auch die Spalte (als zweites) indiziert werden muss (getrennt durch
ein Komma). Auf die \(5\) in der
zweiten Zeile und zweiten Spalte der Matrix A
kann z.B.
zugegriffen werden mit:
A[2,2]
## [1] 5
Um auf ganze Zeilen oder Spalten zuzugreifen, lassen Sie einfach den Index der anderen Dimension weg:
A[1,] # erste Zeile (alle Spalten)
## [1] 1 2 3
A[,1] # erste Spalte (alle Zeilen)
## [1] 1 4
Für fortgeschrittene Verwendungen sind hier noch weitere Eingrenzungen möglich. Um z.B. die (beiden) Elemente der ersten zwei Spalten der zweiten Zeile zu extrahieren, können Sie schreiben:
A[2,1:2] # Zeile 2 - Spalten 1-2
## [1] 4 5
Manche bereits bekannten Funktionen können problemlos auch auf Matrizen angewendet werden:
length(A) # wieviele Elemente gibt es in A?
## [1] 6
mean(A) # Mittelwert aller Elemente
## [1] 3.5
sum(A) # Summe aller Elemente
## [1] 21
Um die Anzahl der Zeilen und Spalten (also die Dimensionen einer
Matrix) zu ermitteln, können wir die Funktion dim()
verwenden. dim()
liefert nun keine einzelne Zahl zurück,
sondern einen Vektor mit zwei Elementen, auf die wir – wie oben
eingeführt – zugreifen können. Das erste Element ist die Anzahl der
Zeilen, das zweite Element die Anzahl der Spalten:
dim(A) # Dimensionen ermitteln
## [1] 2 3
dim(A)[1] # Anzahl Zeilen
## [1] 2
dim(A)[2] # Anzahl Spalten
## [1] 3
Oft wird es ein Interesse an der Summe oder dem Mittelwert der Spalten oder Zeilen geben. Hierfür gibt es natürlich auch Funktionen:
rowSums(A) # Summen der Zeilen
## [1] 6 15
colSums(A) # Summen der Spalten
## [1] 5 7 9
rowMeans(A) # Mittelwerte der Zeilen
## [1] 2 5
colMeans(A) # Mittelwerte der Spalten
## [1] 2.5 3.5 4.5
Fast immer, wenn wir einen existierenden Datensatz nutzen und einlesen, werden wir es mit sog. DataFrames zu tun haben. DataFrames sind ganz ähnlich organisiert wie auch in anderen Programmen wie Excel oder SPSS und bieten viele Vorteile bei ihrer Nutzung.
Zum Einstieg erstellen wir uns nun einen ersten DataFrame, der zu
zehn Versuchspersonen jeweils ihr Geschlecht, ihr Alter und ihre
Körpergröße in cm enthält. Dazu speichern wir zunächst die Inhalte der
Variablen in Vektoren und fügen diese dann zu einem DataFrame namens
daten
zusammen:
vp <- c(1:10)
geschlecht <- c("m", "m", "w", "m", "w", "w", "w", "m", "m", "w")
alter <- c(23, 41, 23, 45, 34, 31, 29, 30, 21, 34)
groesse.cm <- c(156, 182, 174, 176, 156, 187, 156, 166, 190, 189)
daten <- data.frame(vp, geschlecht, alter, groesse.cm)
daten # in der Konsole anzeigen
In der Variablenumgebung sollte der DataFrame daten
nun
angezeigt werden. Ein Klick auf diesen öffnet den DataFrame und das
Fenster sollte so wie in der folgenden Abbildung aussehen.
Eine Alternative zur Erstellung eines DataFrames inklusive der
Möglichkeit, Daten einzugeben, Variablennamen zu vergeben und den Typ
dieser Variablen zu verändern, bietet der Dateneditor von R. Im
Vergleich zu SPSS oder Excel ist dieser nicht sonderlich umfangreich,
aber er tut seine Dienste. Um z.B. einen neuen DataFrame mit dem Namen
neue.daten
zu erstellen, geben Sie ein:
neue.daten <- edit(as.data.frame(NULL))
# NULL gibt an, dass es sich um einen leeren DataFrame handeln soll
Durch einen Klick auf die grau-unterlegten Bereiche mit den
Überschriften var1
usw., wird ein kleines Fenster zur
Änderung des Variablennamens und -typs geöffnet.
Hinweis: Auf Macs kommt es scheinbar manchmal zu
Problemen. Dann können Sie alternativ einen DataFrame mit
voreingestellten Daten öffnen und diese dann einfach verändern:
neue.daten <- edit(data.frame(var1 = c(1, 1, 1)))
Im Folgenden gehen wir davon, dass das DataFrame daten
so wie gerade erstellt existiert.
Um einfach zu erfahren, wie die Namen der Variablen eines DataFrame
sind, kann die Funktion names()
verwendet werden:
names(daten)
## [1] "vp" "geschlecht" "alter" "groesse.cm"
Im Wesentlichen liefert diese Funktion einen Vektor zurück und Variablennamen können geändert werden, indem die Werte dieses Vektors verändert werden. Wollen wir also – aus irgendwelchen Gründen – weniger Lesbarkeit im Datensatz haben, könnten wir z.B. neue Namen wie folgt als einen Vektor aus Strings einfach angegeben:
names(daten) <- c("x1", "x2", "x3", "x4") # Zuweisen eines Vektors mit neuen Namen
names(daten)
## [1] "x1" "x2" "x3" "x4"
Mithilfe der Verwendung von Indizes können wir natürlich auch einzelne Variablennamen ändern:
names(daten)[2] <- "neuer.name" # 2. Variable soll neuen Namen bekommen
names(daten)
## [1] "x1" "neuer.name" "x3" "x4"
Im Folgenden benutzen wir aber nun wieder die Originalnamen:
names(daten) <- c("vp", "geschlecht", "alter", "groesse.cm")
Die wichtigste Strukturierung in einem typischen DataFrame mit (empirischen) Daten ist, dass verschiedene Variablen in Spalten angeordnet sind (mit ihren Namen jeweils als erstes) und jede Zeile einen einzelnen Fall (z.B eine Versuchsperson) beinhaltet. Nun ist es für die praktische Arbeit notwendig, auf einzelne Variablen eines DataFrames zugreifen zu können. Hierfür gibt es zwei Möglichkeiten:
$
”-Operator um per Namen auf die
Variable zuzugreifen.Wollen Sie also bspw. auf die Variable “Geschlecht” zugreifen, sind die folgenden Varianten äquivalent (wir werden i.d.R. mit dem “$”-Operator arbeiten):
daten[2] # per Index
daten$geschlecht # per Namen
Eine neue Variable zu einem bestehenden DataFrame hinzuzufügen geht
ganz einfach, indem Sie einfach den Namen angeben und den gewünschten
Wert zuweisen. Wollen Sie z.B. eine Variable mit dem Namen
eins
hinzufügen und jede Versuchsperson soll den Wert 1 auf
dieser Variablen erhalten, dann geht dies mit:
daten$eins <- 1
Sie können auch direkt neue Variablen aus bestehenden Variablen erstellen und dabei auch die verschiedenen mathematischen Operatoren verwenden. Als Beispiel erstellen wir nun eine Variable, in welcher die Größe nicht in cm, sondern in m gespeichert wird. Dazu muss bekanntlich der Wert in cm durch 100 dividiert werden:
daten$groesse.m <- daten$groesse.cm / 100
Um eine Variable zu löschen, setzen Sie diese einfach auf den Wert
NULL
:
daten$eins <- NULL
Bisher haben wir Variablen und auch den DataFrame daten
per Hand geschaffen. Es wäre aber wenig hilreich, wenn wir größere
Datensätze immer wieder mühsam als Vektoren eingeben müssten, um daraus
einen DataFrame herzustellen. Daher bekommen wir größere oft als
komplette Datei, die wir dann einlesen müssen. Natürlich kann R auch
bestehende Variablen, … speichern und ebenso wieder laden. Der erste
Teil dieses Kapitels beschreibt daher Grundlagen des Ladens und
Speicherns von Daten und Variablen, bevor wir dann mit Möglichkeiten zum
Handling größerer Datensätze fortfahren (z.B. Auswahl bestimmte Fälle
oder Variablen, Umkodieren von Variablen, …).
Zum Ausprobieren der folgenden Ausführungen haben wir Beispieldaten
in der Datei beispieldaten_R_einfuehrung.zip
zusammengestellt, die Sie hier herunterladen können.
Dann speichern Sie den darin enthaltenen Ordner
(beispieldaten_R_einfuehrung
) lokal auf Ihrem Computer,
damit die Datensätze zur Verfügung stehen. Damit R weiß, wo sich Daten
befinden (oder auch: wo Sie Daten hinspeichern möchten), muss zunächst
das aktuelle Arbeitsverzeichnis gesetzt werden. Dies geschieht mit der
Funktion setwd()
, der der entsprechende Pfad übergeben
wird. Angenomen, der Ordner befindet sich auf dem Laufwerk
D
im Ordner Studium
, dann wäre die Angabe:
setwd("D:/Studium/beispieldaten_R_einfuehrung")
Sie können auch choose.dir()
verwenden, um einen Ordner
mit der Maus auszuwählen. Die Funktion gibt dann den Pfad zurück, den
Sie direkt wie im folgenden Beispiel in einer Variablen speichern und an
setwd()
übergeben können:
verzeichnis <- choose.dir() # Aufrufen und in verzeichnis speichern...
setwd(verzeichnis) # ...und dann an setwd() übergeben.
# oder kürzer:
# setwd(choose.dir())
Wenn Sie herausfinden wollen, in welchem Arbeitsverzeichnis Sie sich
gerade befinden, geht dies mit der Funktion getwd()
:
getwd()
Im Folgenden wird immer davon ausgegangen, dass das Arbeitsverzeichnis entsprechend gesetzt ist.
R kann Daten, Variablen, Funktionen, … in einem eigenen Format mit
der Endung *.RData
speichern. Im Ordner befindet sich eine
Datei mit dem Namen beispiel_fuer_R_daten.RData
. Wir
löschen nun alle existierenden Variablen und laden dieses Beispiel, in
welchem sich das DataFrame daten
und eine Bool-Variable mit
Namen testvariable
befindet, die Ihnen nach dem Laden in
der Variablenumgebung angezeigt werden und mit denen Sie nun arbeiten
können:
remove(list = ls())
load(file = "beispiel_fuer_R_daten.RData")
Die meisten Datensätze liegen allerdings in anderer Form vor, oft als
*.txt
-Dateien, wobei einzelne Variable z.B. durch
Tabulatorstopps oder Semikolons getrennt sein können. Beispiele für
beide Varianten finden sich ebenfalls im Ordner (diese Dateien können
Sie auch mit dem Windows Notepad o.ä. öffnen und anschauen). Zum Laden
solcher Daten kann die Funktion read.table()
genutzt
werden. Neben dem Dateinamen übergeben wir als Argumente das jeweilige
Trennzeichen (sep
; “\t
” steht für
“Tabulatorstop”) und legen fest, dass in der ersten Zeile die Namen der
Variablen stehen (header
). Die eingelesenen Daten stehen
dann als DataFrame zur Verfügung:
# laden mit Tabulatorstopps: \t steht für Tabulatorstopp
daten <- read.table(file = "bsp_dataframe_tabs.txt", # Name der Datei
header = TRUE, # 1. Zeile = Variablennamen
sep = "\t") # Trennung durch Tabulator
#
# laden mit Semikolontrennung:
daten <- read.table(file = "bsp_dataframe_semis.txt", # Name der Datei
header = TRUE, # 1. Zeile = Variablennamen
sep = ";") # Trennung durch Semikolon
Um das Einlesen von Daten zu vereinfachen gibt es in R Studio ein Interface, das sie unter Import Dataset oben rechts in der Variablen-Umgebung finden (oder im Menü File - Import Dataset). Aus der Auswahl möglicher Dateiformate könnten Sie z.B. From Text (base) auswählen, wenn es sich um als Text gespeicherte Daten handelt. Nach der Auswahl der Datei öffnet sich die folgende Dialogbox: Die einzelnen Bestandteile der Dialogbox bedeuten dabei folgendes:
Ähnliche Möglichkeiten finden Sie in der Dialogbox, die sich unter From Text (readr) öffnet. Dort sehen Sie auch, wie sich die gemachten Einstellungen in R Code übersetzen. So nützlich Dialogboxen auch erscheinen, es ist am Ende immer ratsam, auch Dateien per Code zu Beginn eines R Skripts zu lesen, wenn die Daten in dem Skript dann ausgewertet werden.
Um verschiedene Variablen, … im RData-Format zu speichern, kann die
Funktion save()
genutzt werden. Im Beispiel werden
daten
und testvariable
in die Datei
beispielname.RData
gespeichert:
save(list = c("daten","testvariable"),
file = "beispielname.RData")
Um das DataFrame daten
ähnlich wie im Ordner zu
speichern, können Sie die Funktion write.table()
wie folgt
benutzen (hier als Beispiel mit Semikolontrennung):
write.table(file = "beispielname.txt", # Name der Datei
daten, # DataFrame
sep = ";", # Trennung durch Semikolon
quote = FALSE, # keine Anführungsstriche um Werte
row.names = FALSE) # Zeilen nicht nummerieren
Bisher haben wir recht einfache Daten benutzt und Funktionen auf diese angewendet. Mit “echten” Daten müssen mitunter mehr vorbereitende Schritte durchgeführt werden, z.B. müssen mehrere Datensätze miteinander verbunden werden, Fälle ausgewählt werden oder Daten umkodiert werden. Wir werden nun eine Reihe von Funktionen kennenlernen, die solche Manipulationen ermöglichen.
Manchmal sind Daten auf mehrere Dateien oder DataFrames verteilt und
müssen vor einer Auswertung zusammengefügt werden. Hierbei können wir
grob zwei Varianten unterscheiden, nämlich das Hinzufügen von Fällen und
das Hinzufügen von Variablen. Beide Varianten können wir an
Beispieldaten ausprobieren; dazu laden Sie zunächst die Datei
dreidatensaetze.RData
aus dem Datenordner. Dadurch sollten
drei DataFrames aktiv sein (daten.teil1
,
daten.teil1_neu
und daten.teil2
).
load("dreidatensaetze")
In diesem Fall haben beide DataFrames die gleichen Variablen,
allerdings von verschiedenen Personen (jeweils \(n=4\) Personen). Um die Gesamtdaten
auswerten zu können, fügen wir diese mit der schon bekannten Funktion
rbind()
zu einem neuen DataFrame namens daten
zusammen, welcher dann \(n=8\) Personen
enthält:
daten.teil1 # Teil 1 ausgeben
daten.teil2 # Teil 2 ausgeben
daten <- rbind(daten.teil1, # Zusammenfassen Teil 1....
daten.teil2) # ..und Teil 2
daten # alle Daten zusammen ausgeben
Eine Voraussetzung hierfür ist, dass beide DataFrames die gleichen
Variablen besitzen (die Reihenfolge ist nicht wichtig, aber Anzahl und
Namen). Ist dies nicht der Fall, hilft die Funktion
rbind.fill()
aus dem Paket plyr
weiter.
Dieser Fall liegt bspw. vor, wenn Sie die Daten der gleichen Personen
auf verschiedene Dateien verteilt haben. Zum Beispiel könnte dies bei
Längsschnittstudien auftreten, wenn Sie zunächst die Daten des ersten
Messzeitpunktes eingeben und später in einer eigenen Datei dann die
Daten des zweiten Messzeitpunktes. In den DataFrames
daten.teil1
und daten.teil1_neu
gibt es
jeweils Daten von den gleichen vier Personen. Um diese zu einem
Gesamtdatensatz zusammen zu fügen, nutzen Sie die Funktion
merge()
. Wichtig ist nun, dass es eine Indikatorvariable
gibt, die angibt, wie die einzelnen Fälle in beiden DataFrames einander
zugeordnet werden sollen. Im vorliegenden Fall nehmen wir dazu die
Variable vp
, die jeweils eine Nummer für jede Person
angibt:
daten.neu <- merge(daten.teil1, # 1. Datei
daten.teil1_neu, # 2. Datei
by = "vp") # Indikatorvariable, die in beiden Dateien existiert
daten.neu
Gäbe es in diesem Beispiel verschiedene Personen in beiden
DataFrames, dann enthält der zusammengesetzte DataFrame nur die Daten
derjenigen Personen, die in beiden DataFrames vorkamen. Sollen dennoch
alle Fälle verwendet werden, kann dies durch Hinzufügen des Parameters
all = TRUE
erzwungen werden. Die nicht vorhandenen Werte
werden dann als fehlend gekennzeichnet.
Heißen die Indikatorvariablen in beiden DataFrames verschieden, kann
statt by
eine genauere Spezifikation mit by.x
und by.y
angegeben werden, wobei damit jeweils die
Variablennamen im ersten und zweiten DataFrame gemeint sind:
daten.neu <- merge(daten.teil1, # 1. Datei
daten.teil1_neu, # 1. Datei
by.x = "vp", # Indikatorvariabe der 1. Datei
by.y = "nr") # Indikatorvariabe der 2. Datei
Falls es keine Indikatorvariable gibt, kann auch die Funktion
cbind()
verwendet werden, um neue Spalten (also Variablen)
zu einem bestehenden Datensatz hinzuzufügen. Allerdings muss hier sehr
darauf geachtet werden, dass z.B. die Versuchspersonen in beiden
Datensätzen in der selben Reihenfolge sind!
Ein sehr häufig gebrauchter Fall ist, dass wir nur eine Auswahl
bestimmter Fälle betrachten wollen. Wir gehen im folgenden Beispiel
davon aus, dass der DataFrame daten
die oben
zusammengesetzen Daten der \(n=8\)
Personen umfasst. Wir wollen nun ein DataFrame subdaten
erstellen, welches nur die Daten von Personen unter 20 Jahren
enthält.
Eine erste Möglichkeit dazu bietet die eckige Klammer, die auch schon
vorher zur Auswahl spezifischer Zeilen und Spalten in Vektoren und
Matrizen verwendet wurde. Wenn ein logischer Vergleich in diese Klammer
eingesetzt wird, dann werden nur die Zeilen ausgegeben, für die der
resultierende Bool-Vektor den Wert TRUE
annimmt, – also die
dort formulierte Bedingung zutrifft:
subdaten <- daten[daten$alter < 20,]
subdaten
Etwas genauer beinhaltet die Indizierung innerhalb der eckigen
Klammer zwei Ausdrücke, die durch ein Komma getrennt sind. Hier ist die
Indizierung genau wie bei einer Matrix: Wir wollen alle Zeilen
extrahieren, auf die alter < 20
zutrifft, von diesen
Zeilen aber alle Spalten. Daher steht hinter dem Kopf wieder nichts.
Eine andere Möglichkeit bietet die Funktion subset()
,
der Sie einerseits das DataFrame übergeben und dann als Argument
subset
einen logischen Ausdruck, in unserem Fall also
alter < 20
. Ausgewählt werden dann alle Fälle, auf die
dieser Ausdruck zutrifft:
subdaten.3 <- subset(daten,
subset = alter < 20) # welche Fälle/Zeilen
subdaten.3
Die Funktion subset()
kann auch leicht dazu verwendet
werden, einzelne Variablen aus einem DataFrame zu extrahieren. Dazu
geben Sie einfach die Namen der gewünschten Variablen (in einem Vektor)
dem Argument select
an (wenn das Argument
subset
weggelassen wird, werden alle Fälle ausgewählt):
subdaten.4 <- subset(daten,
subset = alter < 20, # welche Fälle/Zeilen
select = c(vp, geschlecht)) # welche Variablen/Spalten
subdaten.4
In Kapitel 2 haben wir bereits gelernt, wie einem DataFrame eine neue Variable hinzugefügt wird. Häufig brauchen wir auch eine Möglichkeit, Werte einer Variablen in andere Werte einer neuen Variablen umzukodieren.
Nehmen wir an, wir wollen die Variable alter
so in eine
neue Variable altersstufe
umkodieren, dass nur noch die
Unterscheidungen jung
(\(<20\) Jahre), mittel
(20
oder älter, aber jünger als 40) und alt
(\(\geq 40\) Jahre) vorliegen. Im Wesentlichen
geht dies ganz ähnlich wie die Auswahl über Indizierung, nur dass wir
hier den ausgewählten Fällen einen Wert entsprechend zuweisen:
daten$altersstufe <- "" # neue String-Variable anlegen
# und dann die Werte der Variable alter auf die angelegte Variable umkodieren
daten$altersstufe[daten$alter < 20] <- "jung"
daten$altersstufe[(daten$alter >= 20) & (daten$alter < 40)] <- "mittel"
daten$altersstufe[daten$alter >= 40] <- "alt"
daten
Zudem bietet das Paket car
eine Funktion namens
recode()
, die zum gleichen Zweck verwendet werden kann und
wie folgt verwendet wird:
library(car)
daten$altersstufe.2 <- recode(daten$alter,
"1:19 = 'jung'; 20:39 = 'mittel'; 40:100 = 'alt'" )
daten
Ist nur eine Umkodierung in 2 Stufen beabsichtigt, können wir
schließlich noch auf die Funktion ifelse()
zurückgreifen.
Dieser Funktion wird eine Bedingung übergeben und dahinter zwei Werte:
Wenn die Bedingung TRUE
ist, bekommt die neue Variable den
ersten Wert, ansonsten den zweiten Wert. Um z.B. alle Personen unter 40
Jahren als jung
und alle anderen als alt
zu
kodieren, kann folgende Zeile verwendet werden:
daten$alterstufe.3 <- ifelse(daten$alter < 40, # Bedingung
"jung", # wenn TRUE
"alt") # wenn FALSE
daten
In vielen Reaktionszeitexperimenten, aber auch anderen Studien. kommt
es vor, dass von einer Versuchsperson in den (within-subject)
Bedingungen mehrere Messwerte vorliegen, also z.B. die Reaktionszeiten
in den zahlreichen Durchgängen eines Experimentes. Ein erster Schritt
ist dann oft, den Mittelwert (oder manchmal auch den median) dieser
Werte zu bestimmen, sodass für jede Versuchsperson und Bedingung nur ein
Wert übrig bleibt. Dies nennt man “Daten aggregieren”. Im Datensatz
rohdaten
des nächsten Beispiels liegen von drei
Versuchspersonen jeweils 5 Messungen in den beiden Bedingungen
“kongruent” und “inkongruent” vor:
rohdaten # Beispieldaten
Eine nützliche Funktion zur Datenaggregation ist
aggregate()
. Das folgende Beispiel mittelt die Werte der
Variablen rt
für alle Kombinationen die sich aus
vp
und bedingung
ergeben.
aggregiert.mw <- aggregate(rt ~ vp + bedingung, # Werte und Zellen der Aggregation
data = rohdaten, # welcher Datensatz?
FUN = mean) # welche Funktion?
aggregiert.mw
Dem Argument FUN = ...
können prinzipiell alle
Funktionen übergeben, die dann auf die Daten angewendet werden. Wollen
Sie bspw. den Median der Werte berechnet haben, dann wäre die Übergabe
FUN = median
.
Daten können grundsätzlich in zwei verschiedenen Formaten auftreten, nämlich dem sog. wide-Format und dem sog. long-Format. Im wide-Format steht jede Zeile für einen Fall, also z.B. eine Versuchsperson. Wenn es mehrere Messwerte von dieser Person unter verschiedenen Bedingungen gibt, werden diese in verschiedene Variablen derselben Zeile eingetragen. Zum Beispiel: Nehmen wir an, wir würden die (mittleren) Reaktionszeiten (in Millisekunden) von Versuchspersonen messen unter verschiedenen Ablenkungsbedingungen, nämlich (1) Musik, (2) Gespräch und (3) und ohne Ablenkung im Raum. Bei zwei Versuchspersonen würden die Daten im wide-Format wie folgt aussehen:
wide_data
Wide-Format Daten haben also eine Zeile pro Versuchsperson und legen für weitere Versuchsbedingungen Extra-Variablen an. Long-format Daten haben nur eine Variable für jede gemessene abhängige Variable (hier also Reaktionszeit) und die verschiedenen Bedingungen auf verschiedene Zeilen aufgeteilt und durch eine Gruppierungsvariable unterschieden. Beide Formate haben ihre Vor- und Nachteile.
Während R in den allermeisten Fällen Daten im long-Format bevorzugt,
kann es aber sein, dass das wide-Format sinnvoller ist oder Sie Daten in
dem einen Format erhalten und diese in das jeweils andere Format
umwandeln wollen. Die einfachste Art Daten vom wide- ins long-Format zu
bringen ist die Funktion pivot_longer()
aus dem Paket
tidyr
.
library(tidyr)
long_data <- pivot_longer(wide_data, # wide-Format Daten
cols = c(2:4), # welche Spalten
names_to = "condition", # Gruppierungsvariable
values_to = "reaction_time") # Name der abh. Variablen
long_data
pivot_longer()
benötigt als Argumente einen Datensatz,
die Spalten in denen die zu transformierenden Werte sind, einen Namen
für die neue Variable, die die Namen der alten Variablen als
Ausprägungen enthält (condition
), und einen Namen für die
neue Variable, die alle gemessenen Werte enthält
(reaction_time
).
Und um aus dem long-Format eine Datei im wide-format zu machen, gibt
es die entsprechende Funktion pivot_wider()
aus
tidyr
.
wide_data2 <- pivot_wider(long_data, # long-Format Daten
names_from = condition, # Gruppierungsvariable
values_from = reaction_time) # Name der abh. Variablen
wide_data2
Bisher haben wir uns mit Vorbereitungen der Datenanalyse beschäftigt (z.B. Laden von Daten und Auswahl von Teildatensätzen) und Grundlegendes über die Arbeit mit R gelernt. Nun wollen wir beginnen, Daten zu beschreiben. Dazu werden wir zunächst deskriptive Statistiken betrachten und danach einen Blick auf die Erstellung einfacher Abbildungen zur Visualierung von Daten mit R werfen.
Für die folgenden Beispiele laden wir die Datei
hfgk.RData
, die einen DataFrame mit dem Namen
hfgk
bereitstellt. In diesem Datensatz haben die Variablen
var1
, var2
und var3
jeweils 2, 3
und 4 Ausprägungen. Eine einfache Häufigkeitstabelle für eine Variable,
z.B. hier var3
, können wir mit der Funktion
table()
erhalten:
tab <- table(hfgk$var3)
tab
##
## 1 2 3 4
## 21 28 29 22
Auch zweidimensionale Kontingenztafeln können mit der Funktion
table()
erstellt werden, indem wir einfach beide Variablen
übergeben:
tab <- table(hfgk$var1,
hfgk$var2)
tab
##
## 1 2
## 1 18 13
## 2 16 11
## 3 28 14
Mit solchen Tabellen können wir nun auch weiter arbeiten. Die
Funktion addmargins()
auf tab
angewendet fügt
die Randsummen hinzu und die Funktion prop.table()
angewendet auf tab
erstellt die Kontingenztafel mit
relativen Häufigkeiten:
addmargins(tab) # mit Randsummen
##
## 1 2 Sum
## 1 18 13 31
## 2 16 11 27
## 3 28 14 42
## Sum 62 38 100
prop.table(tab) # relative statt absolute Häufigkeiten
##
## 1 2
## 1 0.18 0.13
## 2 0.16 0.11
## 3 0.28 0.14
Verwenden wir bei prop.table()
noch den Parameter
margin
, können wir auch die bedingten relativen
Häufigkeiten berechnen:
addmargins(prop.table(tab,
margin = 1)) # bedingt nach Zeilen
##
## 1 2 Sum
## 1 0.5806452 0.4193548 1.0000000
## 2 0.5925926 0.4074074 1.0000000
## 3 0.6666667 0.3333333 1.0000000
## Sum 1.8399044 1.1600956 3.0000000
addmargins(prop.table(tab,
margin = 2)) # bedingt nach Spalten
##
## 1 2 Sum
## 1 0.2903226 0.3421053 0.6324278
## 2 0.2580645 0.2894737 0.5475382
## 3 0.4516129 0.3684211 0.8200340
## Sum 1.0000000 1.0000000 2.0000000
Prinzipiell können wir auch mehr als zwei Variablen berücksichtigen,
allerdings wird die Ausgabe dann leider schnell unübersichtlich. Hier
können wir besser die Tabellen mit xtabs()
erstellen und
mit ftable()
umformatieren (prop.table()
, ….
sind auch hier wiederum anwendbar):
ftable(xtabs(~ var1 + var2 + var3, # Häufigkeiten für die Kombination von?
data = hfgk)) # in welchem Datensatz?
## var3 1 2 3 4
## var1 var2
## 1 1 5 5 3 5
## 2 4 1 5 3
## 2 1 1 6 5 4
## 2 2 4 4 1
## 3 1 6 10 8 4
## 2 3 2 4 5
Für den Mittelwert und den Median
von Daten gibt es in R eigene Funktionen. Zur Demonstration erstellen
wir hier einen Vektor namens daten
, die Funktionen können
aber auch auf Variablen eines DataFrames angewendet werden:
daten <- c(4, 5, 3, 8, 6, 7, 8, 3, 6, 4, 2, 3, 8, 8, 8)
mean(daten) # Mittelwert
## [1] 5.533333
median(daten) # Median
## [1] 6
Für den Modus müssen wir einen kleinen Umweg gehen,
indem wir zunächst mit der Funktion table()
eine
Häufigkeitstabelle erstellen und daraus dann den am häufigsten
auftretenden Wert extrahieren:
tab <- table(daten) # Häufigkeitstabelle
tab
## daten
## 2 3 4 5 6 7 8
## 1 3 2 1 2 1 5
as.numeric(names(which.max(tab))) # Modus extrahieren
## [1] 8
Es lohnt sich allerdings, die Häufigkeitstabelle tatsächlich anzuschauen, da mit dieser Methode nur ein Modus (von mehreren möglichen) gefunden wird.
Das wichtigste Maß der Datenvariabilität ist die Varianz, also die
mittlere quadratische Abweichung der Datenpunkte von ihrem Mittelwert:
\[S_X^2=\frac{1}{n}\sum_{i=1}^n(x_i-M_X)^2\]
Zunächst berechnen wir hier die Varianz “von Hand” als Umsetzung der
Formel in R-Code am Beispiel des gerade erstellen Vektors
daten
:
n <- length(daten) # wieviele Datenpunkte gibt es?
varianz <- (1/n) * sum((daten-mean(daten))^2)
varianz
## [1] 4.648889
Natürlich gibt es auch eine R-Funktion zur Bestimmung der Varianz; allerdings kommt diese zu einem minimal anderem Ergebnis:
var(daten)
## [1] 4.980952
Dies liegt daran, dass var()
eine “korrigierte Varianz”
berechnet, die der erwartungstreue (und konsistente) Schätzer der
Populationsvarianz \(\sigma^2\) ist.
Der Unterschied ist, dass nicht durch \(n\), sondern durch \(n-1\) dividiert wird. Die korrigierte
Version berechnet sich also als: \[\hat{S}_X^2=\frac{1}{n-1}\sum_{i=1}^n(x_i-M_X)^2\]
Um das Ergebnis der Funktion var()
in die “normale” Varianz
umzurechnen, können wir unter Verwendung von \(n\) eine Korrektur anwenden und das gerade
per Hand errechnete Ergebnis erhalten:
(n-1)/n * var(daten)
## [1] 4.648889
Die Standabweichung berechnen wir als Wurzel der Varianz, also als
standardabweichung <- sqrt(varianz)
standardabweichung
## [1] 2.156128
oder in der korrigierten Form mit der entsprechenden R-Funktion
sd()
:
standardabweichung <- sd(daten)
standardabweichung
## [1] 2.231805
Es gibt natürlich auch Funktionen, mit denen wir direkt mehrere Maße
zur Beschreibung von Daten gewinnen. Eine Möglichkeit bietet die
Funktion summary()
, die auf ziemlich viele R-Objekte
angewendet werden kann und ihr Verhalten in Abhängigkeit davon ändert,
auf was sie angewendet wird. Um einen schnellen Überblick über die Werte
des Vektors daten
zu bekommen, können wir z.B.
schreiben:
summary(daten)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 2.000 3.500 6.000 5.533 8.000 8.000
Etwas ausführlicher und umfangreicher ist die Funktion
describe()
aus dem Paket psych
, die neben
bekannten Werten auch weitere Werte ausgibt:
library(psych)
describe(daten)
Beide Funktionen können wir auch auf DataFrames anwenden. Für die
folgenden Varianten gehen wir davon aus, dass die Datei
datensatz.RData
geladen wurde, die einen DataFrame namens
datensatz
enthält:
head(datensatz, # Die Funktion head() gibt die ersten...
n = 3) # ...(hier 3) Zeilen eines DataFrames aus
Die erste Variable vp
bezeichnet die Nummer der
Versuchsperson, die zweite, dritte und vierte Variable
(var1
, var2
und var3
) sind dann
jeweils Werte der Personen auf Variablen und die fünfte Variable
gruppe
gibt an, welcher von zwei Gruppen die Versuchsperson
angehört. Die Funktion summary()
kann dann ganz einfach
verwendet werden – da uns aber nur die drei Variablen mit gemessenen
Werten interessieren, wählen wir entsprechend diese drei Spalten
aus:
summary(datensatz[2:4])
## var1 var2 var3
## Min. : 68.00 Min. : 2.00 Min. : 1.00
## 1st Qu.: 89.75 1st Qu.:12.00 1st Qu.:33.89
## Median : 99.00 Median :23.50 Median :39.66
## Mean :100.68 Mean :24.46 Mean :38.54
## 3rd Qu.:112.25 3rd Qu.:36.25 3rd Qu.:44.88
## Max. :143.00 Max. :49.00 Max. :49.61
Ganz ähnlich können wir auch die Funktion describe()
verwenden:
describe(datensatz[2:4])
Im Beispieldatensatz haben wir ja zudem Personen aus zwei
verschiedenen Gruppen. Wir können diese Informationen auch direkt
einzeln für die beiden Gruppen erhalten, wenn wir die Funktion
describeBy()
aus dem Paket psych
verwenden.
Die Gruppierungsvariable(n) müssen wir hier dann als sog. Liste (daher
hinter list()
) übergeben:
describeBy(datensatz[2:4], # welche Variablen?
list(datensatz$gruppe)) # getrennt für die Ausprägungen von gruppe
##
## Descriptive statistics by group
## : 1
## vars n mean sd median trimmed mad min max range skew kurtosis
## var1 1 50 101.58 15.14 101.00 101.60 16.31 69 140.00 71.00 0.08 -0.49
## var2 2 50 24.22 14.76 22.50 23.88 20.76 2 49.00 47.00 0.13 -1.39
## var3 3 50 38.73 7.88 39.68 39.26 8.17 2 48.83 46.83 -1.89 6.96
## se
## var1 2.14
## var2 2.09
## var3 1.11
## ------------------------------------------------------------
## : 2
## vars n mean sd median trimmed mad min max range skew kurtosis
## var1 1 50 99.78 16.15 99.00 99.08 17.79 68 143.00 75.00 0.36 -0.32
## var2 2 50 24.70 14.13 24.00 24.60 16.31 2 49.00 47.00 0.10 -1.14
## var3 3 50 38.35 9.34 39.46 39.53 8.15 1 49.61 48.61 -2.23 6.51
## se
## var1 2.28
## var2 2.00
## var3 1.32
Manchmal benötigen wir gruppenweise auch Informationen, die mit
summary()
oder describeBy()
nicht
bereitgestellt werden. Hier können wir auf die Funktion
tapply()
zurückgreifen, mit der wir eigene
Zusammenfassungen erstellen können. Hier übergeben wir i.W. die zu
beschreibende Variable, danach die Gruppierungsvariable und dann eine
Funktion, die auf die einzelnen Teildatensätze angewendet werden soll.
Um zu sehen, dass wir so auf die gleichen Werte kommen, wird im Beispiel
einfach der Mittelwert auf der Variablen var1
getrennt für
beide Gruppen berechnet:
tapply(X = datensatz$var1, # welche Variable?
INDEX = datensatz$gruppe, # getrennt für die Ausprägungen von gruppe
FUN = mean) # welche Funktion soll verwendet werden?
## 1 2
## 101.58 99.78
Darüber hinaus gibt es eine ganze Reihe anderer Funktionen, die
teilweise sehr ausführliche Beschreibungen von Daten liefern. Ein
Beispiel hierfür ist die Funktion dfSummary()
aus dem Paket
summarytools
.
R verfügt über sehr umfangreiche Fähigkeiten, hochwertige Grafiken zu
erstellen. Grundsätzlich können wir zwischen base
-,
lattice
- und ggplot2
-Abbildungen
unterscheiden; wir werden hier einfache base
- bzw.
lattice
-Abbildungen erstellen. ggplot2
ist das
wohl mächtigste Paket zur Erstellung von Abbildungen, es benutzt aber
eine eigene Syntax und wird daher in einem gesonderten Teil in
Verbindung mit tidyverse
behandelt werden (der aber erst
noch erstellt werden muss). Zahlreiche andere Einführungen und Beispiele
für Abbildungen – oft samt Code – sind auch im Netz zu finden. Eine
hervorragende Quelle (auch für die Inspiration zu verschiedenen
Abbildungen) ist die Seite https://www.r-graph-gallery.com/.
Im Wesentlichen gibt es für viele Arten von Grafiken eine einfache Funktion, der u.a. die entsprechenden Daten übergeben werden und welche dann eine entsprechende Abbildung erstellt (in RStudio erscheinen die Abbildungen im Fenster rechts unten). Diese Funktionen haben eine Vielzahl von Parametern, um die Abbildungen zu verfeinern. Zusätzlich gibt es eine Reihe von Funktionen, mit denen Elemente zu Grafiken hinzugefügt werden können (z.B. Legenden, Text, Punkte, Pfeile, …).
Einige Parameter gibt es für fast alle Arten von Grafiken und wir
werden die wichtigsten hier kurz besprechen. Übersichten über Parameter
gibt es auch hier: https://www.r-graph-gallery.com/cheatsheet/ und hier http://publish.illinois.edu/johnrgallagher/files/2015/10/BaseGraphicsCheatsheet.pdf.
Zudem liefert die R-Hilfe mit ?par
eine Übersicht.
Beginnen wollen wir mit zwei Arten von Parametern die i.d.R. für
mehrere Abbildungen gleichzeitig eine Rolle spielen. Zum Einen wollen
wir manchmal den äußeren Rand von Abbildungen variieren. Dies geschieht
mit dem Parameter mar
, dem ein Vektor mit vier Werten
übergeben wird, der die Anzahl von Linien am Rand unten, links, oben und
rechts spezifiziert. Der Standard ist hier
mar = c(5,4,2,2)+0.1
.
Standardmäßig plottet R immer eine Abbildung. Um mehrere Plots zu
kreieren, können wir die Parameter mfrow
bzw.
mfcol
benutzen, denen ein Vektor mit der Anzahl der Zeilen
und der Spalten übergeben wird, die eine Matrix definieren in der jede
Zelle dann mit einer eigenen Abbildung gefüllt wird. Wollen wir also
zwei Abbildungen nebeneinander plotten, wäre dies möglich mit
mfrow = c(1,2)
oder mfcol = c(1,2)
. (Aufgabe:
Finden Sie heraus, was der Unterschied zwischen beiden Parametern
ist!)
Beide Arten von Parametern (sowie andere Parameter aber auch) können übergreifend gesetzt werden und gelten dann für alle folgenden Abbildungen. Wollen wir z.B. 6 Abbildungen in 3 Zeilen und 2 Spalten plotten und die Ränder etwas vergrößern, dann geht das mit:
par(mar = c(6,6,3,3), mfrow = c(3,2))
Verwenden wir mfrow
werden die entstehenden Plots
zeilenweise aufgefült, bei mfcol
spaltenweise.
Andere Parameter, die typischerweise beim Aufruf einer bestimmten Funktion zur Erstellung einer Grafik gesetzt werden, sind (manche Parameter gelten nur für manche Plot-Funktionen):
main
: Ein String mit der Überschrift einer Grafik.xlab
und ylab
: Jeweils ein String mit der
Beschriftung der \(x\)- und \(y\)-Achsexlim
und ylim
: Jeweils ein Vektor mit
Minimum und Maximum der geplotteten \(x\)- und \(y\)-Achseaxes
: Bool-Variable, die angibt, ob Achsen geplottet
werden sollen (Standard; TRUE
) oder nicht
(FALSE
)plot
: Bool-Variable, die angibt, ob der Plot angezeigt
werden soll (Standard; TRUE
) oder nicht
(FALSE
)lty
und lwd
: eine Zahl, die den Typ und
die Stärke von Linien angibtpch
: eine Zahl, die den Typ von Punkten angibttype
: ein String, der für die Funktion
plot()
die Art des Plots spezifiziert ("p"
=
nur Punkte, "l"
= Linien, "b"
= Punkte und
Linien, "s"
= Treppenstufen, …)col
: ein String, ein numerischer Wert oder ein String-
oder numerischer Vektor, mit dem die Farben angegeben werden, die
verwendet werden sollen. Farben können im RGB-Raum mit der Funktion
rgb()
an col
übergeben werden, wobei hier auch
die Transparenz der Farben einstellbar ist.add
: Bool-Variable, die angibt, ob ein neuer Plot
erstellt werden soll (Standard; FALSE
) oder der Plot dem
aktuellen hinzugefügt werden soll (TRUE
)Die Größe aller oder einzelner Objekte wird bei R immer relativ mit
dem Parameter cex
angegeben (für character
expansion):
cex
: alle Elementecex.lab
: Achsenbeschriftungcex.axis
: Achsenwertecex.main
: TitelMit verschiedenen weiteren Funktionen können Abbildungen ergänzt werden. Im Folgenden werden einige solcher Funktionen kurz beschrieben und weiter unten bei den Beispielen dann eingesetzt (mehr Informationen gibt es immer mit der R-Hilfe):
points()
: Fügt Punkte zu einer Abbildung an den
angegebenen \(x\)- und \(y\)-Koordinaten hinzu.segments()
: Fügt Liniensegmente von einem Start- zu
einem Endpunkt hinzu.arrows()
: Fügt Pfeile von einem Start- zu einem
Endpunkt hinzu.text()
: Fügt Text an den angegebenen \(x\)- und \(y\)-Koordinaten hinzu.axis()
: Wenn im Plot `axes = FALSE´ gesetzt wird,
können hiermit die Achsen manuell hinzugefügt werden.legend()
: Fügt eine Legende zu einer Abbildung
hinzu.Wenn wir RStudio verwenden, werden die Grafiken in das Fenster rechts
unten geplottet. Mittels des Feldes Export
können wir dann
Grafiken speichern und in den Zwischenspeicher kopieren. Dies ist eine
einfache Möglichkeit; die Qualität der Abbildungen kann aber deutlich
erhöht werden, wenn wir die dazu gedachten Funktionen zum speichern
nutzen.
Diese Funktionen haben in der Regel den selben Aufbau: Am Anfang steht ein Funktionsaufruf, der das Dateiformat, die Größe und die gewünschte Auflösung spezifiziert und ein sog. Grafik-Device öffnet. Danach folgen dann die Zeilen, die die Abbildung erstellen und beendet wird das Ganze, indem das Device geschlossen wird. Am Beispiel einer jpg-Abbildung würde das so aussehen:
jpeg(filename = "beispielname.jpg"), width = 600, height = 400, res = 150)
#
# ... hier folgen die Zeilen, die die Abbildung erstellen
#
dev.off() # und das Device wird wieder geschlossen
Andere Formate werden z.B. mit den Funktionen bmp()
,
tiff()
oder auch pdf()
erstellt.
Manchmal ist es nötig, in Abbildungen mit mathematischen Zeichen und
Formeln zu arbeiten. Dazu eignen sich Strings nur sehr bedingt, aber wir
können dann die Funktion expression()
nutzen, die nach
einer bestimmten Syntax Formeln und andere Zeichen erlaubt. Genauere
Informationen über die Syntax finden Sie unter https://stat.ethz.ch/R-manual/R-devel/library/grDevices/html/plotmath.html.
Ein Beispiel für die Verwendung von expression()
finden Sie
in Abschnitt 3.2.2 (Liniendiagramme). Eine andere Möglichkeit besteht in
der Verwendung von bquote()
.
Scatterplots sind die typischen Darstellungen von Punktewolken, wenn z.B. bei \(n=100\) Versuchspersonen die zwei Variablen \(X\) und \(Y\) gemessen wurden. Dabei steht jeder Punkt dann für eine Person. Für das folgende Beispiel ziehen wir 50-mal einen zufälligen Wert zwischen 1 und 50, je einmal für die Variable \(X\) und einmal für die Variable \(Y\) und plotten diese dann erst einmal ohne weitere Parameter:
X <- sample(x = c(1:50), # 100 zufällige Vektor aus dem Bereich 1-50 ziehen
size = 100, # und als Variable X speichern (ist ein Vektor mit
replace = TRUE) # 100 Elementen)
Y <- sample(x = c(1:50), # 100 zufällige Vektor aus dem Bereich 1-50 ziehen
size = 100, # und als Variable Y speichern (ist ein Vektor mit
replace = TRUE) # 100 Elementen)
plot(X, Y) # einfachen Scatterplot erstellen
Um diesen Plot schöner zu gestalten, können wir nun einzelne Parameter nutzen und die Abbildung ergänzen:
# Plot ohne Achsen, rote Punkte
plot(X, Y, # Daten
main = "Ein hübscherer Scatterplot", # Titel
xlab = "Variable X", # Beschriftung x-Achse
ylab = "Variable Y", # Beschriftung y-Achse
axes = FALSE, # keine Achsen zeichnen
col = "red", # Farbe der Punkte
pch = 16, # Typ der Punkte
cex = 1.5) # Größenskalierung
# Nun fügen wir manuell die beiden Achsen hinzu... erst die x- und dann die y-Achse
axis(side = 1, # 1 = x-Achse
at = seq(from = 0, to = 50, by = 5), # an welche Stellen sollen Labels?
labels = seq(from = 0, to = 50, by = 5)) # die Labels selber
axis(side = 2, # 2 = y-Achse
las = 2, # dreht die Beschriftung um 90°
at = seq(from = 0, to = 50, by = 10),
labels = seq(from = 0, to = 50, by = 10))
abline(h = 0) # horizontale Linie bei y = 0
Auch Liniendiagramme können mit der Funktion plot()
geschaffen werden. Dazu müssen wir den Parameter type
entsprechend setzen. Im Beispiel unten gehen wir davon aus, dass 2
Gruppen unter 3 verschiedenen Bedingungen untersucht worden sind. Die
Werte der einen Gruppe malen wir direkt mit plot()
, die
Werte der anderen Gruppe fügen wir anschließend mit
points()
hinzu:
x <- c(1, 2, 3) # Werte der x-Achse
y1 <- c(6, 8, 9) # Gruppe 1, y-Werte
y2 <- c(4, 6, 5) # Gruppe 2, y-Werte
plot(x, y1, # Daten
type = "b") # "b" = both (Punkte und Linien)
points(x, y2, # fügt Punkte und Linen für Gruppe hinzu
type = "b")
Auch hier ändern wir diesen wenig schönen Standardplot nun entsprechend
ab, um ihn zu ergänzen und ansehnlicher zu gestalten. Unter anderem
müssen wir hier die Skalierung der \(y\)-Achse mit ylim
ändern,
damit die Werte von Gruppe 2 überhaupt zu sehen sind. Zur Illustration
tragen wir zudem noch einen – zugegebenermaßen sinnfreien – Text unter
Nutzung von expression()
ein:
x <- c(1, 2, 3) # Werte der x-Achse
y1 <- c(6, 8, 9) # Gruppe 1, y-Werte
y2 <- c(4, 6, 5) # Gruppe 2, y-Werte
plot(x, y1,
type = "b",
main = "Ein hübscherer Plot",
xlab = "Bedingung",
ylab = "Variable Y",
axes = FALSE,
ylim = c(0,10))
points(x, y2,
type = "b",
pch = 19) # Punkte der Gruppe 2 ausgefüllt in schwarz
# Achsen hinzufügen und beschriften
axis(side = 1,
at = x,
labels = x)
axis(side = 2,
las = 2,
at = seq(from = 0, to = 10, by = 2),
labels = seq(from = 0, to = 10, by = 2))
abline(h=0)
# Legende hinzufügen
legend(x = 1, y = 10, # Koordinaten der Legende
legend = c("Gruppe 1", "Gruppe 2"), # Beschriftung
pch = c(1, 19), # Typen der Punkte
bty = "n") # entfernt Rahmen um Legende
# Text mit mathematischem Ausdruck einfügen
beispieltext <- expression(plain("Die Wurzel von 2 schreibt man als ")~sqrt(2))
text(x = 2, y = 2, # Koordinaten
beispieltext) # Text
Balkendiagramme werden mit der Funktion barplot()
erstellt, der die Höhe der jeweiligen Balken als Vektor (oder als
Matrix) übergeben wird. Im folgenden Beispiel gehen wir davon aus, dass
es drei verschiedene Bedingungen gibt:
werte <- c(3, 5, 4) # Werte auf der abhängigen Variablen
barplot(werte) # einfachstes Balkendiagramm
Auch hier können wir die Abbildung wieder verfeinern – beachten Sie
zudem, dass die Funktion barplot()
etwas zurückgibt, was
wir in der Variable mp
speichern:
werte <- c(3, 5, 4) # Werte auf der abhängigen Variablen
mp <- barplot(werte,
main = "Hübscheres Balkendiagramm", # Titel
axes = FALSE, # keine Achsen
ylim = c(0,7), # Skalierung y-Achse
xlab = "Bedingung", # Beschriftung x-Achse
ylab = "Variable Y") # Beschriftung y-Achse
# was ist mp? -> enthält die x-Koordinaten der Mittelpunkte der Balken
# und wird nun genutzt, um die Balken an der x-Achse zu beschriften
axis(side = 1, # x-Achse
at = mp, # an die Mittelpunkte der Balken
labels = c("A", "B", "C")) # die Labels an sich
axis(side = 2, # y-Achse
las = 2,
at = c(0:7),
labels = c(0:7))
abline(h=0) # horizontale Linie bei y = 0
Nun erweitern wir das Balkendiagramm indem wir uns vorstellen, zwei
Gruppen von Personen hätten an den drei Bedingungen teilgenommen, d.h.
wir haben am Ende sechs Werte die geplottet werden und zudem gruppiert
werden sollen: Die zwei Gruppen innerhalb jeder Bedingung sollen
nebeneinander stehen, während etwas Platz zwischen den Bedingungen sein
soll. Die Werte müssen nun als Matrix an barplot()
übergeben werden:
werte <- rbind(c(3, 5, 4), # Werte der Gruppe 1
c(4, 5, 3)) # Werte der Gruppe 2
mp <- barplot(werte,
main = "Hübscheres Balkendiagramm mit Farbe",
beside = TRUE, # sorgt für die Gruppierung
axes = FALSE,
ylim = c(0,7),
xlab = "Bedingung",
ylab = "Variable Y",
col = c("blue", "green")) # Farben der Gruppen
# was ist mp? -> Ist nun eine Matrix mit den 6 x-Koordinaten der 6 Balken: um jeweils
# die Mitte zu finden, können wir die spaltenweisen Mittelwerte mit colMeans() nutzen
axis(side = 1,
at = colMeans(mp), # jeweilige Mitten der Gruppen
labels = c("A","B","C")) # Labels an sich
axis(side = 2,
las = 2,
at = c(0:7),
labels = c(0:7))
abline(h=0)
# Legende hinzufügen
legend(x=1 ,y=7,
legend=c("Gruppe 1", "Gruppe 2"), # Beschriftung
fill = c("blue","green"), # malt farbige Boxen davor
bty = "n")
Probieren Sie selber aus, was sich verändert, wenn Sie in diesem Fall
beside = FALSE
setzen. Das Diagramm an sich wird dann
gezeichnet, beim Hinzufügen der \(x\)-Achse wird aber ein Fehler ausgegeben
werden, da mp
nur noch ein Vektor ist, die Funktion
colMeans()
aber mindestens eine \(2\times 2\)-Matrix benötigt. Dies müssen
Sie dann anpassen.
Histogramme werden zur Visualisierung von absoluten oder relativen
Häufigkeiten benutzt, wobei die einzelnen Datenwerte in Klassen
eingeteilt werden. Für das folgende Beispiel werden zunächst 300 Zahlen
zwischen 1 und 50 gezogen und dann durch weitere 300 Zahlen zwischen 30
und 50 ergänzt. Zunächst übergeben wir die Daten an die Funktion
hist()
zur Erzeugung eines einfachen Histogramms:
daten <- sample(x = c(1:50), # 300 Zahlen zwischen 1 und 50
size = 300,
replace = TRUE)
daten <- c(daten, sample(x = c(30:50), # plus 300 weitere Zahlen zwischen
size = 300, # 30 und 50
replace = TRUE) )
hist(daten) # einfachstes Histogramm
Natürlich können wir auch Histogramm modifizeren und die Funktion
hist()
gibt auch interessante Werte zurück. Ein erster
wichtiger Punkt betrifft die Frage, zu wievielen Klassen die Daten
zusammengefasst werden sollen. Standardmäßig verwendet
hist()
die sog. Sturges-Regel zur Bestimmung der
Klassen-Anzahl; wir können aber auch andere Algorithmen, Vektoren mit
konkreten Klassengrenzen oder einfach die Anzahl der zu bildenden
Klassen dem Parameter breaks
übergeben. Mit
labels = TRUE
bewirken wir, dass die konkreten Fallzahlen
pro Klasse angezeigt werden und die Rückgabe von hist()
speichern wir in der Variable hist_werte
:
hist_werte <- hist(daten,
breaks = 20,
labels = TRUE,
col = "grey50",
ylim = c(0,140),
axes = FALSE,
main = "Histogramm",
xlab = "Wertebereich",
ylab = "absolute Häufigkeit")
# in hist_werte stehen nun verschiedene Informationen über das erstellte
# Histogramm, u.a. die Klassengrenzen (hist_werte$breaks) und die Häufigkeiten
# (hist_werte$counts) -> diese können wir nun weiterverwenden. Wenn bei axis()
# keine Labels verwendet werden, verbaut R diejenigen, die es für die passendsten
# Labels hält
axis(side = 1,
at = hist_werte$breaks) # x-Achse an den Klassengrenzen orientiert
axis(side = 2,
las = 2)
abline(h = 0)
Übungsaufgabe: Laden Sie den Datensatz
datensatz.RData
und erstellen Sie nebeneinander die beiden
Histogramme für die Variablen var1
und var2
!
Die Daten sollen dazu jeweils in 12 (var1
) bzw. 5
(var2
) Klassen eingeteilt werden, die Überschrift soll
fehlen, die \(x\)-Achse mit dem
Variablennamen und die \(y\)-Achse mit
“absolute Häufigkeit” beschriftet werden. Die Farbe der Balken soll
“rot” für var1
und “blau” für var2
sein. Eine
Lösung finden Sie am Ende dieses Kapitels.
Tortendiagramme werden häufig verwendet, um Anteile zu illustrieren.
Wir nutzen hier zunächst die Funktion pie()
um ein
2D-Tortendiagramm zu erstellen.
namen <- c("A", "B", "C", "D", "E") # fünf verschiedene Objekte...
daten <- c(10, 12, 4, 16, 8) # ...und ihre Werte
prozent <- round(daten/sum(daten)*100) # Prozent der Werte an der
# Gesamtsumme berechnen
# hier basteln wir uns Labels, indem mit der Funktion paste() Strings
# aneinander gefügt werden
namen <- paste(namen, prozent) # Prozentwerte zu den Namen hinzufügen
namen <- paste(namen,"%",sep="") # das %-Zeichen noch anhängen
pie(daten, # Tortendiagramm malen
labels = namen, # Beschriftungen
col = rainbow(length(namen)), # die ersten (fünf) Farben aus rainbow
radius = 1) # Radius der des Kreises
Auch 3D-Tortendiagramme sind leicht möglich, wenn wir die Funktion
pie3D()
aus dem Paket plotrix
nutzen. Im
Wesentlichen funktioniert die Funktion so wie pie()
– wir
speichern aber wiederum Werte die von pie3D()
zurückgegeben
werden und nutzen diese dann um die Labels separat mit der Funktion
pie3D.labels()
hinzuzufügen:
library(plotrix)
namen <- c("A", "B", "C", "D", "E") # fünf verschiedene Objekte...
daten <- c(10, 12, 4, 16, 8) # ...und ihre Werte
prozent <- round(daten/sum(daten)*100) # Prozent der Werte an der
# Gesamtsumme berechnen
# hier basteln wir uns Labels, indem mit der Funktion paste() Strings
# aneinander gefügt werden
namen <- paste(namen, prozent) # Prozentwerte zu den Namen hinzufügen
namen <- paste(namen,"%",sep="") # das %-Zeichen noch anhängen
werte <- pie3D(daten, # 3D Diagramm zeichnen
explode = 0.3,
radius = 1.3,
col = rainbow(length(namen)))
pie3D.labels(radialpos = werte, # Legende hinzufügen
labels = namen,
radius = 1.6)
Viele der deskriptiven Statistiken können mit sog. Boxplots
visualisiert werden. Wir werden dazu nun die Variablen var2
und var3
des DataFrames datensatz
(welches in
datensatz.RData
enthalten ist) nutzen. Um diese beiden
Variablen gemeinsam als Boxplot zu visualieren, nutzen wir ganz
einfach:
boxplot(datensatz[3:4]) # Boxplot der 3. und 4. Variable aus datensatz
In solchen Boxplots markiert die dicke horizontale Linie den Median und
die untere und obere Grenze der Box sind das 1. und das 3. Quartil. Die
gestrichelten vertikalen Linien sind diejenigen Werte, die maximal
1.5-mal den Interquartilabstand entfernt von der Box liegen. Für die
Variable var3
sehen wir außerdem 3 Kreise unten: Dies sind
Werte die mehr als 1.5-mal den Interquartilabstand von der Box enfernt
liegt und oft als “Ausreißer” bezeichnet werden.
Wir können auch recht leicht Boxplots einer Variable, aber getrennt
nach z.B. Gruppen erstellen. Der folgende Aufruf ist zu lesen als “Die
Variable var2
als Funktion der Variable
gruppe
”:
boxplot(datensatz$var2 ~ datensatz$gruppe)
Alle weiteren Parameter und Ergänzungen können bei Boxplots ähnlich verwendet werden, wie in den vorangegangen Beispielen.
Eine mögliche Variante sieht so aus:
par(mfrow = c(1,2))
hist(datensatz$var1,
breaks = 12,
main = "",
xlab = "var1",
ylab = "absolute Häufigkeit",
col = "red")
hist(datensatz$var2,
breaks = 5,
main = "",
xlab = "var2",
ylab = "absolute Häufigkeit",
col = "blue")
Bisher sind wir einfach davon ausgegangen, dass R beginnend mit der ersten Zeile sequenziell alle Zeilen eines Skriptes der Reihe nach abarbeitet. Es gibt aber Fälle, wo wir genau dies nicht wollen, sondern vielleicht ein Teil nur dann ausgeführt werden soll, wenn ein bestimmtes Ereignis eingetreten ist, eine Variable einen bestimmten Wert hat, … und ansonsten übersprungen werden soll. Oder, in Abhängigkeit davon welchen Wert eine Variable hat, wollen wir den einen oder einen anderen Code abarbeiten. So etwas kann mit Konditionalabfragen geschehen. Darüber hinaus wollen wir manchmal auch Code einfach wiederholen. Zu diesem Zweck können wir Schleifen verwenden.
Konditionalabfragen und Schleifen sind auch wichtige Bestandteile zur Programmierung von R, wenn wir Simulationen durchführen wollen.
Die wichtigste Art, die Ausführung von Code von bestimmten
Ereignissen oder Werten einer Variablen abhängig zu machen, ist mit
Hilfe von if
-Abfragen. Eine solche Abfrage besteht i.W. aus
einer Bedingung die wahr oder falsch sein kann (TRUE
oder
FALSE
): Wenn diese Bedingung TRUE
ist, wird
der folgende Code ausgeführt; ansonsten nicht. Der ggf. ausuführende
Code ist dabei in geschweiften Klammern abgegrenzt. Ein ganz einfaches
Beispiel ist das folgende:
a <- 2 # Variable, deren Wert gleich getestet wird
if (a == 2) { # Abfrage...
print("Dieser Code wird ausgeführt!") # ...wird nur ausgeführt, wenn TRUE
}
## [1] "Dieser Code wird ausgeführt!"
Da die Bedingung a == 2
wahr ist, also
TRUE
, wird der Code innerhalb der folgenden geschweiften
Klammern ausgeführt. Geben Sie a
nun einen anderen Wert,
dann wird der Code nicht ausgeführt. Mehrere Abfragen geschehen, indem
if
erweitert wird zu else if
, und eine
“Restkategorie”, die also zutrifft wenn keine der vorab genannten
Bedigungen zutrifft, kann mit else
eingeführt werden.
Probieren Sie auch hier andere Werte für a
aus und schauen,
wie sich das kleine Programm verhält!
a <- 3 # Variable deren Wert gleich getestet wird
if (a == 2) { # Abfrage 1: a == 2?
print("a hat den Wert 2!") # ...dann hier rein
} else if (a == 3) { # Abfrage 2: a == 3?
print("a hat den Wert 3!") # ...dann hier rein
} else { # ansonsten...
print("a ist weder 2 noch 3!") # ...hier rein
}
## [1] "a hat den Wert 3!"
Prinzipiell können if
-Abfragen auch beliebig
verschachtelt werden und auch verschiedene Arten von Variablen können in
die Bedingungen mit eingehen:
geschlecht <- "w"
alter <- 30
# Beispiel für zwei ineinander geschachtelte Abfragen
if (geschlecht == "w") { # "äußere Abfrage"
if (alter >= 25) { # "innere Anfrage"
print("Es handelt sich um eine weibliche VP von mindestens 25 Jahren!")
} else if (alter < 25) {
print("Es handelt sich um eine weibliche VP, die jünger als 25 ist!")
}
} else { # "else" zu "äußerer Abfrage"
print("Es handelt sich nicht um eine weibliche VP!")
}
## [1] "Es handelt sich um eine weibliche VP von mindestens 25 Jahren!"
Verschachtelte Abfragen können alternativ auch mit logischen Ausdrücken wie und und oder verknüpft werden, um kompakter formuliert zu werden:
geschlecht <- "m"
alter <- 22
if ( (geschlecht == "m") & (alter >= 25) ) {
print("Es handelt sich um eine männliche VP von mindestens 25 Jahren!")
} else if ( (geschlecht == "m") & (alter < 25) ) {
print("Es handelt sich um eine männliche VP, die jünger als 25 ist!")
} else {
print("Es handelt sich nicht um eine männliche VP!")
}
## [1] "Es handelt sich um eine männliche VP, die jünger als 25 ist!"
Die Klammern um die einzelnen Bedingungen sind hier nicht zwingend nötig; ihre Verwendung macht aber erstens das Lesen komplexer Ausdrücke einfacher und zweitens können Klammern auch verwendet werden, um zu bewirken, dass zwei Bedingungen zunächst gemeinsam evaluiert werden und dann in Relation zu einer anderen Bedingung gesetzt werden. Zudem bietet sich bei derartigen Abfragen an, sich das “Einrücken” von Zeilen anzugewöhnen, um die Lesbarkeit zu erhöhen und Fehler zu vermeiden.
Mit Schleifen können Zeilen wiederholt werden. R bietet verschiedene
Schleifen an. Wir beginnen mit repeat
- und
while
-Schleifen und kommen dann zur wichtigen
for
-Schleife.
repeat
- und while
-SchleifenDie erste Art von Schleife wiederholt Code einfach unendlich oft.
Dazu beginnt ein Block mit dem Wort repeat
und in
geschweiften Klammern steht das, was wiederholt werden soll:
i <- 0
repeat {
i <- i + 1 # Wert von i um eins erhöhen
print(i) # Wert von i ausgeben
}
Wenn wir diese Schleife allerdings ausführen, wird sie tatsächlich
einfach immer wieder ausgeführt. Das ist natürlich i.d.R. sinnfrei, da
wir eine Schleife auch wieder verlassen wollen. Dies können wir mit
break
erreichen: Dieser Befehl verlässt die Schleife und
die Codeausführung wird hinter der schließenden geschweiften Klammer
fortgesetzt. Wollen wir also z.B. die Schleife nur solange wiederholen,
bis i
den Wert 6 annimmmt, dann geht das wie folgt:
i <- 0
repeat {
i <- i + 1 # Wert von i um eins erhöhen
if (i == 6) { # wenn i == 6 ist...
break # ...dann Schleife verlassen
}
print(i) # Wert von i ausgeben
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
break
funktioniert in allen Schleifen, aber meistens
können wir Abbrüche von Schleife auch eleganter bewirken. Zum Beispiel,
können wir Schleifen mit dem Wort while
einleiten. In
diesem Fall wird in Klammern noch ein logischer Ausdruck übergeben und
die Schleife wird sooft wiederholt, wie dieser Ausdruck
TRUE
ist:
i <- 0
while (i < 6) {
i <- i + 1 # Wert von i um eins erhöhen
print(i) # Wert von i ausgeben
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
Versuchen Sie sich klar zu machen, warum die
repeat
-Schleife bis 5 zählt, die
while
-Schleife aber bis 6 gezählt hat!
for
-SchleifenDie vielleicht wichtigste Variante einer Schleife sind
for
-Schleifen. Hierbei wird zusätzlich i.d.R. eine Variable
bei jedem Durchlauf in ihrem Wert verändert und mit diesem Wert kann
innerhalb einer Schleife auch gearbeitet werden. Wir benötigen dazu die
veränderliche Variable (wir nennen diese im Beispiel i
) und
einen Vektor der angibt, welche Werte i
der Reihe nach
annehmen soll. Um z.B. die Zahlen von 1-4 auszugeben, können wir dies
mit einer for
-Schleife wie folgt tun:
for (i in c(1:4)) {
print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
Die Schleife wird also vier-mal wiederholt und die Variable
i
nimmt nacheinander die Werte des Vektor
c(1:4)
an, der aus den zahlen 1, 2, 3 und 4 besteht.
i
könnten wir auch als Index verwenden, um auf die Elemente
eines Vektors zuzugreifen:
geschlecht <- c("m", "w", "w", "d") # Vektor aus Strings
for (i in c(1:4)) {
print(geschlecht[i]) # i wird benutzt als Index für den Vektor
}
## [1] "m"
## [1] "w"
## [1] "w"
## [1] "d"
Wir haben hier jetzt immer eine numerische Variable verwendet, die
ihren Wert verändert. Prinzpiell kann aber jeder Typ hier verwendet
werden. Wollen wir die gleiche Ausgabe wie im letzten Beispiel erzeugen,
können wir den Vektor geschlecht
auch direkt in die
for
-Schleife verbauen:
for (i in geschlecht) { # jetzt werden i die Elemente aus geschlecht...
print(i) # ...zugewiesen und hier ausgegeben
}
## [1] "m"
## [1] "w"
## [1] "w"
## [1] "d"
for
-SchleifenHäufig gibt es jeweils eine eigene Datei mit den Daten einer
Versuchsperson. Das heißt, Sie haben so viele Dateien wie Sie
Versuchspersonen haben. Mit Hilfe einer for
-Schleife können
wir aber sehr leicht diese Datensätze nacheinander einlesen und zu einem
großen Datensatz zusammenfassen. Im Folgenden gehen wir davon aus, dass
Sie bereits mit setwd()
das Arbeitsverzeichnis auf den
Ordner mit den Datenbeispielen gesetzt haben. In diesem Ordner gibt es
einen weiteren Unterordner namens einlesen_mit_for
, der
vier Dateien enthält. Jede Datei enthält die Daten einer Versuchsperson.
Der folgende Code liest diese vier Dateien nacheinander ein und fügt
diese untereinander zusammen:
setwd("./einlesen_mit_for") # der Punkt am Anfang steht für 'aktuelles Verzeichnis'
# dir() liefert einen Vektor mit den Dateien, die auf *.txt enden
dateinamen <- dir(pattern = "/*.txt",
full.names = TRUE)
# in dateinamen stehen nun die Namen aller Dateien im Ordner die auf *.txt enden
# in der for-Schleife werden nacheinander die Elemente durchgegangen und der Variablen
# datei zugewiesen
daten <- NULL # leere Variable daten anlegen
for(datei in dateinamen) {
daten_vp <- read.table(datei, # datei einlesen (eine VP)
header = TRUE,
sep = ";")
daten <- rbind(daten, # "unten" an daten anhängen...
daten_vp) # ...was gerade eingelesen wurde
}
Nun befinden sich in daten
alle vier einzelnen Dateien
untereinander und mit der eigentlich Auswertung könnte begonnen
werden:
daten
Nehmen Sie nun einmal an, es würde die Funktion
sum()
nicht geben. Nutzen Sie Ihre bisherigen Kenntnisse
und eine for
-Schleife, um für jeden beliebig-langen Vektor
numerischer Variablen die Summe der Elemente zu berechnen.
Eine Berechnung der Summe funktioniert nur, wenn alle Elemente numerisch sind. Modifizieren Sie die Schleife so, dass nicht-numerische Elemente im Vektor ignoriert werden und die Schleife trotzdem duchläuft.
Zuletzt soll die Schleife so umgeschrieben werden, dass im Falle von nicht-numerischem Input irgendwo im Eingebevektor sofort eine Warnmeldung ausgegeben wird und die gesamte Schleife abbricht.
Bisher haben wir uns nur an bereits existierenden Funktionen bedient. R wird aber auch dadurch besonders flexibel durch das Schreiben eigener Funktionen. Das ist immer dann nützlich, wenn Sie innerhalb eines Skriptes dieselbe Rechnung viele Male ausführen möchten. Dann wird der Code übersichtlicher und einfacher zu schreiben, wenn Sie die wiederholte Rechnung in eine Funktion auslagern.
Um eine Funktion zu programmieren müssen Sie einer bestimmten
Struktur folgen, die der einer Schleife ähnelt. Im folgenden Code wollen
wir eine Funktion schreiben, die zu einer Zahl 2 addiert. Diese Funktion
soll den Namen addiere
bekommen und das wichtige
Schlüsselwort ist function
.
addiere_2 <- function(zahl){
return(zahl + 2)
}
Diese Funktion wird ein Element übergeben (zahl
, der
Name kann wie bei jedem Objekt frei ausgesucht werden), addiert dann 2
zu der übergebenden Zahl (zahl + 2
) und gibt das Ergebnis
als Rückgabe zurück. Eine Funktion liefert immer das zurück, was als
letztes in ihr berechnet wurde, wenn wir es explizit machen wollen, dann
können wir dazu am Ende einer Funktion das Schlüsselwort
return
verwenden (was sich oft auch der Lesbarkeit halber
anbietet).
Wenn Sie diesen Code ausführen, wird zunächst kein Output erzeugt, sondern die Funktion wird in der Variablenumgebung definiert und kann von nun an benutzt werden. Aus diesem Grund können Sie Funktionen entweder am Anfang eines Skriptes definieren, in welchem auch der Rest des Codes steht, oder auch (was sauberer und oft handlicher ist) ein eigenes Skript für die neue Funktion schreiben; die Funktion quasi auslagern und von dort ausführen. Ist eine Funktion definiert worden, kann sie nun wie jede andere Funktion aufgerufen werden:
addiere_2(13)
## [1] 15
Einer Funktion können auch mehrere Elemente übergeben werden. Die folgende Funktion gibt bspw. aus, welche der beiden Zahlen die kleinere bzw. größere Zahl ist:
ordne_2 <- function(zahl1, zahl2){ # 2 Zahlen als Übergabe
beide.zahlen <- c(zahl1, zahl2) # beide Zahlen in einen Vektor
print(paste0("kleinere Zahl: ",min(beide.zahlen))) # Minimum der beiden Zahlen
print(paste0("größere Zahl: ",max(beide.zahlen))) # Maximum der beiden Zahlen
}
ordne_2(7, 3)
## [1] "kleinere Zahl: 3"
## [1] "größere Zahl: 7"
Die Rückgabe einer Funktion kann auch direkt in einer neuen Variable
gespeichert werden und dann weiter verarbeitet werden, wie hier am
Beispiel der Funktion addiere_2()
gezeigt wird:
neu <- addiere_2(10) # addiert 2 zu 10 und speichert das Ergebnis in neu
neu + 10 # und dann wird 10 dazu addiert
## [1] 22
Eine Funktion kann immer nur eine Variable (bzw. ein Objekt)
zurückgeben. Wollen wir mehrere Werte zurückgeben, müssen diese in einen
Vektor zusammengefügt werden, der vom aufrufenenden Code dann
entsprechend “zerlegt” wird. Wenn die Rückgabewerte von verschiedenen
Typen sind, dann müsste eine Liste (list
) verwendet
werden.
Die folgende Funktion greift das Prinzip auf, die
sum()
-Funktion mit einer for
-Schleife
nachzubauen und in einer Funktion auszuführen. Dazu führen wir noch eine
Erweiterung ein, nämlich übergeben auch einen Parameter
mittelwert
, der spezifiert, ob nur die Summe berechnet
werden soll mittelwert = FALSE
oder der Mittelwert
berechnet werden soll (mittelwert = TRUE
). Dieser Funktion
werden also ein Vektor mit Werten sowie ein Parameter übergeben. Der
jeweilige Rückgabewert ist dann die Summe oder der Mittelwert der
Elemente des Vektors.
sum.mw <- function(daten, mittelwert = FALSE){
summe <- 0 # Initieren der Output-Variable
for(i in daten){ # Schleife, geht alle Elemente von daten durch...
summe <- summe + i # ...und addiert i (das momentane Element) dazu
}
if (mittelwert == FALSE) { # wenn nur Summe zurückgegeben werden soll
return(summe)
} else if (mittelwert == TRUE) { # wenn der Mittelwert berechnet werden soll
return(summe / length(daten)) # dann Mittelwert aus Summe berechnen
}
}
beispiel <- c(3, 5, 1, 7, 2)
sum.mw(daten = beispiel,
mittelwert = FALSE)
## [1] 18
sum.mw(daten = beispiel,
mittelwert = TRUE)
## [1] 3.6
In Kopf der Funktion haben wir mit mittelwert = FALSE
einen Default-Wert definiert, der für mittelwert
angenommen
werden soll, wenn er beim Aufruf nicht weiter spezifiziert wird. Wollen
wir also nur die Summe erhalten, können wir das Argument auch komplett
weglassen und es würde kurz auch der folgende Aufruf reichen:
sum.mw(beispiel)
## [1] 18
Alle selbstgeschriebenen Funktionen können auch innerhalb anderer
Funktionen verwendet werden, z.B. in denen der
apply
-Familie oder der Funktion
aggregate()
.
Sie kennen bereits den Unterschied zwischen der einfachen und der
korrigierten Varianz und die Funktion var()
berechnet
standardmäßig die korrigierte Varianz. Schreiben Sie eine Funktion die
stattdessen die einfache Varianz berechnet.
Schreiben Sie eine Funktion, der Sie numerische Werte übergeben und die entweder “warm” oder “kalt” zurückgibt, je nachdem ob der Übergabewert größer/gleich 37 oder kleiner als 37 ist.
Schreiben Sie eine Funktion, die für einen numerischen Vektor nach Wahl der Benutzer:in entweder den Mittelwert, den Median oder den Modus berechnet (nicht alles gleichzeitig)! Sie können innerhalb der Funktion auch auf existierende Funktionen zurückgreifen.