Skip to content
Christian Treffenstädt edited this page Apr 8, 2014 · 1 revision

Dataframes, Matrizen und Zugriffsoperationen

Stellen wir uns vor, wir haben 5 Versuchspersonen einen Fragebogen mit 4 Items vorgelegt. Genauer gesagt haben wir die Personen gebeten uns Angaben zu ihrem Alter, Geschlecht, ihrer Körpergröße (in Metern) und ihrem Intelligenzquotienten (welche sie unmittelbar vorher in einem anderen Test ermittelt haben) zu machen. Die Ergebnisse der einzelnen Items sind von uns zunächst in separate Listen übertragen worden.

In R werden Listen als Vektoren dargestellt. Vektoren entsprechen z.B. Listen in Python, oder Arrays in anderen Programmiersprachen. Ein Vektor in R besteht aus runden Klammern mit einem vorangestellten c:

age = c(25, 29, 20, 31, 25)
gender = c("m", "f", "f", "m", "f")
height = c(1.76, 1.83, 1.58, 1.95, 1.8)
iq = c(85, 125, 115, 90, 120)

Wie wir sehen können Vektoren unterschiedliche Arten von Elementen enthalten. Allerdings enthält jeder Vektor nur Elemente einer Sorte. Es ist nicht zulässig, dass Vektoren gemischte Arten von Elementen enthalten. Wenn wir versuchen einen Vektor mit gemischten Elementen zu erstellen, werden diese alle in ein gemeinsames Format übetragen (üblicherweise findet eine Konvertierung nach character statt, also in einen String).

Mit einzelnen Listen können wir in aller Regel wenig anfangen. Wir arbeiten normalerweise mit einem Experimentaldatensatz, der eine tabellarischen Datendarstellung aufweist, bei der unsere Versuchspersonen in Zeilen und die Variablen in den Spalten dargestellt werden.

Experimentaldatensätze können grundsätzlich auf zwei Arten in R repräsentiert sein. Zum einen gibt es den Datentyp "data.frame", der mit dem gleichlautenden Befehl erzeugt werden kann:

data_frame <- data.frame(age = age, gender = gender, height = height, iq = iq)

data_frame
##   age gender height  iq
## 1  25      m   1.76  85
## 2  29      f   1.83 125
## 3  20      f   1.58 115
## 4  31      m   1.95  90
## 5  25      f   1.80 120

Als Argumente der data.frame Funktion übergeben wir beliebige Spaltennamen und ordnen diesen dann Daten zu. In unserem Fall übergeben wir unsere Listen und als Spaltennamen nehmen wir einfach die Namen unserer Listen (daher z.B. age=age).

Ein Datenframe wird in R-Studio mit der Beschreibung X obs. of Y variables angegeben, wobei X der Anzahl der Zeilen und Y der Anzahl der Spalten in den Daten entspricht. Wenn wir eine CSV-Datendatei mit dem Befehl read.csv() oder read.csv2() einlesen, werden die enthaltenen Daten in ein Datenframe konvertiert. Deshalb ist bei unserer Arbeit das Datenframe der am häufigsten genutzte Datentyp.

Es gibt allerdings eine weitere Variante, um unsere Datensätze in R darzustellen. Dieser zweite Datentyp ist die Matrix. Wir können aus unseren Listen eine Matrix erzeugen, in dem wir diese mit Hilfe des Befehls cbind() als Spalten aneinander reihen (cbind steht für column bind. Es gibt auch den Befehl rbind, um Listen in Reihen aneinander zu fügen). Dabei ist zu beachten, dass die Listen, die mittels cbind() zusammengefügt werden, die selbe Länge haben sollten (Müssen sie zwar nicht, aber für uns macht in aller Regel nichts anderes Sinn):

data_matrix <- cbind(age, gender, height, iq)

data_matrix
##      age  gender height iq   
## [1,] "25" "m"    "1.76" "85" 
## [2,] "29" "f"    "1.83" "125"
## [3,] "20" "f"    "1.58" "115"
## [4,] "31" "m"    "1.95" "90" 
## [5,] "25" "f"    "1.8"  "120"

Eine Matrix wird in R-Studio mit der Beschreibung ZxY datatype matrix angegeben. Z ist dabei die Anzahl der Reihen, Y ist die Anzahl der Spalten und datatype bezeichnet den enthaltenen Datentyp. In unserem Beispiel handelt es sich um eine 5x4 character matrix. Ein Vektor in R entspricht übrigens einfach einer 1xY Matrix, wobei Y die Anzahl der Elemente in unserem Vektor beschreibt. Man spricht dabei auch von einer eindimensionalen Matrix.

Was dabei auffällt ist der Datentyp character und hier haben wir einen der wichtigsten Unterschiede zwischen Datenframes und Matrizen. Eine Matrix darf im Gegensatz zu einem Datenframe keine Unterschiedlichen Datentypen in verschiedenen Spalten besitzen. Bei einem Datenframe muss lediglich die Datenart innerhalb einer einzelnen Spalte konsistent sein (wie bei Listen auch), aber bei einer Matrix müssen alle Spalten den selben Datentyp enthalten. In unserem Beispiel wurden deshalb alle Listen in den Datentyp character konvertiert (also in Strings). Das erklärt auch die Anführungsstriche um unsere einzelnen Werte herum (Weil dies wie bei anderen Programmiersprachen auch die Kennzeichnung für einen String darstellt).

Da wir unsere Datensätze sehr häufig verschiedene Datentypen enthalten, ist es in der Regel besser diese im Format data.frame anzulegen. Die Einlesefunktionen read.csv() und read.csv2() tun dies für uns wie gesagt automatisch. Trotzdem sollte man sich mit dem Datentyp matrix ebenfalls vertraut machen, da manche Funktionen in R eine Matrix als Argument anfordern.

