Die folgende Notation wird in diesem Dokument benutzt:
R Code (den Du über copy & paste in Euer Script überführen und ausführen kannst) wird so dargestellt:
R_Code() # Hashtag kennzeichnet einen Kommentar; wird von R ignoriert = nicht ausgeführt
Die Ausgabe von Befehlen in R wird hier so dargestellt:
## [1] "Die Ausgabe eines R-Befehls"
Aufgaben zu den einzelnen Kapiteln werden eingerückt dargestellt:
Aufgaben
Lösungen, Zusatzaufgaben sowie sowie wichtige Hinweise und Tipps haben folgende Buttons:
Ein Dataframe ist vermutlich die typische Datenstruktur für Datensätze in R. Am besten vergleichbar ist ein Dataframe mit der Datentabellenstruktur, die Du aus gängigen Statistikprogrammen (z.B. Excel) kennst. Dataframes sind also zweidimensionale Tabellen, deren Spalten Überschriften haben (können) und immer gleich lang sind (also dieselbe Anzahl an Dateneinträgen haben). Dabei darf eine Spalte immer nur den gleichen Datentyp (z.B. numerische oder logische Werte) beinhalten; ein Dataframe kann aber spaltenweise unterschiedliche Datentypen darstellen.
In dieser Lerneinheit wirst Du lernen (a) wie man Dataframes in R erstellt oder einliest, (b) wie man Elemente/Werte in Dataframes anspricht, (c) wie Du diese Elemente oder ganze Dataframes modifizierst und (d) wie Du Dataframes als .txt-Dateien ausschreiben uns speichern kannst.
Anhand des folgenden Skriptbeispiels erlernen wir kurz wie
Objektzuweisungen (<-
) in R funktionieren, was Vektoren
sind, und dass sie unterschiedliche Datentypen beinhalten können.
# Wir erstellen einen Vektor, der Zeichenkettern ('characters') beinhaltet
Dichter <- c("Goethe", "Schiller", "Brecht")
# Wir erstellen einen Vektor, der numerische Werte beinhaltet
Sterbealter <- c(83, 46, 57)
# Wir erstellen einen Vektor, der logische Werte (wahr oder falsch) beinhaltet
Brillentraeger <- c(FALSE, FALSE, TRUE)
# In den vorigen Beispiele wurde jeweils ein Datentyp einem Vektor zugewiesen. Ein Vektor sollte auch immer nur einen Datentyp speichern, da das Mischen von Datentypen schnell zu Problemen führt sobald man mit ihnen arbeitet (sehen wir später):
NotrufNr <- c(110, 112) # gut!
NotrufNr <- c(110, 112, "Mama") # nicht gut!
Anstelle der Zuweisung <-
akzeptiert R auch ein einigen
vielleicht intuitiver erscheinendes =
. Beides hat Vor- und
Nachteile; beides funktioniert.
Aus den drei erstellten Vektoren ‘Dichter’, ‘Sterbealter’ und
‘Brillentraeger’ erstellen wir nun mit der Funktion
data.frame()
unseren ersten Dataframe
# Wir nennen unseren Datafame (ein Objekt) hier 'df' und nutzen die Funktion 'data.frame()'. Wir geben jeder Spalte einen Namen und weisen ihr die Werte der jeweiligen Vektoren zu.
df <- data.frame("Dichter"= Dichter, "Sterbealter" = Sterbealter, "Brillentraeger" = Brillentraeger)
# So sieht unser Dataframe nun aus
print(df)
## Dichter Sterbealter Brillentraeger
## 1 Goethe 83 FALSE
## 2 Schiller 46 FALSE
## 3 Brecht 57 TRUE
R hält einige Funktionen bereit, um sich einen Überblick über einen Dataframe zu verschaffen. Das ist insbesondere für größere Dataframes mit vielen Zeilen und/oder Spalten sinnvoll. Wir werden diese Funktionen dennoch an unserem sehr übersichtlichen Beispiel testen
# Die Funktion 'dim()' gibt die Anzahl der Zeilen (erster Wert) und Spalten (zweiter Wert) aus; alternativ kannst Du Dir mit 'ncol(df)' oder 'nrow(df)' die Anzahl der Spalten bzw. Zeilen einzeln anzeigen lassen.
dim(df)
ncol(df)
nrow(df)
# Mit der Funktion 'summary()' erhalten wir spaltenweise eine Zusammenfassung unseres 'df'.
summary(df)
# Die Funktion 'str()' zeigt die Struktur unseres 'df' an (inkl. der Datentypen in den Spalten - mit '$' gekennzeichnet)
str(df)
# Die Funktionen 'head()' und 'tail()' zeigen die ersten/letzten sechs Zeilen des 'df' an. Das lassen wir hier mal sein, das ergibt bei einem Dataframe mit nur drei Zeilen keinen Sinn...
## [1] 3 3
## [1] 3
## [1] 3
## Dichter Sterbealter Brillentraeger
## Length:3 Min. :46.0 Mode :logical
## Class :character 1st Qu.:51.5 FALSE:2
## Mode :character Median :57.0 TRUE :1
## Mean :62.0
## 3rd Qu.:70.0
## Max. :83.0
## 'data.frame': 3 obs. of 3 variables:
## $ Dichter : chr "Goethe" "Schiller" "Brecht"
## $ Sterbealter : num 83 46 57
## $ Brillentraeger: logi FALSE FALSE TRUE
Dafür gibt es verschiedene Methoden. Einerseits kann man mittels Indizierungen auf Zeilen und Spalten zugreifen (wie bei einer Matrix). Man gibt also einen Index für die Zeile und/oder Spalte an, die man auswählen möchte. Andererseits gibt es auch Zugriffmöglichkeiten, die typischerweise für Dataframes entwickelt wurden (Dollarzeichen-Indizierung).
# Matrixartige Indizierung: Im erste Beispiel wollen wir die beiden letzten Einträge der Tabelle auslesen. Diese speichern wir in einem neuen Dataframe ('df_ohnegoethe', der dann nur noch aus zwei Zeilen besteht).
df_ohnegoethe <- df[2:3,] # Indizierung [,]: vor dem Komma indiziert man die Zeilen, nach dem Komma die Spalten. Wir wollen also nur Zeilen 2-3 und alle Spalten, d.h. Goethe fliegt raus
df_ohnegoethe
# Matrixartige Indizierung: Im zweiten Beispiel wollen wir uns nur die Sterbealter aller Dichter anzeigen lassen:
df[,2] # Wir wollen alle Zeilen (keine Wertabgabe, aber nur die Sterbealter, also die zweite Spalte)
# Dollarzeichen-Indizierung: Mit $-Zeichen können wir über die Spaltenüberschriften ('header') auf einzelne Spalten zugreifen. Das zweite Beispiel der Matrixartigen Indizierung erreichen wir also auch über das folgende Beispiel. Wir können so bspw. dann auch das mittlere Sterbealter unserer drei Dichter berechnen (mittels der Funktion 'mean()').
df$Sterbealter
mean(df$Sterbealter)
## Dichter Sterbealter Brillentraeger
## 2 Schiller 46 FALSE
## 3 Brecht 57 TRUE
## [1] 83 46 57
## [1] 83 46 57
## [1] 62
In diesem Abschnitt sehen wir, wie wir (1) bestehende Daten in einem Dataframe ändern, (2) Einträge hinzufügen oder löschen, (3) Daten in unserem Dataframe sortieren und (4) regelbasiert Teilmengen filtern können.
Eine kurze Recherche hat ergeben, dass Brecht nicht 57 sondern 58 Jahre alt wurde. Das wollen wir korrigieren. Dafür müssen wir zuerst den Wert, den wir ändern wollen, indizieren, um ihn dann ändern zu können. Welche Möglichkeiten stehen dafür zur Verfügung?
df[3,2]
df$Sterbealter[3]
df$Sterbealter[which(df$Dichter == "Brecht")] # die dritte Möglichkeit ist neu, wird später aber noch interessant...
# so können wir den Eintrag in unserem 'df' mit dem richtigen Sterbealter ersetzen; anhand von Beispiel 2 oben
df$Sterbealter[3] <- 58
df
## [1] 57
## [1] 57
## [1] 57
## Dichter Sterbealter Brillentraeger
## 1 Goethe 83 FALSE
## 2 Schiller 46 FALSE
## 3 Brecht 58 TRUE
Das Land der Dichter hat ja wohl noch mehr vorzuweisen als nur
Goethe, Schiller und Brecht. Wir wollen unseren Dataframe also mit
weiteren Einträgen (Zeilen) ergänzen (rbind()
). Außerdem
wollen wir für alle Dichter noch weitere Informationen (Spalten), z.B.
Anzahl der verfassten Dramen, hinzufügen (cbind()
).
# Hinzufügen von weiteren Zeilen
grass <- c("Grass", 88, TRUE) # die Elemente des Vektors entsprechen den Spalteüberschriften unseres 'df'
heine <- c("Heine", 59, FALSE)
df <- rbind(df, grass, heine) # 'rbind' hängt die Vektoren 'grass' und 'heine' als weitere Zeilen an 'df' an.
df
## Dichter Sterbealter Brillentraeger
## 1 Goethe 83 FALSE
## 2 Schiller 46 FALSE
## 3 Brecht 58 TRUE
## 4 Grass 88 TRUE
## 5 Heine 59 FALSE
# Hinzufügen weiterer Spalten
dramen <- c(28, 13, 48, 5, 2) # die Elemente des Vektors entsprechen der Anzahl der Dichter (Zeilen) in 'df'
df <- cbind(df, dramen) # 'cbind' hängt den Vektor 'dramen' als weitere Spalte an 'df' an.
df
## Dichter Sterbealter Brillentraeger dramen
## 1 Goethe 83 FALSE 28
## 2 Schiller 46 FALSE 13
## 3 Brecht 58 TRUE 48
## 4 Grass 88 TRUE 5
## 5 Heine 59 FALSE 2
Nun ist es leider so, dass die Spalte ‘Sterbealter’ nicht mehr
numerische Werte beinhaltet (siehe str(df)
). Das liegt
daran, dass wir in den Vektoren grass
und
heine
unterschiedliche Datentypen nutzen (wir erinnern uns:
unterschiedliche Datentypen in einem Vektor = nicht gut!). R legt diese
nun alle als Zeichenketten aus. Da wir mit diesen Werten allerdings
weiterrechnen wollen, müssen wir die Einträge dieser Spalte wieder in
numerische Werte umwandeln:
df$Sterbealter <- as.numeric(df$Sterbealter)
Jetzt ist es möglich, weitere Berechnungen vorzunehmen. Die Ergebnisse dieser Rechnung (wir berechnen ein Ergebnis mit den Vektoren aus zwei Spalten) können wir gleich in einer neuen Spalte sichern. Wir berechnen hier ‘höchst wissenschaftlich’ den Effizienzindex der jeweiligen Dichter, also die Anzahl der Dramenveröffentlichungen pro erreichtes Lebensalter. Da sehen wir, dass Brecht ein echter Malocher und Heine eher ein fauler Feingeist war…
df$Effizienzindex <- df$dramen / df$Sterbealter
df
## Dichter Sterbealter Brillentraeger dramen Effizienzindex
## 1 Goethe 83 FALSE 28 0.33734940
## 2 Schiller 46 FALSE 13 0.28260870
## 3 Brecht 58 TRUE 48 0.82758621
## 4 Grass 88 TRUE 5 0.05681818
## 5 Heine 59 FALSE 2 0.03389831
Für Dataframes mit vielen Einträgen kann es hilfreich sein, Dateneinträge nach Größe zu sortieren, z.B. signifikante Werte (kleine p-values) nach vorne. Wie das grundsätzlich funktioniert, folgt hier am Beispiel unserer Dichter und ihres Effizenzindexes.
df[order(df$Effizienzindex, decreasing = TRUE),] # Argument 'decreasing = FALSE' ergibt umgekehrte Reihenfolge
## Dichter Sterbealter Brillentraeger dramen Effizienzindex
## 3 Brecht 58 TRUE 48 0.82758621
## 1 Goethe 83 FALSE 28 0.33734940
## 2 Schiller 46 FALSE 13 0.28260870
## 4 Grass 88 TRUE 5 0.05681818
## 5 Heine 59 FALSE 2 0.03389831
Bei kann order()
auch noch weitere Sortierkriterien
bekommen.
Zufrieden mit dem Ergebnis? Eher nicht! Wir müssen uns eingestehen, dass der Effizienzindex Quatsch ist, schließlich haben die Dichter ja nicht nur Dramen veröffentlicht, sondern auch sonst viel Kram gemacht. Wir wollen diese Spalte also wieder löschen. Vielleicht denken wir aber auch, dass Grass gar kein echter Dichter war. In diesem Fall wollen wir eine Zeile löschen.
df.copy <- df # hiermit erstellen wir eine Kopie von 'df'. Empfiehlt sich immer, um Fehler rückgängig machen zu können
# Löschen wir zuerst Grass aus der Liste. Dafür löschen wie Zeile 4
df <- df[-4,] # einfach mit Minuszeichen vor der Indizierung
df
## Dichter Sterbealter Brillentraeger dramen Effizienzindex
## 1 Goethe 83 FALSE 28 0.33734940
## 2 Schiller 46 FALSE 13 0.28260870
## 3 Brecht 58 TRUE 48 0.82758621
## 5 Heine 59 FALSE 2 0.03389831
# Löschen wir nun die Spalte 'Effizienzindex' (Spalte 5). Das funktioniert entweder auch über die Indizierung, also 'df <- df[,-5]', oder indem wir der Spalte 'NULL' zuweisen:
df$Effizienzindex <- NULL
df
## Dichter Sterbealter Brillentraeger dramen
## 1 Goethe 83 FALSE 28
## 2 Schiller 46 FALSE 13
## 3 Brecht 58 TRUE 48
## 5 Heine 59 FALSE 2
Oftmals wirst Du mit Daten(tabellen) arbeiten, die Du nicht selbst erstellst, sondern von Behörden, aus dem Netz o.Ä. bezieht. In diesem Datensatz ist dann häufig nur ein Ausschnitt für Dich interessant, z.B. eine bestimmte Zeitreihe. R bietet hier eine Reihe eleganter Möglichkeiten, Teilmengen aus Datensätzen zu filtern. Einige davon hier einmal am Beispiel unserer Dichter demonstriert.
which()
können z.B. wir die
Dichter filtern, die das heutige Renteneintrittsalter nicht erreicht
haben:df[which(df$Sterbealter < 67),] # Indizierung sagt nur die Zeileneinträge für die die Abfrage zutrifft, und alle Spalten
## Dichter Sterbealter Brillentraeger dramen
## 2 Schiller 46 FALSE 13
## 3 Brecht 58 TRUE 48
## 5 Heine 59 FALSE 2
# ...oder wir filtern die Dichter, die älter als 50 wurden, aber jünger als 80 blieben
df[which(df$Sterbealter > 50 & df$Sterbealter < 80),]
## Dichter Sterbealter Brillentraeger dramen
## 3 Brecht 58 TRUE 48
## 5 Heine 59 FALSE 2
subset()
filtern wir hier alle
Dichter, die Brillenträger waren (1. Beispiel) bzw. keine Brillenträger
(2. Beispiel) waren:subset(df, df$Brillentraeger == TRUE) # '==' heißt 'ist gleich'
## Dichter Sterbealter Brillentraeger dramen
## 3 Brecht 58 TRUE 48
subset(df, df$Brillentraeger != TRUE) # '!=' heißt ist 'nicht-gleich'; ebenso ginge 'df$Brillentraeger==FALSE'
## Dichter Sterbealter Brillentraeger dramen
## 1 Goethe 83 FALSE 28
## 2 Schiller 46 FALSE 13
## 5 Heine 59 FALSE 2
df[c(1,5),c(1,3)] # 'c(1,5)' vor dem Komma sagt, wir nehmen Zeilen 1 und 5 mit;'c(1,3)' sagt, wir wollen nur Spalte 1 und 3
## Dichter Brillentraeger
## 1 Goethe FALSE
## NA <NA> <NA>
Damit haben haben wir nun eine Übersicht über das Wesen von Dataframes im Allgemeinen und einige grundsätzliche Operationen kennengelernt, die man mit ihnen vornehmen kann. In der Realität haben wir es mit weitaus umfangreichen Datensätzen zu tun. Die schreiben wir natürlich nicht manuell in einen Dataframe. Solche Tabellen (z.B. aus Excel) lassen sich in R einlesen und als Dataframe behandeln. Damit wird es im nächsten Teil weitergehen.