Themen (CLICK ME)
- Warum R? Und nicht SPSS, Excel und co. - Vor- und Nachteile
- R-(Studio) installieren und kennenlernen
- Datenhandling - Wie gehe ich mit erhobenen Daten um?
- Einfache Deskriptivstatistik - Mittelwert, Standardabweichung und co.
Einleitende Worte
Dieser Beitrag ist im Rahmen des R Workshops für angehende KliPPs Masterstudierende entstanden. Die hier aufgeführten Inhalte sind alles andere als originell und sollten als Zusammenfassung der Statistik I und Statistik II Beiträge verstanden werden. Der Verdienst gehört den Autoren der Beiträge. Wir empfehlen für eine auführlichere Behandlung der Themen in den entsprechenden Beiträgen nachzulesen.
Warum R?
R(-Studio) installieren und kennenlernen
Installation
Obwohl die “Basis” R-Software schon zur Nutzung der gleichnamigen Programmiersprache befähigt, verwenden wir aufgrund der höheren Nutzerfreundlichkeit die integrierte Entwicklungsumgebung RStudio. Beide sind kostenlos erhältlich, beispielsweise bei Posit wo Sie eine übersichtliche Anleitung sowie die Downloadlinks erwarten.
Hier klicken, um zu Posit zu gelangen
Aufbau von RStudio
RStudio besteht aus vier Panels. Oben links befindet sich nach Öffnen einer neuen Skriptdatei (Strg+Shift+n (Mac OS: Cmd+Shift+n) oder über den New File Button) das Skript. In R dient dieses nur zur Strukturierung der Syntax, in RStudio kann man diese dort mit Strg+Return (Mac OS: cmd+Return) oder dem Run Button ausführen. Das Resultat erscheint dann in der Konsole unten links. Oben Rechts finden Sie das Environment. Dies sollte zu Beginn dieses Workshops noch leer sein und sich im Laufe des Tages mit Datensätzen und Objekten füllen. Zuguterletzt befindet sich unten rechts ein Panel mit mehreren Tabs. Unter Files können Sie durch Ordner Datein aufindig machen. Grafische Darstellungen erfolgen im Plots Tab. Unter Packages erhalten Sie eine Übersicht der installierten Erweiterungen für R. Der wohl wichtigste Tab ist der Help Tab. In diesem erhalten Sie Hilfe zu R-Funktionen und Packages.
Die Help Page
Der größte Vorteil gegenüber dem Basis R hat Rstudio wahrscheinlich mit der soeben angepriesenen Help-Funktion.
Diese ist zu erreichen durch:
?
funktionsname()help
(Funktionsname)über die Suchfunktion des Help-Tabs
F1 Drücken während der Cursor sich über der Funktion befindet
Abschnitt | Inhalt |
---|---|
Description | Beschreibung der Funktion |
Usage | Zeigt die Arguente an, die die Funktion entgegennimmt. Argumente auf die ein = folgt haben Standardeinstellungen und müssen nicht jedes mal aufs Neue definiert werden, Argumente ohne = jedoch schon. |
Arguments | Liste der Argumente mit Beschreibung |
Details | Zusatzinformationen zur Funktion |
Values | Übersicht über die möglichen Ergebnisinhalte der Funktion |
See also | Ähnliche Funktionen |
Examples | Praxisbeispiel, Funktion wird angewendet |
R-Studio Settings und Vorteile gegenüber R
Settings
Auch wenn man nun schon sofort mit den eigenen Projekten anfangen könnte, kann es hilfreich sein, die Personalisierungsoptionen, die R bietet, auch zu nutzen. Hier ein kurzer Überblick nützlicher Einstellungen und wo man diese ändert:
Einstellung | Änderung | Beschreibung |
---|---|---|
Font Size | Tools>Global Options>Appearance>Font Size | Anpassen der Schriftgröße |
Theme | Tools>Global Options>Appearance>Theme | Themes beeinflussen Hintergrund- und Schriftfarbe.Idealerweise sollte ein Theme gewählt werden, welches hilft, den Syntax besser zu überblicken. |
Rainbow Parentheses | Tools>Global Options>Code>Display>Syntax>Use Rainbow Parentheses | Zusammengehörige Klammern erhalten dieselbe Farbe. Hilft bei der Übersichtlichkeit. |
Indentation Guidelines | Tools>Global Options>Code>Display>General>Indentation Guidelines | Die eingerückte Fläche wird farbig markiert. Hilft beim Überblick. |
Selbstverständlich gibt es noch etliche weitere Personalisierungsoptionen. Diese sind jedoch zu diesem Zeitpunkt nicht relevant.
Objekte, Funktionen, arithmetische und logische Operatoren
Vektoren
Vektoren sind eindimensionale Datenstrukturen, in denen Elemente des gleichen Typs zusammengeführt werden. Unterschieden wird zwischen folgenden vier Typen:
Typ | Kurzform | Inhalt |
---|---|---|
logical | logi | wahr (TRUE) oder falsch (FALSE) |
numeric | num | Beliebige Zahlen |
character | char | Kombinationen aus Zahlen und Buchstaben |
integer | int | ganze Zahlen |
Beispiel: Flanker Test (Eriksen & Eriksen, 1974)
Die Nutzung von Vektoren wird im Folgenden am Flanker Test verdeutlicht. Dieser Test prüft die selektive Aufmerksamkeit indem Probanden auf einen Zielreiz in der Mitte einer Reizreihe reagieren, während sie irrelevante, ablenkende Reize (die sogenannten “Flanker”) ignorieren.
Ablauf des Flanker Tests:
In der Mitte erscheint ein Pfeil (oder Buchstabe) als Zielreiz.
Links und rechts davon stehen ablenkende Pfeile, die entweder in dieselbe Richtung (kompatibel) oder in die entgegengesetzte Richtung (inkompatibel) zeigen.
Die Aufgabe ist, so schnell wie möglich die Richtung des Zielpfeils anzugeben, ohne sich von den Flanker-Reizen ablenken zu lassen.
Ziel: Messung der Reaktionszeit und Genauigkeit
Frage: unterscheidet sich die Reaktionsgeschwindigkeit zwischen den Versuchsbedingungen signifikant?
Wir nehmen nun im Folgenden an, eine Datenreihe gemessen zu haben und diese interpretieren zu wollen.
Zunächst legen wir die Reaktionszeit als numerischen Vektor ab. Hierfür nehmen wir die c()-Funktion:
# Reaktionszeiten als numerischen Vektor
reaction <- c(600, 520, 540, 680, 560, 590, 620, 630)
Wenn wir nun überprüfen wollen, ob der reaction Vektor auch wirklich als numerical Vektor vorliegt, nutzen wir die class() Funktion:
# Vektor Klasse anzeigen
class(reaction)
## [1] "numeric"
Mit der str()-Funktion können wir uns die Elemente des Vektors ausgeben lassen.
str(reaction)
## num [1:8] 600 520 540 680 560 590 620 630
Nun legen wir die Richtung der flankierenden Zeichen fest.
flankers <- c("<","<",">","<",">",">",">","<")
Nun testen wir, ob es sich um einen character Vektor handelt:
is.character(flankers)
## [1] TRUE
Vektoren lassen sich in manchen Fällen auch in andere Arten umwandeln:
reaction_as_char <- as.character(reaction)
reaction_as_char
## [1] "600" "520" "540" "680" "560" "590" "620" "630"
Diese Umwandlung funktioniert. Wir hätten die Reaktionszeiten auch direkt als character Vektor hinterlegen können, indem wir die Werte in Anführungsreichen setzen (“200”).
Die Umwandlung vom Character Vektor zu einem Numerical Vektor funktioniert jedoch nicht:
flankers_as_numeric <- as.numeric(flankers)
## Warning: NAs introduced by coercion
Da der Flanker Test untersucht ob die Aufmerksamkeit beeinflusst wird wenn die zielzeichen von inkongruenten Zeichen umgeben werden, müssen wir nun überprüfen in welcher Bedingung Kongruentz und in welcher Inkongruenz vorherrscht:
# Zielzeichen erstellen
target <- c(">", ">", ">", "<", "<", "<", ">", ">")
# Vergleich von Vektoren (Kongruenz)
cong <- flankers == target
cong #logischer Vektor
## [1] FALSE FALSE TRUE TRUE FALSE FALSE TRUE FALSE
Dieser logische Vektor zeigt uns, dass es sich nur in Bedingungen 3,4 und 7 um Kongruente Reize handelt. Auch logical Vektoren lassen sich überprüfen:
is.logical(cong)
## [1] TRUE
Nun erstellen wir aus dem Flanker Vektor einen Faktor:
flankers_factorial <- as.factor(flankers)
# Ausgabe des Factors
str(flankers_factorial)
## Factor w/ 2 levels "<",">": 1 1 2 1 2 2 2 1
Nun lassen wir uns noch die Levels ausgeben:
levels(flankers_factorial)
## [1] "<" ">"
Wenn wir nun möchten, dass “>” den Wert 1 erhält, nutzen wir relevel():
releveled_flankers_factorial <- relevel(flankers_factorial, '>')
releveled_flankers_factorial
## [1] < < > < > > > <
## Levels: > <
Im folgenden machen wir aus unserem Faktor einen numerical Vektor. Dies war zuvor mit dem Flankers-Vektor im Character Format nicht mögöich.
numeric_from_flankers <- as.numeric(flankers_factorial)
numeric_from_flankers
## [1] 1 1 2 1 2 2 2 1
Nun steht 1 für “<” und 2 für “>”.
char_from_flankers <- as.character(flankers_factorial)
char_from_flankers
## [1] "<" "<" ">" "<" ">" ">" ">" "<"
Wir können den Faktor jedoch auch wieder in einen Character-Vektor umwandeln.
Zu beachten ist, dass beide Umwandlungen Konsequenzen für weiterführende Operatoren haben.
Funktionen
In R gibt man einer Funktion einen Input und erhält einen bestimmten Output zurück. Jede Funktion erledigt hierbei eine bestimmte Aufgabe. Dies hat die Vorteile der Wiederverwendbarkeit, Organisation und Effizienz. Natürlich könnte man den Mittelwert einer Datenmenge manuell berechnen, mit der darauf ausgerichteten Funktion geht es jedoch schneller.
(34+47+23+90+23+45+89+98)/8
## [1] 56.125
mean(c(34,47,23,90,23,45,89,98))
## [1] 56.125
In R wird zunächst die Funktion genannt und darauffolgend die Argumente. An diesem Beispiel lässt sich bereits die generelle Struktur von Funktionen in R erkennen:
# funktion(argument1, argument2, argument3, ...)
Zusammenfassung Umgang mit Funktionen
Beschreibung | Code.Stil |
---|---|
Funktionen schachteln | funktion1(funktion2(argument)) |
Objekt im Environment anlegen | objekt <- funktion1(argument) |
Ergebnis-Pipe | funktion1(argument) |> funktion2() |
Objekte
In R können Ergebnisse in Objekte angelegt werden und diese wiederum in Funktionen als Argument eingesetzt werden. Der Zuweisungspfeil (Windows: Alt + -) (Mac OS: Option + -) weist dem Objekt ein Ergebnis zu.
Mittelwert <- mean(c(34,47,23,90,23,45,89,98))
Mittelwert # oder auch: print(Mittelwert)
## [1] 56.125
Hierbei gibt die Konsole das Objekt erst aus, wenn ich dieses noch einmal benenne oder es als Argument in die print()
-Funktion einsetze. Bei der Bennenung des Objektes gilt es zu beachten, dass das erste Zeichen keine Zahl sein darf und gleichnamige Objekte überschrieben werden.
Objekte glänzen erst wirklich sobald sie in anderen Funktionen eingesetzt werden:
x <- c(100, 20, 24, 89, 40)
mean(x) == Mittelwert # prüft, ob der Mittelwert von x gleich dem Objekte "Mittelwert" ist
Eine Alternative ist die Pipe. Hierbei wird das Ergebnis nicht als Objekt abgelegt, sondern direkt an eine Funktion weitergegeben.:
# Beispiel mit Verschachtelung
var(c(89,48,38,29,39,49,54))
# Beispiel Pipe
c(89,48,38,29,39,49,54) |> var()
Der Vorteil hiebei ist, dass wir das ganze wieder von links nach rechts lesen können. Auch hier gibt es Möglichkeiten zur Verschachtelung:
# Berechnung der Standardabweichung aus Varianz heraus
c(89, 48, 38, 29, 39, 49, 54) |> var() |> sqrt()
Environment
Im Environment finden sich Objekte wieder. Diese lassen sich jedoch auch mit ls() ausgeben:
ls()
## [1] "char_from_flankers" "cong"
## [3] "data" "data2"
## [5] "data3" "data4"
## [7] "flankers" "flankers_as_numeric"
## [9] "flankers_factorial" "Mittelwert"
## [11] "numeric_from_flankers" "plot1"
## [13] "plot2" "plot3"
## [15] "reaction" "reaction_as_char"
## [17] "releveled_flankers_factorial" "stimulus1"
## [19] "stimulus2" "stimulus3"
## [21] "target" "x"
Entfernen können wir Objekte mit rm(). Dies geschieht jedoch ohne Warnung und ist final.
rm(Mittelwert)
ls() # Environment ohne Mittelwert erscheint.
## [1] "char_from_flankers" "cong"
## [3] "data" "data2"
## [5] "data3" "data4"
## [7] "flankers" "flankers_as_numeric"
## [9] "flankers_factorial" "numeric_from_flankers"
## [11] "plot1" "plot2"
## [13] "plot3" "reaction"
## [15] "reaction_as_char" "releveled_flankers_factorial"
## [17] "stimulus1" "stimulus2"
## [19] "stimulus3" "target"
## [21] "x"
rm(list = ls()) # Enviroment vollständig leeren
ls()
## character(0)
Vor dem Schließen der Software fragt R-Studio häufig, ob man das Environment speichern will.
Dagegen spricht:
Übersichtlichkeit: es ist angenehmer mit einer leeren Umgebung zu beginnen.
Man prüft direkt ob die Ergebnisse auch für andere reproduzierbar sind. Alles Nötige sollte im Skript passieren.
Arithmetische und Logische Operatoren
Die Operatoren, die Sie bereits aus der Mathematik kennen, funktionieren so auch in RStudio als Arithmetische Operatoren:
# Addition
1 + 2
## [1] 3
# Subtraktion
1 - 2
## [1] -1
# Multiplikation
1 * 2
## [1] 2
# Division
(1 + 4) / (2 + 8)
## [1] 0.5
# Potenz
2 ^ 3
## [1] 8
Bis hierhin waren das alles Beispiele die auch jeder gewöhliche Taschenrechner ausführen könnte. RStudio beherrscht jedoch auch logische Abfragen, deren Ergebnisse boolesch - also entweder wahr(TRUE) oder falsch(FALSE)- sind. Wie in vielen anderen Programmiersprachen nutzt man das ! zum negieren.
# Logische Abfragen
1 == 2 # Ist gleich
## [1] FALSE
1 != 2 # Ist ungleich
## [1] TRUE
1 < 2 # Ist kleiner als
## [1] TRUE
1 > 2 # Ist größer als
## [1] FALSE
1 <= 2 # Ist kleiner/gleich
## [1] TRUE
1 >= 2 # Ist größer/gleich
## [1] FALSE
!(1 == 2) # Ist Klammerinhalt NICHT gleich?
## [1] TRUE
Das ist jetzt zugegebenermaßen noch nicht sonderlich beeindruckend. Etwas später werden wir jedoch lernen, das dies auch auf Daten und nicht nur einzelne Elemente angewendet werden kann.
Warnings- vs. Error-Messages
RStudio gibt drei Arten von Rückmeldungen: Messages, Warnings und Errors
1.Messages: Messages dienen grundsätzlich zur Kommunikation und liefern bspw. Informationen bezüglich des Zustandes einer Funktion.
2.Warning:Sie erhalten zwar ein Ergebnis, es könnte jedoch etwas schiefgelaufen sein.
Beispiel:
log(-1)
## Warning in log(-1): NaNs produced
## [1] NaN
Hier erhalten wir die Warnung, dass NaN (Not a Number) als Ergebnis produziert, da der Logarithmus von -1 nicht im Bereich der reellen Zahlen liegt.
3.Errors: Errors entstehen, wenn kein Ergebnis produziert wird. Dies geschieht meist wenn wir der Funktion nicht die richtigen Argumente geben.
Beispiel:
x <- numeric(0) # Ein leerer Vektor für x
y <- numeric(0) # Ein leerer Vektor für y
lm(y ~ x) # Versucht eine lineare Regression durchzuführen
## Error in lm.fit(x, y, offset = offset, singular.ok = singular.ok, ...): 0 (non-NA) cases
Der Fehler “alle Fälle NA” bedeutet, dass R keine gültigen Daten hat – unsere Vektoren sind leer, also kann keine Regression durchgeführt werden.
Datentypen
Nun haben wir bereits einen Datentypen, den Vektor, kennengelernt. Diese lassen sich auch zusammenführen, je nach Relation zueinander.
Typ | Dimensionen | Zusammensetzung | Anmerkungen |
---|---|---|---|
Matrix | 2 | Vektoren des gleichen Typs | Bietet sich v.a. für große Datensätze an. Ist eine Sonderform des Arrays. |
Array | n | Vektoren des gleichen Typs | - |
Data.Frame | 2 | Vektoren der gleichen Länge | Häufigst genutzte Variante in der Psychologie. Ist eine Sonderform der List |
List | 1 | Beliebige Objekte | - |
Erstellen einer Matrix
# Erstellen von 2 Vektoren des gleichen Typs
age1 <- c(30,71,33,28,19)
age2 <- c(98,4,67,43,21)
matrix1 <- cbind(age1, age2)
matrix2 <- rbind(age1, age2)
matrix3 <- matrix(data= c(age1, age2), ncol=2, byrow=TRUE)
matrix1 # die Vektoren werden als columns angeordnet
## age1 age2
## [1,] 30 98
## [2,] 71 4
## [3,] 33 67
## [4,] 28 43
## [5,] 19 21
matrix2 # die Vektoren werden als rows angeordnet
## [,1] [,2] [,3] [,4] [,5]
## age1 30 71 33 28 19
## age2 98 4 67 43 21
matrix3 # design wird durch Argumente ncol, nrow und byrow im Matrix-Befehl bestimmt
## [,1] [,2]
## [1,] 30 71
## [2,] 33 28
## [3,] 19 98
## [4,] 4 67
## [5,] 43 21
Da für eine Matrix alle Vektoren den gleichen Typ haben müssen, gleicht cbind sie in den allgemeinsten Fall an.
job <- c("Pfegefachkraft", "Elektroniker","Grundschullehrer","Rettungssanitäter","Redakteur")
burnout <- c(TRUE,FALSE,FALSE,FALSE,TRUE)
matrix4 <- cbind(age1,job,burnout)
# Alle 3 Vektoren nun char, also keine mathematischen Berechnungen nun mehr möglich
matrix4
## age1 job burnout
## [1,] "30" "Pfegefachkraft" "TRUE"
## [2,] "71" "Elektroniker" "FALSE"
## [3,] "33" "Grundschullehrer" "FALSE"
## [4,] "28" "Rettungssanitäter" "FALSE"
## [5,] "19" "Redakteur" "TRUE"
Da wir nun die Möglichkeit verloren haben num1 als numerischen Vektor zu nutzen, erstellen wir nun aus den selben Vektoren einen data.frame
. In diesen bleiben die Vektoren Typen erhalten.
df1 <- data.frame(age1,job,burnout)
df1
## age1 job burnout
## 1 30 Pfegefachkraft TRUE
## 2 71 Elektroniker FALSE
## 3 33 Grundschullehrer FALSE
## 4 28 Rettungssanitäter FALSE
## 5 19 Redakteur TRUE
##Listet Variablen und Typ auf
str(df1)
## 'data.frame': 5 obs. of 3 variables:
## $ age1 : num 30 71 33 28 19
## $ job : chr "Pfegefachkraft" "Elektroniker" "Grundschullehrer" "Rettungssanitäter" ...
## $ burnout: logi TRUE FALSE FALSE FALSE TRUE
Indizieren
Indizieren bedeutet, dass wir zwar auf bestimmte Elemente eines Objekts zugreifen, ohne diese aus dem Objekt zu entfernen. Das Objekt bleibt weiterhin so bestehen, jedoch kann es manipuliert werden z.B. indem wir Werte ersetzen, Zeilen/Spalten ansprechen oder nach Bedingungen filtern. Hierfür nutzen wir eckige Klammern (Windows: Str+Alt+8) (Mac OS: Option+5). Dies geschieht nach dem Muster: daten[rows,columns]
# Zugriff auf den 4. Eintrag der Spalte age1
df1[4, 'age1']
## [1] 28
# Der vierte Eintrag der Spalte 'age1' auf 20 setzen (verändert Ursprungsdateb)
df1[4, 'age1'] <- 20
# Ganze Spalte 'age1' anzeigen, um die Änderung zu sehen
df1[,'age1']
## [1] 30 71 33 20 19
# Erstellen einer neuen Spalte 'no_burnout', die das Gegenteil der Spalte 'burnout' darstellt - über die Sinnhaftigkeit machen wir uns hier mal lieber keine Gedanken
df1$no_burnout <- !df1$burnout
# Hinzufügen einer sechsten Zeile (Änderung am Datensatz)
df1[6,] <- data.frame(
age1 = 42,
job = "Friseur",
burnout = TRUE,
no_burnout = TRUE)
# Entfernen dieser eben geschaffenen sechsten Zeile
df1 <- df1[-6,]
# Zugriff auf die 5. Zeile und die 4. Spalte (ohne Extraktion)
df1[5, 4]
## [1] FALSE
# Zugriff auf die gesamte 1. Zeile
df1[1, ]
## age1 job burnout no_burnout
## 1 30 Pfegefachkraft TRUE FALSE
# Zugriff auf die 1. Spalte
df1[, 1]
## [1] 30 71 33 20 19
# Zugriff auf die 2. und 3. Zeile, 3. Spalte
df1[c(2, 3), 3]
## [1] FALSE FALSE
# Zugriff auf alle Zeilen, in denen 'burnout' TRUE ist
df1[df1$burnout, ]
## age1 job burnout no_burnout
## 1 30 Pfegefachkraft TRUE FALSE
## 5 19 Redakteur TRUE FALSE
Datenextraktion
In Anderen Fällen extrahiert (entnimmt) man die Daten aus dem Objekt, um sie in einem seperaten Objekt zu speichern. Dies ist hilfreich, wenn wir die entnommenen Daten analysieren wollen, ohne das Ursprungsobjekt zu verändern.
Dies exerzieren wir nun erstmal an der zuvor erstellten Variable age1 durch.
# age1 ausgeben lassen
str(age1)
## num [1:5] 30 71 33 28 19
Wir wissen nun, dass age1 ein numerischer Vektor mit 5 Elementen ist. Wir lassen uns nun das 5. Element ausgeben.
# das 5.Element von age1 ausgeben lassen- jedoch nicht verändern
age1[5]
## [1] 19
Wir können uns die Variable jedoch auch ohne das 5. Element ausgeben lassen:
# age1 ohne Element 5 ausgeben lassen
age1[-5]
## [1] 30 71 33 28
Wir können uns auch eine Auswahl an Elementen ausgeben lassen. Dies gelingt indem wir einem Objekt einen numerischen Vektor zuweisen und dieses Objekt dann in den eckigen Klammern nutzen.
# sich eine Auswahl ausgeben lassen
auswahl <- c(1, 3, 5)
age1[auswahl]
## [1] 30 33 19
# Auswahl in neuem Objekt abspeichern
age_select<-age1[auswahl]
Das funktioniert natürlich auch geschachtelt.
# verschachtelt eine Auswahl ausgeben lassen
age1[c(1, 3, 5)]
## [1] 30 33 19
Wir können auch abrufen, welche Elemente eines character vektors TRUE oder FALSE sind.
!(df1$burnout)
## [1] FALSE TRUE TRUE TRUE FALSE
# Abrufen der Job-Bezeichnungen für die Personen, die einen Burnout haben (TRUE)
df1$job[df1$burnout]
## [1] "Pfegefachkraft" "Redakteur"
# Abrufen der Job-Bezeichnungen für Personen ohne Burnout (FALSE)
df1$job[!df1$burnout]
## [1] "Elektroniker" "Grundschullehrer" "Rettungssanitäter"
# Auch dies kann wieder in Objekte abgelegt werden
job_nburn <- df1$job[!df1$burnout]
# 5. Zeile, 4. Spalte ausgeben lassen
df1[5, 4]
## [1] FALSE
df1[1, ] # 1. Zeile, alle Spalten
## age1 job burnout no_burnout
## 1 30 Pfegefachkraft TRUE FALSE
df1[, 1] # Alle Zeilen, 1. Spalte
## [1] 30 71 33 20 19
df1[c(2, 3), 3] # 2. und 3. Zeile, 3. Spalte
## [1] FALSE FALSE
df1[burnout, ] # Alle kongruenten Zeilen, alle Spalten
## age1 job burnout no_burnout
## 1 30 Pfegefachkraft TRUE FALSE
## 5 19 Redakteur TRUE FALSE
nrow(df1) # Anzahl der Zeilen
## [1] 5
ncol(df1) # Anzahl der Spalten
## [1] 4
dim(df1) # Alle Dimensionen
## [1] 5 4
names(df1) # Namen der Variablen
## [1] "age1" "job" "burnout" "no_burnout"
df1[, 'age1'] # Einzelne Variable auswählen
## [1] 30 71 33 20 19
df1[, c('age1', 'burnout')] # Mehrere Variable auswählen
## age1 burnout
## 1 30 TRUE
## 2 71 FALSE
## 3 33 FALSE
## 4 20 FALSE
## 5 19 TRUE
df1$age1 # eine Variable indizieren
## [1] 30 71 33 20 19
Daten Import und Export
Ähnlich wie wenn man ein word Dokument speichert und in seinem eigenen Dateispeicher einen geeigneten Ort zum speichern suchen muss, benötigt auch R eine Angabe dazu, wo Daten und Syntax hinterlegt werden soll. Dieser Ort kann R als working Directory mitgeteilt werden. Um das aktuelle Working Directory zu erhalten, kann man die Funktion getwd()
nutzen.
getwd()
## [1] "/home/martin/pandar/content/workshops/refresher"
Der hat wahrscheinlich das Format C:/Users/Name/Documents. Um manuell einen anderen Ordner zu nutzen, kann dieser mit setwd()
festgelegt werden:
# setwd('Pfad/Zum/Ordner')
Der Inhalt eines Ordners lässt sich mit dir()
ausgeben.
dir()
R hat zwei eigene Datenformate mit denen Dateien abgespeichert werden können: RDA und RDS.
Dateiformat | Dateiendung | Speichern | Laden | Einsatzort |
---|---|---|---|---|
RDA | .rda | save() | load() | gemeinsames Speichern mehrerer Objekte |
RDS | .rds | saveRDS() | readRDS() | Speichern einzelner Objekte (z.B. Datensätze) |
Klartextformate | .txt oder .dat | write.table() | read.table() | Textbasierte Speicherung und Laden |
CSV | .csv | write.csv() | read.csv() | Tabellendaten im CSV-Format |
Dies probieren wir nun mit unserem df1 Datensatz:
save(df1, file = 'df1.rda')
Um zu testen, ob wir ihn auch wieder abrufen können, leeren wir nun erstmal das Environment:
rm(list = ls())
ls()
## character(0)
Wenn wir jetzt den Datensatz laden, wird er mit seiner Originalbenennung (df1) wiederhergestellt:
load('df1.rda')
ls()
## [1] "df1"
Jetzt durchlaufen wir die gleichen Schritte noch einmal mit dem RDS Format:
saveRDS(df1, 'df1.rds')
rm(list = ls())
ls()
## character(0)
Beim Laden des Datensatzes können wir diesen jetzt einem beliebigen Objekt zuweisen:
work <- readRDS('df1.rds')
work
## age1 job burnout no_burnout
## 1 30 Pfegefachkraft TRUE FALSE
## 2 71 Elektroniker FALSE TRUE
## 3 33 Grundschullehrer FALSE TRUE
## 4 20 Rettungssanitäter FALSE TRUE
## 5 19 Redakteur TRUE FALSE
Für eine erste Dateninspektion eignen sich die folgenden Funktionen:
nrow(work) # Anzahl der Zeilen
## [1] 5
ncol(work) # Anzahl der Spalten
## [1] 4
dim(work) # Alle Dimensionen
## [1] 5 4
names(work) # Namen der Variablen
## [1] "age1" "job" "burnout" "no_burnout"
CSV Datensatz einlesen
osf <- read.csv(file = url("https://osf.io/zc8ut/download"))
# riesiger Datensatz, wir wollen nur 6 Variablen
osf <- osf[, c("ID", "group", "stratum", "bsi_post", "swls_post", "pas_post")]
Datenhandling
Faktoren erstellen
In einem früheren Abschnitt erstellten wir aus einem Vektor einen Factor. Dies funktioniert auch mit Variablen aus Datensätzen.
Für unser Beispiel laden wir den Datensatz “Bildungsinvestitionen auf der Welt (edu_exp)”. Dieser beinhaltet öffentlich zugängliche Daten, die von Gapminder zusammengetragen wurden.
# Datensatz laden
load(url('https://pandar.netlify.app/daten/edu_exp.rda'))
Eine kurze Erläuterung der Variablenbedeutungen:
geo
: Länderkürzel, das zur Identifikation der Länder über verschiedene Datenquellen hinweg genutzt wirdCountry
: der Ländername im EnglischenWealth
: Wohlstandseinschätzung des Landes, unterteilt in fünf GruppenRegion
: Einteilung der Länder in die vier groben Regionenafrica
,americas
,asia
undeurope
Year
: JahreszahlPopulation
: BevölkerungExpectancy
: Lebenserwartung eines Neugeborenen, sollten die Lebensumstände stabil bleiben.Income
: Stetiger Wohlstandsindikator für das Land (GDP pro Person)Primary
: Staatliche Ausgaben pro Schüler:in in der primären Bildung als Prozent desincome
(GDP pro Person)Secondary
: Staatliche Ausgaben pro Schüler:in in der sekundären Bildung als Prozent desincome
(GDP pro Person)Tertiary
: Staatliche Ausgaben pro Schüler:in oder Student:in in der tertiären Bildung als Prozent desincome
(GDP pro Person)Index
: Education Index des United Nations Development Programme
Eine Ausprägung von 100 auf der Variable Primary
in Deutschland hieße also zum Beispiel, dass pro Schüler:in in der Grundschule das Äquivalent der Wirtschaftsleistung einer/eines Deutschen ausgegeben würde. 50 hieße dementsprechend, dass es die Hälfte dieser Wirtschaftsleistung in diese spezifische Schulausbildung investiert wird.
Betrachten wir die Variable Year
.
str(edu_exp$Year)
## int [1:4316] 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 ...
Wir sehen, dass die Variable noch als integer (wie numerical aber ganzzahlig) hinterlegt ist. Wenn wir uns aber die Jahrzehnte kategorial und nicht stetig wie die Jahreszahlen betrachten wollen, müssen wir einen Faktor erstellen. Das Erstellen von Faktoren ist eine Vorraussetzung für einige statistische Analysen, aber auch eine sinnvolle Visualisierung.
# Verwende cut() um die Jahre in Kategorien einzuteilen
edu_exp$Decade <- cut(edu_exp$Year,
breaks = c(1990, 2000, 2010, 2020),
labels = c("90s", "2000s", "2010s"),
right = FALSE)
str(edu_exp$Decade)
## Factor w/ 3 levels "90s","2000s",..: 1 1 1 2 2 2 2 2 2 2 ...
Allerdings kommt es eher selten vor, dass kontinuierliche Variablen willkürlich in Kategorien unterteilt werden müssen. Deswegen folgt nun ein Beispiel, in dem schon Kategorien vorliegen.
data$variable <- factor(data$variable,
levels = c("Level1", "Level2", "Level3"),
labels = c("Label1", "Label2", "Label3"))
NAs rausschmeißen
Weswegen treten NA’S auf?
- Fragen überlesen / nicht gesehen
- Antwort verweigert
- Unzulässige Angaben gemacht (im Papierformat)
- Unleserliche Schrift (im Papierformat)
Weshalb sind sie problematisch?
Für statistische Analysen sind fehlende Werte ein Problem, weil sie außerhalb der zulässigen Antworten liegen.
# gibt NA zurück
mean(edu_exp$Primary)
## [1] NA
Dies ist verständlicherweise nicht zielführend für unsere Analysen. In R kann man NA’s auf zwei Ebenen angehen:
Ebene | Funktion | Beschreibung | Beispiel |
---|---|---|---|
global(Datensatz) | na.omit | Entfernt jede Beobachtung, die mind. ein NA enthält. | Jeder Proband, der mind. eine Frage nicht beantwortet, wird ausgeschlossen. |
lokal(Variable) | na.rm = TRUE | Das Argument na.rm ist in vielen Funktionen für univariate Statistiken enthalten. Per Voreinstellung wird NA als Ergebnis produziert, wenn fehlende Werte vorliegen. Fehlende Werte werden nur für diese eine Analyse ausgeschlossen, wenn sie auf der darin untersuchten Variable keinen Wert haben - Datensatz bleibt erhalten. | Beispiel: Ein Proband hat zwar sein Alter nicht angegeben, wird aber dennoch bei der Korrelation zwischen Region und Expectancy mit einbezogen. |
Bevor wir uns für eine Methode entscheiden, müssen wir aber erstmal versuchen, ob unser Datensatz NA’s hat und wenn ja, auf welchen Variablen:
# Unterschiedliche Möglichkeiten NA's abzufragen
is.na(edu_exp) # gibt TRUE/FALSE für jede einzelne Zelle aus
anyNA(edu_exp) # gibt es mindestens ein NA?
sum(is.na(edu_exp)) # wieviele NA's gibt es insgesamt im Datensatz?
complete.cases(edu_exp) # Welche Zeilen sind vollständig?
# Zeigt die Anzahl fehlender Werte pro Spalte an
colSums(is.na(edu_exp))
## geo Country Wealth Region Year Population Expectancy
## 0 0 0 0 0 0 1120
## Income Primary Secondary Tertiary Index Decade
## 174 2753 2907 2905 288 0
Wir erfahren, dass die Variablen Expectancy
, Income
, Primary
, Secondary
, Tertiary
und Index
NA’s enthalten. Wenn wir dem Global Approach folgen wollen, dann können wir mit na.omit()
ein neues Objekt mit dem bereinigten Datensatz erschaffen.
# Entfernt alle Zeilen, die NAs enthalten
edu_exp_clean1 <- na.omit(edu_exp)
dim(edu_exp_clean1)
## [1] 811 13
Wir können aber auch nur die Einträge löschen, die auf einer bestimmten Variable (nämlich die, die wir untersuchen wollen) löscht.
# Nur Zeilen mit fehlenden Werten in einer bestimmten Spalte entfernen:
edu_exp_clean2 <- edu_exp[!is.na(edu_exp$Expectancy), ]
str(edu_exp_clean2) # behält mehr observations bei
## 'data.frame': 3196 obs. of 13 variables:
## $ geo : chr "afg" "afg" "afg" "afg" ...
## $ Country : chr "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
## $ Wealth : chr "low_income" "low_income" "low_income" "low_income" ...
## $ Region : chr "asia" "asia" "asia" "asia" ...
## $ Year : int 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 ...
## $ Population: int 17788819 18493132 19262847 19542982 19688632 21000256 22645130 23553551 24411191 25442944 ...
## $ Expectancy: num 50.7 50 50.8 51 51.1 51.6 52.1 52.5 52.9 53.2 ...
## $ Income : num NA NA NA NA NA ...
## $ Primary : num NA NA NA NA NA NA NA NA NA NA ...
## $ Secondary : num NA NA NA NA NA NA NA NA NA NA ...
## $ Tertiary : num NA NA NA NA NA NA NA NA NA NA ...
## $ Index : num 0.18 0.19 0.2 0.2 0.21 0.22 0.23 0.26 0.27 0.28 ...
## $ Decade : Factor w/ 3 levels "90s","2000s",..: 1 1 1 2 2 2 2 2 2 2 ...
Subsets erstellen
Anstatt den ganzen Datensatz zu betrachten, ist es oft sinnig Subsets zu erstellen, die mit logischen oder artithmetischen Operatoren eine Teilstichprobe abspalten.
# Subset von Pbn aus Ländern mit mehr als 10 Mio einwohnern
edu_exp_subset <- edu_exp[edu_exp$Population > 10000000, ]
# Ergebnis anzeigen
edu_exp_subset
# mehr als 10 mio UND EUROPA
edu_exp_subset2 <- edu_exp[edu_exp$Population > 10000000 & edu_exp$Region == "europe", ]
edu_exp_subset2
Pakete
Packages sind Sammlungen von Funktionrn, Datensätzen und Dokumentationen, die man zusätzlich zu denen in der System Library herunterladen kann. Jedes Paket wurde hierbei für bestimmte Aufgaben entwickelt. Das hat zum Einen den Vorteil, dass man nur die packages herunterladen muss, die man auch wirklich benötigt, und zum Anderen, muss nicht immer das gesamte R geändert werden sobald sich ein package aktualisiert.
Der tab mit den packages befindet sich in dem Panel unten rechts zwischen “plots” und “help”. Hier wird ersichtlich, dass es neben der System Library mit den vorinstallierten Paketen noch die User Library mit den manuell hinzugefügten packages gibt.
Zwar finden wir im Tab oben links den “Install”-Button, der ein weiteres Fenster zum Installieren der packages öffnet, jedoch ist es ratsam, die Installation der packages im Skript durchzuführen. Somit bleibt das Skript für jeden der es erhält ausführbar, unabhängig von der eigenen User Library.
Für das Herunterladen und Abrufen der Packages nutzen wir install.packages() und library().
install.packages("psych")
library(psych)
Das hier zu demonstrationszwecken verwendete psych-package wurde speziell für psychologische Analysen entwickelt. Im Verlaufe des Seminars werden Sie jedoch noch weitere packages kennenlernen.
Einfache Deskriptivstatistik
Übersicht
Skala | Aussage | Transformation | Zentrale Lage | Dispersion |
---|---|---|---|---|
Nominal | Äquivalenz | eineindeutig | Modus | Relativer Informationsgehalt |
Ordinal | Ordnung | monoton | Median | Interquartilsbereich |
Intervall | Verhältnis von Differenzen | positiv linear | Mittelwert | Varianz, Standardabweichung |
Verhältnis | Verhältnisse | Ähnlichkeit | … | … |
Absolut | absoluter Wert | Identität | … | … |
Nominal- und Ordinalskalierte Daten
Häufigkeitstabellen
Eine deskriptivstatistische Möglichkeit zur Darstellung diskreter (zählbarer) nominalskalierter Variablen sind Häufigkeitstabellen. Diese können in R
mit der Funktion table()
angefordert werden.
Absolute Häufigkeiten
table(edu_exp$Wealth)
##
## high_income low_income
## 4 1320 682
## lower_middle_income upper_middle_income
## 1034 1276
Relative Häufigkeiten
Relative Häufigkeiten können aus absoluten Häufigkeiten abgeleitet werden:
$h_j = \frac{n_j}{n}$
In R können wir das mit Hilfe der prop.table()
-Funktion bewerkstelligen:
table(edu_exp$Wealth) |> prop.table()
##
## high_income low_income
## 0.0009267841 0.3058387396 0.1580166821
## lower_middle_income upper_middle_income
## 0.2395736793 0.2956441149
Modus
Den Modus, also die Ausprägung einer Variable die am häufigsten vorkommt können wir dann direkt aus einer solchen Häufigkeitstabelle ablesen
table(edu_exp$Wealth)
##
## high_income low_income
## 4 1320 682
## lower_middle_income upper_middle_income
## 1034 1276
oder mit Hilfe einer weiteren Funktion direkt ausgeben lassen
table(edu_exp$Wealth) |> which.max()
## high_income
## 2
Der Modus der Variable Wealth
lautet high_income, die Ausprägung trat 1320-mal auf.
Relativer Informationsgehalt
Da es keine einfache Funktion in R für die Berechnung des relativen Informationsgehalts gibt verweisen wir sie auf das entsprechende Kapitel auf pandaR. Dort wird die manuelle Berechnung anhand der Formel mit R
als Taschenrechner gezeigt.
Median
Der Median lässt sich mit Hilfe der gleichnamigen Funktion (median()
) in R ganz einfach berechnen. Zunächst reduzieren wir unseren Datensatz auf ein bestimmtes Jahr.
edu_2003 <- subset(edu_exp, subset = Year == 2003)
median(edu_2003$Population)
## [1] 6766669
Die median Population aller im Datensatz erfassten Länder betrug im Jahr 2003 6.766.669.
Interquartilsbereich
Bei ordinalskalierten Daten wird häufig der Interquartilsbereich (IQB) als Dispersionsmaß gewählt. IQB ist der Bereich zwischen dem 1. und 3. Quartil.
Um die Quartile oder jedes beliebige andere Quantil einer Verteilung zu erhalten, kann die Funktion quantile()
verwendet werden. Beispielsweise können wir die Grenzen des IQB und den Median mit folgender Eingabe gleichzeitig abfragen.
quantile(edu_2003$Population,
c(0.25, 0.5, 0.75))
## 25% 50% 75%
## 1519292 6766669 21367466
Der IQB liegt also zwischen 1.519.292 und 21.367.466.
Aufmerksamen Lesern könnte schon aufgefallen sein das es sich bei der Variable Population
nicht um eine ordinalskalierte Variable handelt sondern um eine mindestens intervallskalierte, ja gar absolutskaliert Variable. Für die Berechnugn des Medians und IQBs stellt dies jedoch kein Problem dar, da Maße der zentralen Tendenz und Dispersionsmaße von niedrigeren Skalenniveaus auch bei höheren angwendet werden können. Es werden dann halt nicht alle Informationen vollends genutzt.
Intervallskalierte Daten
Mittelwert
Auch die Berechnung des Mittelwerts ist in R mit Hilfe einer Funktion möglich. Diese heißt wie man vielleicht erwarten könnte mean()
.
mean(edu_2003$Population)
## [1] 32569642
Varianz
Auch für die Varianz und Standardabweichung haben wir zwei intuitive Funktionen, var()
und sd()
.
var(edu_2003$Population)
## [1] 1.578144e+16
sd(edu_2003$Population)
## [1] 125624202
An dieser Stelle sollte erwähnt werden R berechnet die geschätzte Varianz/Standardabweichung, mehr dazu können sie im entsprechenden Statistik I Beitrag nachlesen.