Datenframes und Matrizen lassen sich sehr leicht in den jeweils anderen Datentyp konvertieren. Dazu nutzen wir den Befehl as.data.frame() bzw. as.matrix():

as.data.frame(data_matrix)
##   age gender height  iq
## 1  25      m   1.76  85
## 2  29      f   1.83 125
## 3  20      f   1.58 115
## 4  31      m   1.95  90
## 5  25      f    1.8 120
as.matrix(data_frame)
##      age  gender height iq   
## [1,] "25" "m"    "1.76" " 85"
## [2,] "29" "f"    "1.83" "125"
## [3,] "20" "f"    "1.58" "115"
## [4,] "31" "m"    "1.95" " 90"
## [5,] "25" "f"    "1.80" "120"

Ein weiterer Vorteil des Datentyp data.frame ist der hier verwendbare $-Operator, mit dem man sehr leicht einzelne Spalten eines Datenframes, nicht aber einer Datenmatrix ausswählen kann:

data_frame$age
## [1] 25 29 20 31 25
data_matrix$age
## Error: $ operator is invalid for atomic vectors

Sowohl bei Matrizen als auch bei Datenframes können wir allerdings durch [] bestimmte Spalten, Zeilen oder Felder auswählen. Die Syntax hierfür lautet name[X,Y], wobei name den Namen des Datenframes oder der Matrix, X die Zeile und Y die Spalte beschreibt. Hier einige Beispiele:

# Auswahl des Feldes in Zeile 1 und Spalte 1:

data_frame[1, 1]
## [1] 25
data_matrix[1, 1]
##  age 
## "25"
# Auswahl der kompletten Zeile 1:

data_frame[1, ]
##   age gender height iq
## 1  25      m   1.76 85
data_matrix[1, ]
##    age gender height     iq 
##   "25"    "m" "1.76"   "85"
# Auswahl der kompletten Spalte 1:

data_frame[, 1]
## [1] 25 29 20 31 25
data_matrix[, 1]
## [1] "25" "29" "20" "31" "25"

Man beachte hierbei, dass zur Auswahl aller Spalten oder aller Reihen diese Angabe in den Klammern einfach leer gelassen wird. So können wir auch die komplette Matrix oder das komplette Datenframe durch [,] anzeigen lassen:

data_frame[, ]
##   age gender height  iq
## 1  25      m   1.76  85
## 2  29      f   1.83 125
## 3  20      f   1.58 115
## 4  31      m   1.95  90
## 5  25      f   1.80 120
data_matrix[, ]
##      age  gender height iq   
## [1,] "25" "m"    "1.76" "85" 
## [2,] "29" "f"    "1.83" "125"
## [3,] "20" "f"    "1.58" "115"
## [4,] "31" "m"    "1.95" "90" 
## [5,] "25" "f"    "1.8"  "120"

Wir können statt Angabe von Nummern zur Auswahl spezifischer Spalten auch die Spaltennamen verwenden:

data_frame[, "age"]
## [1] 25 29 20 31 25
data_matrix[, "age"]
## [1] "25" "29" "20" "31" "25"

Darüber hinaus können wir sogar mehrere Spalten auswhälen, in dem wir eine Liste mit Spaltennamen übergeben:

data_frame[, c("age", "height")]
##   age height
## 1  25   1.76
## 2  29   1.83
## 3  20   1.58
## 4  31   1.95
## 5  25   1.80
data_matrix[, c("age", "height")]
##      age  height
## [1,] "25" "1.76"
## [2,] "29" "1.83"
## [3,] "20" "1.58"
## [4,] "31" "1.95"
## [5,] "25" "1.8"

Das funktioniert ebenfalls mit den Spaltennummern oder mit Nummernsequenzen. Hier einige weitere Beispiele:

# Auswahl der Spalten 1 (age) und 3 (height) mit Spaltennummern

data_frame[, c(1, 3)]
##   age height
## 1  25   1.76
## 2  29   1.83
## 3  20   1.58
## 4  31   1.95
## 5  25   1.80
data_matrix[, c(1, 3)]
##      age  height
## [1,] "25" "1.76"
## [2,] "29" "1.83"
## [3,] "20" "1.58"
## [4,] "31" "1.95"
## [5,] "25" "1.8"
# Auswahl der Spalten 1 (age), 2 (gender) und 3 (height) mit einer
# Nummernsequenz

data_frame[, 1:3]
##   age gender height
## 1  25      m   1.76
## 2  29      f   1.83
## 3  20      f   1.58
## 4  31      m   1.95
## 5  25      f   1.80
data_matrix[, 1:3]
##      age  gender height
## [1,] "25" "m"    "1.76"
## [2,] "29" "f"    "1.83"
## [3,] "20" "f"    "1.58"
## [4,] "31" "m"    "1.95"
## [5,] "25" "f"    "1.8"
# Gemischte Auswahl von 1,2 und 4 durch Sequenz und Spaltennummer

data_frame[, c(1:2, 4)]
##   age gender  iq
## 1  25      m  85
## 2  29      f 125
## 3  20      f 115
## 4  31      m  90
## 5  25      f 120
data_matrix[, c(1:2, 4)]
##      age  gender iq   
## [1,] "25" "m"    "85" 
## [2,] "29" "f"    "125"
## [3,] "20" "f"    "115"
## [4,] "31" "m"    "90" 
## [5,] "25" "f"    "120"

Es ist allerdings keine Vermischung von Spaltennummern und Spaltennamen möglich:

data_frame[, c(1:2, "iq")]
## Error: undefined columns selected
data_matrix[, c(1:2, "iq")]
## Error: Indizierung außerhalb der Grenzen

