1 Legende

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:

2 Was sind Dataframes?

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.

3 Bevor es losgeht - ein paar Grundlagen

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.

4 Erstellen eines Dataframes in R

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

5 Erkunden des Dataframes

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

6 Wie greifen wir nun auf Elemente in unserem Dataframe zu?

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

7 Modifizierungen von Dataframes

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.

7.1 Bestehende Daten ändern

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

7.2 Einträge hinzufügen

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.

7.3 Einträge löschen

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

7.4 Regelbasierte Teilmengen

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.

  1. mit Hilfe der Funktion 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
  1. mit Hilfe der Funktion 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
  1. mittels Indizierungen greifen wir nur auf bestimmte Ausschnitte unseres Dataframes zu. Hier müssen wir vorher wissen, was wir mitnehmen wollen; d.h. diese Teilmenge ist nicht regelbasiert (zumindest weiß R davon nichts):
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>

8 Wie geht es weiter?

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.