Die hier aufgeführten Beispiele zur Auswahl von Spalten mit Hilfe von Spaltennummern lassen sich natürlich auch auf Zeilen übertragen.

Filteroperationen

Nachdem wir nun verschiedene Arten kennen gelernt haben, wie man auf spezifische Zeilen, Spalten oder Zellen innerhalb von Dataframes und Matrizen zugreifen kann, wollen wir uns eine weitere Methode ansehen, mit der man die Auswahl von Daten noch spezifischer gestalten kann.

Häufig ist es notwendig, nur Datensätze auszuwählen, die bestimmten Kriterien entsprechen, d.h. bei denen eine oder mehrere Variablen eine spezifische Ausprägung haben. Ein Beispiel hierfür wäre die Auswahl aller Frauen in einem Datensatz. Da Datensätze häufig nicht nach Geschlecht sortiert sind, muss die Auswahl mithilfe einer Filteroperation durchgeführt werden. Hierfür gibt es in R genau wie in anderen Programmiersprachen Vergleichsoperatoren, die das Filtern ermöglichen. Wir wählen nun alle Frauen in unserem Testdatensatz aus:

data_frame[data_frame[, 2] == "m", ]  # Auswahl in Datenframe mit Spaltennummer
##   age gender height iq
## 1  25      m   1.76 85
## 4  31      m   1.95 90
data_matrix[data_matrix[, 2] == "m", ]  # Auswahl in Datenmatrix mit Spaltennummer
##      age  gender height iq  
## [1,] "25" "m"    "1.76" "85"
## [2,] "31" "m"    "1.95" "90"
data_matrix[data_matrix[, "gender"] == "m", ]  # Auswahl in Datenmatrix mit Spaltenname
##      age  gender height iq  
## [1,] "25" "m"    "1.76" "85"
## [2,] "31" "m"    "1.95" "90"
data_frame[data_frame$gender == "m", ]  # Auswahl in Datenframe mit $-Operator und Variablenname
##   age gender height iq
## 1  25      m   1.76 85
## 4  31      m   1.95 90

Zu beachten ist hierbei die genaue Syntax in den eckigen Klammern. Wir definieren den Filter, in dem wir eine bestimmte Spalte auswählen (hier data_frame$gender) und eine Vergleichsoperation (== bedeutet 'ist gleich') für die Werte in dieser Spalte ausführen (also gender == 'm'). Diese Vergleichsoperation für eine Spalte steht nun aber in der eckigen Klammer auf der linken Seite des Kommas, wo wir die Zeilen angeben, die wir auswählen wollen. Die Logik hierbei ist, dass wir alle Zeilen auswählen möchten, in denen die Spalte 'gender' den Eintrag 'm' hat. Theoretisch kann man natürlich auch anders herum alle Spalten auswählen, in denen eine bestimmte Zeile einen spezifischen Wert hat. Dies ist für unsere Arbeit aber in der Regel irrelevant und so konzentrieren wir uns auf die Auswahl spezifischer Zeilen anhand von Spaltenwerten.

Hier einige weitere Beispiele für Auswahlfilter mit verschiedenen Vergleichsoperatoren:

data_frame[data_frame[, 3] <= 1.8, ]  # Alle Probanden, deren Körpergröße kleiner oder gleich 1.80m beträgt
##   age gender height  iq
## 1  25      m   1.76  85
## 3  20      f   1.58 115
## 5  25      f   1.80 120
data_matrix[data_matrix[, "iq"] > 115, ]  # Probanden mit einem IQ über 115 (115 wird ausgeschlossen)
##      age  gender height iq   
## [1,] "25" "m"    "1.76" "85" 
## [2,] "29" "f"    "1.83" "125"
## [3,] "31" "m"    "1.95" "90" 
## [4,] "25" "f"    "1.8"  "120"
data_matrix[data_matrix[, "iq"] >= 115, ]  # Probanden mit einem IQ größer oder gleich 115 (115 wird eingeschlossen)
##      age  gender height iq   
## [1,] "25" "m"    "1.76" "85" 
## [2,] "29" "f"    "1.83" "125"
## [3,] "20" "f"    "1.58" "115"
## [4,] "31" "m"    "1.95" "90" 
## [5,] "25" "f"    "1.8"  "120"
data_frame[data_frame$age < 30, ]  # Probanden, die jünger sind als 30 Jahre
##   age gender height  iq
## 1  25      m   1.76  85
## 2  29      f   1.83 125
## 3  20      f   1.58 115
## 5  25      f   1.80 120

Ab und zu ist es notwendig, spezifische einzelne Zellen auszuwählen anstatt bestimmter Reihen oder Spalten. Dabei ist es möglich mit Hilfe eines Filters eine Menge von Zellenauszuwählen, die über verschiedene Zeilen und verschiedene Spalten verteilt sein können. Um dies zu demonstrieren erstellen wir ein Datenframe, bei dem vereinzelt Werte fehlen und stattdessen der String 'None' eingetragen wurde:

age = c(25, 29, 20, "None", 31)
gender = c("m", "f", "f", "m", "f")
height = c(1.76, 1.83, 1.58, 1.95, "None")
iq = c("None", 125, 115, 90, 120)

data_frame <- data.frame(age = age, gender = gender, height = height, iq = iq)

data_frame
##    age gender height   iq
## 1   25      m   1.76 None
## 2   29      f   1.83  125
## 3   20      f   1.58  115
## 4 None      m   1.95   90
## 5   31      f   None  120

Wir haben nun einen Datensatz, in dem der Wert 'None' sich in verschiedenen Zeilen und Spalten wiederfindet. Nun ist es so, dass hierdurch der Datentyp für alle Variablen, die 'None' enthalten, auf Character festgelegt wurde (wie wir wissen enthalten Vektoren und auch Spalten in Datenframes keine gemischten Veriablentypen). Deswegen erkennt unser Datenframe nun alle Spalten als nichtnumerisch, so dass wir mit diesen nicht rechnen können.

Wir können nun unsere faktorielle Variable umwandeln in eine numerische Variable. Dieser Schritt erfolgt zweistufig, da wir die Funktionen as.character und as.numeric miteinander verknüpfen müssen. Eigentlich wollen wir ja eine Umwandlung in as.numeric, aber diese gibt uns lediglich Faktorstufen zurück, wenn wir hier eine faktorielle Variable übergeben. Wenn wir vorher unsere Werte mit as.character in nichtfaktorielle Stringvariablen umwandeln, erhalten wir anschließend durch die Umwandlung mit as.numeric die tatsächlichen Variablenwerte:

data_frame$age  # Age ist nun ein Vektor von Strings und wird deshalb als faktorielle Variable (Nominalskalenniveau und nicht als numerische Veriable angezeigt)
## [1] 25   29   20   None 31  
## Levels: 20 25 29 31 None
sum(data_frame$age, na.rm = TRUE)  # Summen können nicht über nichtnumerische Variablen gebildet werden
## Error: sum not meaningful for factors
data_frame$age2 <- as.numeric(data_frame$age)  # Gibt nicht die Alterswerte, sondern Stufennummern zurück

data_frame$age2
## [1] 2 3 1 5 4
data_frame$age3 <- as.numeric(as.character(data_frame$age))  # Gibt die Alterswerte zurück, aber mit einer Warnung
## Warning: NAs durch Umwandlung erzeugt
data_frame$age3
## [1] 25 29 20 NA 31
sum(data_frame$age3, na.rm = TRUE)  # Nun kann die korrekten Summen gebildet werden
## [1] 105

Nun haben wir wieder eine numerische Variable, mit der wir rechnen können. Allerdings gibt R bei der Umwandlung eine Warnung aus. Um dieses Problem zu beseitigen müssen wir 'None' vor der Umwandlung durch einen Wert ersetzen, der als fehlender Wert auch in numerischen Vektoren von R erkannt wird. Dieser Wert ist NA (not available) und stellt einen generellen Platzhalter für fehlende Werte in R dar. Um nun alle 'None' Einträge in unserem Datenframe durch NA zu ersetzen nutzen wir einen Filter, der genau auf diese spezifischen Zellen zugreift:

data_frame[data_frame == "None"] <- NA  # Ersetzt alle 'None' Einträge durch NA

data_frame$age
## [1] 25   29   20   <NA> 31  
## Levels: 20 25 29 31 None
as.numeric(as.character(data_frame$age))
## [1] 25 29 20 NA 31

Wie wir sehen haben wir nun dasselbe Ergebnis wie bei einer automatischen Umwandlung von 'None' in NA, allerdings ohne die Warnung. In diesem Fall ist die manuelle Umwandlung offensichtlich unnötig, aber in anderen Fällen kann dies durchaus sinnvoll sein und es lassen sich auch andere Beispiele finden, in denen man auf spezifische Zellen im gesamten Datensatz zugreiden möchte, z.B. um einzelne Werte zu rekodieren.

Der Befehl grep()

Wir haben gelernt, dass man mit Hilfe von Spaltennamen anstatt von Spaltennummern sowohl in Matrizen als auch Dataframes auf einzelne Variablen zugreifen kann. Bei Dataframes geht dies sowohl mit [] als auch mit dem $-Operator. Besonders bei großen Datensätzen (z.B. bei Messwiederholungsdesigns) ist es häufig sehr viel leichter die Variablennamen für den Datenzugriff zu nutzen, als die Spalten zu zählen und die entsprechenden Nummern zu ermitteln. Aber auch hier kann der Datenzugriff umständlich werden, z.B. wenn wir auf sehr viele Variablen oder Variablen mit komplizierten Namen zugreifen möchten.

Anstatt in solchen Fällen einfach wieder die Spaltennummern abzuzählen bietet uns R mit dem Befehl grep() die Möglichkeit, mit einem sogenannten regulären Ausdruck nach Variablennamen zu suchen. Hier ein Beispiel für einen Datensatz mit einigen messwiederholten Variablen:

trial1 = c(2, 4, 1, 1, 5)
trial2 = c(1, 4, 2, 2, 4)
trial3 = c(5, 3, 3, 1, 3)
trial4 = c(3, 4, 4, 2, 2)
trial5 = c(2, 4, 5, 1, 1)
trial6 = c(5, 6, 1, 2, 5)
trial7 = c(2, 4, 2, 1, 4)
trial8 = c(3, 4, 3, 4, 4)
trial9 = c(3, 7, 4, 1, 3)
trial10 = c(5, 4, 5, 5, 2)
trial11 = c(4, 8, 1, 1, 1)
trial12 = c(4, 4, 2, 3, 1)
trial13 = c(2, 4, 3, 1, 5)
trial14 = c(1, 3, 4, 2, 4)
trial15 = c(1, 4, 5, 1, 3)
trial16 = c(2, 4, 2, 2, 2)
trial17 = c(3, 4, 2, 2, 1)
trial18 = c(2, 4, 3, 2, 4)
trial19 = c(4, 5, 4, 1, 5)
trial20 = c(2, 4, 5, 4, 5)

data_frame <- data.frame(trial1 = trial1, trial2 = trial2, trial3 = trial3, 
    trial4 = trial4, trial5 = trial5, trial6 = trial6, trial7 = trial7, trial8 = trial8, 
    trial9 = trial9, trial10 = trial10, trial11 = trial11, trial12 = trial12, 
    trial13 = trial13, trial14 = trial14, trial15 = trial15, trial16 = trial16, 
    trial17 = trial17, trial18 = trial18, trial19 = trial19, trial20 = trial20)

In diesem einfachen Datenframe ist es sehr leicht auf die komplette Sequenz der Trials zuzugreifen:

data_frame[, 1:20]
##   trial1 trial2 trial3 trial4 trial5 trial6 trial7 trial8 trial9 trial10
## 1      2      1      5      3      2      5      2      3      3       5
## 2      4      4      3      4      4      6      4      4      7       4
## 3      1      2      3      4      5      1      2      3      4       5
## 4      1      2      1      2      1      2      1      4      1       5
## 5      5      4      3      2      1      5      4      4      3       2
##   trial11 trial12 trial13 trial14 trial15 trial16 trial17 trial18 trial19
## 1       4       4       2       1       1       2       3       2       4
## 2       8       4       4       3       4       4       4       4       5
## 3       1       2       3       4       5       2       2       3       4
## 4       1       3       1       2       1       2       2       2       1
## 5       1       1       5       4       3       2       1       4       5
##   trial20
## 1       2
## 2       4
## 3       5
## 4       4
## 5       5

Allerdings wird dies problematischer, wenn die Trials nicht alle am Stück gespeichert sind:

interception = c(99, 99, 99, 99, 99)

data_frame <- data.frame(trial1 = trial1, trial2 = trial2, trial3 = trial3, 
    trial4 = trial4, trial5 = trial5, trial6 = trial6, trial7 = trial7, trial8 = trial8, 
    trial9 = trial9, trial10 = trial10, interception = interception, trial11 = trial11, 
    trial12 = trial12, trial13 = trial13, trial14 = trial14, trial15 = trial15, 
    trial16 = trial16, trial17 = trial17, trial18 = trial18, trial19 = trial19, 
    trial20 = trial20)

Hier müssen wir unsere Sequenz splitten:

data_frame[, c(1:10, 12:21)]
##   trial1 trial2 trial3 trial4 trial5 trial6 trial7 trial8 trial9 trial10
## 1      2      1      5      3      2      5      2      3      3       5
## 2      4      4      3      4      4      6      4      4      7       4
## 3      1      2      3      4      5      1      2      3      4       5
## 4      1      2      1      2      1      2      1      4      1       5
## 5      5      4      3      2      1      5      4      4      3       2
##   trial11 trial12 trial13 trial14 trial15 trial16 trial17 trial18 trial19
## 1       4       4       2       1       1       2       3       2       4
## 2       8       4       4       3       4       4       4       4       5
## 3       1       2       3       4       5       2       2       3       4
## 4       1       3       1       2       1       2       2       2       1
## 5       1       1       5       4       3       2       1       4       5
##   trial20
## 1       2
## 2       4
## 3       5
## 4       4
## 5       5

Es ist natürlich vorstellbar, dass wir sehr viele verschiedene Variablen zwischen unseren Trialvariablen haben und im Extremfall müssten wir jede Zeilennummer einzeln angeben. Die Alternative ist ein Zugriff durch eine Liste der Variablennamen:

trial_names = c("trial1", "trial2", "trial3", "trial4", "trial5", "trial6", 
    "trial7", "trial8", "trial9", "trial10", "trial11", "trial12", "trial13", 
    "trial14", "trial15", "trial16", "trial17", "trial18", "trial19", "trial20")

data_frame[, trial_names]  # das entspricht dem Befehl, wenn wir die Liste direkt hineingeschrieben hätten
##   trial1 trial2 trial3 trial4 trial5 trial6 trial7 trial8 trial9 trial10
## 1      2      1      5      3      2      5      2      3      3       5
## 2      4      4      3      4      4      6      4      4      7       4
## 3      1      2      3      4      5      1      2      3      4       5
## 4      1      2      1      2      1      2      1      4      1       5
## 5      5      4      3      2      1      5      4      4      3       2
##   trial11 trial12 trial13 trial14 trial15 trial16 trial17 trial18 trial19
## 1       4       4       2       1       1       2       3       2       4
## 2       8       4       4       3       4       4       4       4       5
## 3       1       2       3       4       5       2       2       3       4
## 4       1       3       1       2       1       2       2       2       1
## 5       1       1       5       4       3       2       1       4       5
##   trial20
## 1       2
## 2       4
## 3       5
## 4       4
## 5       5

Nun brauchen wir uns keine Gedanken mehr darum zu machen, in welchen Spalten sich unsere Variablen befinden. Es macht aber auch einige Arbeit die Liste aufzuschreiben. Es gibt dann allerdings auch solche Fälle, in denen die Variablennamen sehr lang oder umständlich werden und nur noch ein kleiner Teil des Namens eine Aussage darüber enthält, um welche Trialdaten es sich handelt.

Als Beispiel passen wir nun die Variablennamen in unserem Befehl data.frame() an:

data_frame <- data.frame(testtrial1blub = trial1, whatevertrial2 = trial2, trial3 = trial3, 
    haha.trial4 = trial4, siehstemaltrial5 = trial5, trial6soso = trial6, trial7 = trial7, 
    trial8 = trial8, trial9 = trial9, trial10 = trial10, interception = interception, 
    trial11spass = trial11, trial12 = trial12, trial13 = trial13, trial14versehen = trial14, 
    Damn.You.trial15 = trial15, trial16 = trial16, Offendingtrial17 = trial17, 
    Gibts.das.trial18 = trial18, trial19braucht.man = trial19, trial20finito = trial20)

Stellen wir uns vor, die hier beschriebenen Variablen liegen jetzt auch noch an ganz unterschiedlichen Stellen in einem sehr großen Datensatz. Zum einen ist es für uns nun sehr anstrengend die Liste mit den Variablennamen zu schreiben (nicht einfach copy & paste), aber das Abzählen der Spaltennamen ist auch keine Alternative.

Für diese Fälle schenkt uns R den Befehl grep(). Dieser Befehl erlaubt es uns mittels regulären Ausdrücken nach Variablennamen zu suchen, die bestimmte Kriterien erfüllen.

Ein regulärer Ausdruck definiert ein bestimmtes Muster innerhalb eines Strings, nach dem wir mit grep() suchen können. Das einfachste Beispiel ist nach einer bestimmten Buchstabenfolge zu suchen, die in allen gesuchten Variablennamen auftritt, aber nicht in irgendeinem Variablennamen, den wir nicht suchen. In unserem Beispiel können wir nach trial suchen, da diese Buchstabenfolge nur in den von uns gesuchten Variablen auftritt (und nicht in interception):

variables <- grep("trial", names(data_frame))

variables
##  [1]  1  2  3  4  5  6  7  8  9 10 12 13 14 15 16 17 18 19 20 21

Die beiden wichtigsten Argumente von grep() sind das gesuchte Muster (pattern) und die Strings, in denen nach dem spezifizierten Muster gesucht werden soll. Wir übergeben des Muster als einfachen String 'trial', da wir nach dieser Buchstabenfolge suchen. Der Suchort sind die Variablennamen unserer Dataframes, die wir mit dem Befehl names() erhalten.

In der Standardeinstellung liefert uns grep() die Spaltennummern aller Variablen, deren Namen mit unserem Suchmuster kompatibel sind (bei denen es also einen match für das pattern gab). Daher erhalten wir beim Ausführen des Befehls eine Liste von Nummern. Diese können wir wiederum benutzen, um auf die gefundenen Variablen zuzugreifen:

data_frame[, variables]
##   testtrial1blub whatevertrial2 trial3 haha.trial4 siehstemaltrial5
## 1              2              1      5           3                2
## 2              4              4      3           4                4
## 3              1              2      3           4                5
## 4              1              2      1           2                1
## 5              5              4      3           2                1
##   trial6soso trial7 trial8 trial9 trial10 trial11spass trial12 trial13
## 1          5      2      3      3       5            4       4       2
## 2          6      4      4      7       4            8       4       4
## 3          1      2      3      4       5            1       2       3
## 4          2      1      4      1       5            1       3       1
## 5          5      4      4      3       2            1       1       5
##   trial14versehen Damn.You.trial15 trial16 Offendingtrial17
## 1               1                1       2                3
## 2               3                4       4                4
## 3               4                5       2                2
## 4               2                1       2                2
## 5               4                3       2                1
##   Gibts.das.trial18 trial19braucht.man trial20finito
## 1                 2                  4             2
## 2                 4                  5             4
## 3                 3                  4             5
## 4                 2                  1             4
## 5                 4                  5             5

Wie wir sehen erhalten wir alle Variablen, die unsere Trialdaten enthalten. Wir sehen also, dass die Verwendung des Befehls grep() uns eine Menge Zeit sparen kann. Um uns nicht die Spaltennummern, sondern die gefundenen Variablennamen ausgeben zu lassen, können wir auch das Argument value auf True setzen:

variables <- grep("trial", names(data_frame), value = TRUE)

variables
##  [1] "testtrial1blub"     "whatevertrial2"     "trial3"            
##  [4] "haha.trial4"        "siehstemaltrial5"   "trial6soso"        
##  [7] "trial7"             "trial8"             "trial9"            
## [10] "trial10"            "trial11spass"       "trial12"           
## [13] "trial13"            "trial14versehen"    "Damn.You.trial15"  
## [16] "trial16"            "Offendingtrial17"   "Gibts.das.trial18" 
## [19] "trial19braucht.man" "trial20finito"

Hier lässt sich sehr viel leichter überprüfen, ob auch tatsächlich die richtigen Variablen ausgewählt wurden und diese Namensliste lässt sich natürlich genau so verwenden wie die Liste der Spaltennummern:

data_frame[, variables]
##   testtrial1blub whatevertrial2 trial3 haha.trial4 siehstemaltrial5
## 1              2              1      5           3                2
## 2              4              4      3           4                4
## 3              1              2      3           4                5
## 4              1              2      1           2                1
## 5              5              4      3           2                1
##   trial6soso trial7 trial8 trial9 trial10 trial11spass trial12 trial13
## 1          5      2      3      3       5            4       4       2
## 2          6      4      4      7       4            8       4       4
## 3          1      2      3      4       5            1       2       3
## 4          2      1      4      1       5            1       3       1
## 5          5      4      4      3       2            1       1       5
##   trial14versehen Damn.You.trial15 trial16 Offendingtrial17
## 1               1                1       2                3
## 2               3                4       4                4
## 3               4                5       2                2
## 4               2                1       2                2
## 5               4                3       2                1
##   Gibts.das.trial18 trial19braucht.man trial20finito
## 1                 2                  4             2
## 2                 4                  5             4
## 3                 3                  4             5
## 4                 2                  1             4
## 5                 4                  5             5

Ein weiteres Argument von grep() erlaubt es, alle Variablen anzuzeigen, die nicht zum definierten Muster passen. Wir setzen invert auf True:

variables <- grep("trial", names(data_frame), value = TRUE, invert = TRUE)

variables
## [1] "interception"

Reguläre Ausdrücke sind ein sehr mächtiges Werkzeug beim Programmieren. Sie erlauben es uns mit Hilfe einer speziellen Syntax nicht nur nach bestimmten festgelegten Zeichenfolgen zu suchen, sondern auch dynamisch nach sehr komplexen Mustern in Strings zu suchen.

Ein paar kurze Beispiele für spezielle Zeichen, die wir in regukären Ausdrücken verwenden können:

  • . steht für ein beliebiges Zeichen
  • ? sagt, dass das vorangegangene Zeichen 0 oder einmal auftritt
    • sagt, dass das vorangegangene Zeichen mindestens 1 mal (bis unbegrenzt) auftritt
    • sagt, dass das vorangegangene Zeichen 0 oder bis unbegrenzt oft auftritt

Wir benutzen ein paar dieser Zeichen nun in einigen Beispielen:

grep(".*", names(data_frame), value = TRUE)  # Eine unbegrenzte Menge beliebiger Zeichen
##  [1] "testtrial1blub"     "whatevertrial2"     "trial3"            
##  [4] "haha.trial4"        "siehstemaltrial5"   "trial6soso"        
##  [7] "trial7"             "trial8"             "trial9"            
## [10] "trial10"            "interception"       "trial11spass"      
## [13] "trial12"            "trial13"            "trial14versehen"   
## [16] "Damn.You.trial15"   "trial16"            "Offendingtrial17"  
## [19] "Gibts.das.trial18"  "trial19braucht.man" "trial20finito"
grep(".ri.l", names(data_frame), value = TRUE)  # t und a werden durch beliebige Zeichen ersetzt
##  [1] "testtrial1blub"     "whatevertrial2"     "trial3"            
##  [4] "haha.trial4"        "siehstemaltrial5"   "trial6soso"        
##  [7] "trial7"             "trial8"             "trial9"            
## [10] "trial10"            "trial11spass"       "trial12"           
## [13] "trial13"            "trial14versehen"    "Damn.You.trial15"  
## [16] "trial16"            "Offendingtrial17"   "Gibts.das.trial18" 
## [19] "trial19braucht.man" "trial20finito"
grep("ia?", names(data_frame), value = TRUE)  # die Buchstabenfolge ia, wobei a optional ist
##  [1] "testtrial1blub"     "whatevertrial2"     "trial3"            
##  [4] "haha.trial4"        "siehstemaltrial5"   "trial6soso"        
##  [7] "trial7"             "trial8"             "trial9"            
## [10] "trial10"            "interception"       "trial11spass"      
## [13] "trial12"            "trial13"            "trial14versehen"   
## [16] "Damn.You.trial15"   "trial16"            "Offendingtrial17"  
## [19] "Gibts.das.trial18"  "trial19braucht.man" "trial20finito"
grep("ia*", names(data_frame), value = TRUE)  # die Buchstabenfolge ia, wobei a nicht bis unbegrenzt auftreten kann
##  [1] "testtrial1blub"     "whatevertrial2"     "trial3"            
##  [4] "haha.trial4"        "siehstemaltrial5"   "trial6soso"        
##  [7] "trial7"             "trial8"             "trial9"            
## [10] "trial10"            "interception"       "trial11spass"      
## [13] "trial12"            "trial13"            "trial14versehen"   
## [16] "Damn.You.trial15"   "trial16"            "Offendingtrial17"  
## [19] "Gibts.das.trial18"  "trial19braucht.man" "trial20finito"
grep("ia+", names(data_frame), value = TRUE)  # die Buchstabenfolge ia, wobei a mindestens einmal vorkommen muss
##  [1] "testtrial1blub"     "whatevertrial2"     "trial3"            
##  [4] "haha.trial4"        "siehstemaltrial5"   "trial6soso"        
##  [7] "trial7"             "trial8"             "trial9"            
## [10] "trial10"            "trial11spass"       "trial12"           
## [13] "trial13"            "trial14versehen"    "Damn.You.trial15"  
## [16] "trial16"            "Offendingtrial17"   "Gibts.das.trial18" 
## [19] "trial19braucht.man" "trial20finito"

Hier zwei weitere wichtige Zeichen:

  • ^ markiert den Anfang des Strings (keine Zeichen davor erlaubt)
  • $ markiert das Ende des Strings (keine Zeichen danach erlaubt)
grep("^trial", names(data_frame), value = TRUE)  # Der String beginnt mit Trial
##  [1] "trial3"             "trial6soso"         "trial7"            
##  [4] "trial8"             "trial9"             "trial10"           
##  [7] "trial11spass"       "trial12"            "trial13"           
## [10] "trial14versehen"    "trial16"            "trial19braucht.man"
## [13] "trial20finito"
grep("trial..?$", names(data_frame), value = TRUE)  # Der String endet mit Trial und ein bis zwei beliebigen Zeichen
##  [1] "whatevertrial2"    "trial3"            "haha.trial4"      
##  [4] "siehstemaltrial5"  "trial7"            "trial8"           
##  [7] "trial9"            "trial10"           "trial12"          
## [10] "trial13"           "Damn.You.trial15"  "trial16"          
## [13] "Offendingtrial17"  "Gibts.das.trial18"

Wenn wir nun nach einem Zeichen suchen wollen, dass eigentlich ein Sonderzeichen darstellt, müssen wir diesem Zeichen ein \ voranstellen (das zeichen "escapen")

grep(".trial", names(data_frame), value = TRUE)  # Der String enthält trial und davor ein beliebiges Zeichen
## [1] "testtrial1blub"    "whatevertrial2"    "haha.trial4"      
## [4] "siehstemaltrial5"  "Damn.You.trial15"  "Offendingtrial17" 
## [7] "Gibts.das.trial18"
grep("\\.trial", names(data_frame), value = TRUE)  # Der String enthält trial und davor einen Punkt
## [1] "haha.trial4"       "Damn.You.trial15"  "Gibts.das.trial18"

Achtung! Eigentlich braucht man bei regulären Ausdrücken nur ein ** zum escapen, allerdings brauchen wir hier zwei, da wir unseren Ausdruck in einen R-String schreiben und R ansonsten denkt, dass das Zeichen für diesen String gilt und nicht für den regulären Ausdruck darin. Zwei \ bedeuten also einfach nur, dass das eine ** an den regulären Ausdruck durchgereicht wird.

Es gibt auch spezielle Escape-Sequenzen, die in regukären Ausdrücken eine Bedeutung haben:

  • \d Ziffern 0 bis 9 (in R \d)
  • \D Alles außer Ziffern (in R \D)
  • \w Alle Buchstaben und Ziffern (in R \w)
  • \s Whitespace-Zeichen, z.B. Leertaste, Zeilenumbruch, Tabstop, etc. (in R \s)

Hier ein paar Beispiele:

grep("trial\\d\\d", names(data_frame), value = TRUE)  # Der String enthält trial und danach zwei Ziffern
##  [1] "trial10"            "trial11spass"       "trial12"           
##  [4] "trial13"            "trial14versehen"    "Damn.You.trial15"  
##  [7] "trial16"            "Offendingtrial17"   "Gibts.das.trial18" 
## [10] "trial19braucht.man" "trial20finito"
grep("trial\\d\\D", names(data_frame), value = TRUE)  # Der String enthält trial, danach eine Ziffer und folgend keine Ziffer
## [1] "testtrial1blub" "trial6soso"
grep("\\wtrial", names(data_frame), value = TRUE)  # Der String enthält trial und davor einen Buchstaben oder eine Ziffer
## [1] "testtrial1blub"   "whatevertrial2"   "siehstemaltrial5"
## [4] "Offendingtrial17"

Es ist auch möglich mittels [] eine bestimmte Menge von möglichen Zeichen zu definieren, oder Zeichen auszuschließen:

grep("[a-z]trial", names(data_frame), value = TRUE)  # Der String enthält trial und davor einen Kleinbuchstaben
## [1] "testtrial1blub"   "whatevertrial2"   "siehstemaltrial5"
## [4] "Offendingtrial17"
grep("[ar]trial", names(data_frame), value = TRUE)  # Der String enthält trial und davor a oder r
## [1] "whatevertrial2"
grep("[a.]trial", names(data_frame), value = TRUE)  # Der String enthält trial und davor a oder einen Punkt
## [1] "haha.trial4"       "Damn.You.trial15"  "Gibts.das.trial18"
grep("[^.]trial", names(data_frame), value = TRUE)  # Der String enthält trial und davor ein beliebiges Zeichen, aber KEINEN Punkt
## [1] "testtrial1blub"   "whatevertrial2"   "siehstemaltrial5"
## [4] "Offendingtrial17"
grep("trial\\d\\d?[A-Za-z]", names(data_frame), value = TRUE)  # Der String enthält trial und danach ein oder zwei Ziffer und dann Buchstaben
## [1] "testtrial1blub"     "trial6soso"         "trial11spass"      
## [4] "trial14versehen"    "trial19braucht.man" "trial20finito"

Mit {} kann man eine Unter- und Obergrenze für das Auftreten bestimmter Zeichen festlegen:

grep("trial\\d{1,2}", names(data_frame), value = TRUE)  # Der String enthält trial und danach eine oder zwei Ziffern
##  [1] "testtrial1blub"     "whatevertrial2"     "trial3"            
##  [4] "haha.trial4"        "siehstemaltrial5"   "trial6soso"        
##  [7] "trial7"             "trial8"             "trial9"            
## [10] "trial10"            "trial11spass"       "trial12"           
## [13] "trial13"            "trial14versehen"    "Damn.You.trial15"  
## [16] "trial16"            "Offendingtrial17"   "Gibts.das.trial18" 
## [19] "trial19braucht.man" "trial20finito"
grep("trial\\d{1,2}$", names(data_frame), value = TRUE)  # Der String enthält trial und danach eine oder zwei Ziffern und danach keine weiteren Zeichen
##  [1] "whatevertrial2"    "trial3"            "haha.trial4"      
##  [4] "siehstemaltrial5"  "trial7"            "trial8"           
##  [7] "trial9"            "trial10"           "trial12"          
## [10] "trial13"           "Damn.You.trial15"  "trial16"          
## [13] "Offendingtrial17"  "Gibts.das.trial18"
grep("^[A-Za-z0-9]{6,7}$", names(data_frame), value = TRUE)  # Enthält 6 oder 7 Buchstaben und Ziffern und sonst nichts
## [1] "trial3"  "trial7"  "trial8"  "trial9"  "trial10" "trial12" "trial13"
## [8] "trial16"
grep("^[A-Za-z0-9]{7}$", names(data_frame), value = TRUE)  # Enthält genau 7 Buchstaben und Ziffern und sonst nichts
## [1] "trial10" "trial12" "trial13" "trial16"

Wir sehen, dass man extrem komplizierte Muster erstellen kann, die nur zu ganz bestimmten Buchstabenkombinationen passen. Wir benutzen reguläre Ausdrücke auch häufig in Python und Alfred enthält sogar ein Element, bei dem man den Input gegen einen selbstdefinierten regulären Ausdruck matchen kann. So überprüfen wir zum Beispiel auf die Eingabe einer validen E-Mailadresse:

regEx = "^[A-Za-z0-9._%+-äÄüÜöÖß]+@[A-Za-z0-9.-äÄüÜöÖß]+\\.[A-Za-zäÄüÜöÖß]{2,6}$"

mail_list = c("test", "bla", "blub.com", "alfred@aol", "[email protected]", 
    "falsches Zeug")

grep(regEx, mail_list, value = TRUE)