Zum Hauptinhalt springen

R Tipps und Tricks - Teil 1

R, der Open-Source-Allrounder mit schwerem Einstieg

Vor etwa drei Jahren bin ich von kommerziellen Statistiklösungen, wie SPSS, auf R umgestiegen. Mittlerweile kann ich mit Überzeugung sagen, dass ich erstmal kein anderes Tool mehr für Advanced Analytics brauche. Vor allem in Verbindung mit der IDE "R-Studio" hat die Software einen Reifegrad erreicht, um sie bedenkenlos in großen Data-Science-Projekten einzusetzen.

Man braucht sich allerdings nicht vormachen, dass man R einfach installiert und loslegt. Die Lernkurve ist vergleichsweise steil und es gibt nicht nur in Bezug auf die verschiedenen Pakete viele unterschiedliche Wege, dasselbe zu tun. Nicht selten hab ich mich geärgert, dass ich mitten im Auswerten plötzlich über einen banalen Schritt gestolpert bin, dessen Umsetzung ich für R erst recherchieren musste. Ich möchte daher in diesem und hoffentlich vielen folgenden Teilen Tipps und Tricks für R aufgreifen, die ich gerne schon früher als Einsteiger gekannt hätte.

Spaltenselektion in R

Scheinbar verschiedene Schreibweisen

Fangen wir mit etwas Banalem an, der Spaltenselektion in Dataframes. Sie wird natürlich in allen Einführungswerken behandelt, aber ich war ehrlich gesagt nicht immer von den Erläuterungen überzeugt. R unterscheidet drei Schreibweisen, um eine Spalte zu selektieren. Tatsächlich sind es nur zwei, nämlich...

data['spaltenname']
data[['spaltenname']]
data$spaltenname

Das hat mich zu Beginn immer etwas verwirrt. Die Schreibweise data$spaltenname ist natürlich die klar bevorzugte, da man nicht mit lästigen Klammern und Anführungszeichen hantieren muss. Noch wichtiger ist die Autovervollständigung unter R-Studio, wenn man STRG + SPACE drückt. Diese funktioniert halt nur so.

Nun habe ich ständig die Situation, in der ich den Namen einer Spalte als String in einem Skalar vorliegen habe, z.B. wenn ich eine Schleife über eine Liste von Variablen laufen lasse. Dort hab ich dann als Anfängerfehler häufig die Schreibweise data[spaltenNamenString] statt data[[spaltenNamenString]] gewählt, weil ich es aus anderen Programmiersprachen einfach so gewohnt bin. Manchmal funktioniert es trotzdem auf wundersame Weise, meistens aber wirft irgendetwas einen Fehler.

Die Variante mit doppelten Klammern ist technisch identisch mit der Variante mit $. Die Variante mit einfachen Klammern dagegen ist nicht wirklich ein Weg, um eine Spalte aus einem Dataframe zu extrahieren. In Tutorials und einem Buch hab ich die Erläuterung gefunden, dass damit ein neues Dataframe mit der genannten Spalte darin zurückgegeben wird. Das ist nur insofern richtig, als es nicht notwendigerweise ein Dataframe sein muss. Es kann auch irgendein anderer Datencontainer sein, der diese Schreibweise unterstützt.

Hintergründe der Selektion mit einfachen Klammern

Aber wozu eigentlich? Warum sollte ich mit data['spaltenName'] eine Kopie des ursprünglichen Dataframes mit nur einer Spalte ziehen? In dieser Form ist das Literal tatsächlich eher nutzlos, das liegt aber daran, dass es sich um einen Spezialfall eines anderen Anwendungsfalles handelt.

R bietet ja die Möglichkeit, ein Spaltensubset eines Dataframes zu selektieren, eben mit dieser Schreibweise, z.B. data[c('spalte_A', 'spalte_B', 'spalte_C')], oder auch nur über die Indizes, z.B. data[c(1,2,89,99)] oder data[1:10].

Nun ist R so gestrickt, dass es keine eigentlichen Variablen in dem Sinne eines typisierten singulären Wertes gibt. Es gibt stattdessen Skalare, also Vektoren mit Elementen des gleichen Typs. Wenn man spaltenName<-'spalte_a' schreibt, dann legt man eben einen Character-Vektor der Länge 1 an, gleichbedeutend mit spaltenName<-c('spalte_a').

dummy<-'dummyText'
dummy2<-c('dummyText')
identical(dummy, dummy2) ## Ergebnis True

Somit wird das Resultat von data['spaltenName'] verständlicher, da es data[c('spaltenName')] entspricht.

Kreuztabellen korrekt beschriftet

Früher habe ich Kreuztabellen immer so formuliert, da ich dann die Autovervollständigung in R-Studio beim Tippen nutzen konnte: table(datensatz$spalteA~datensatz$spalteB). Ich hab mich dann immer geärgert, dass ich am Output jedes Mal überlegen musste, was denn nun in den Spalten und was in den Zeilen gezählt wird, da die Beschriftung fehlte. Mit dem Wissen von oben ist es eigentlich klar, warum. Die Daten aus Spalte A und Spalte B werden aus dem Datensatz als Vektoren extrahiert, die an sich keine Namen haben.

Wenn ich die Kreuztabelle aber so schreibe: table(datensatz[c('spalteA', 'spalteB')]), dann übergebe ich nicht zwei anonyme Vektoren, sondern einen data.frame mit zwei Spalten, die ihre Namen behalten. Dann klappt es auch mit der Beschriftung.

Best Practice für Spaltenfilter

Abschließend möchte ich in diesem Zusammenhang noch eine Best-Practice-Empfehlung geben, und zwar: Die Schreibweisen mit der direkten Angabe von Indizes, wie data[1:10], sollte man besser gleich wieder vergessen, sie sind zu fehleranfällig und unleserlich. Ich definiere mittlerweile über verschiedene Wege immer einen Vektor mit den Spaltennamen, z.B. predictors_relevant = c('Predictor01','Predictor03','Predictor05','Predictor09') und wähle im Anschluss nur über diesen Vektor aus, z.B. data[predictors_relevant]. Das macht den Code dynamischer und ich weiß bei sprechenden Titeln sofort, was ich eigentlich gerade auswähle.

Genauso halte ich nicht viel von der Minus-Schreibweise, um Spalten auszuschließen. Ich verwende stattdessen setdiff, dann sind die beteiligten Spalten alle im Klartext leserlich, z.B. wenn ich alle Prädiktoren außer 'predictor03' verwenden will: Data[setdiff(predictors_relevant, c('Predictor03'))].

Stefan Seltmann
Dein Ansprechpartner
Stefan Seltmann
Lead Expert
Stefan liebt das Programmieren, vor allem rund um Data Engineering und Data Science, und arbeitet quasi in seinem Hobby. Gerade für Softwareentwicklung mit Python und/oder Spark punktet er als b.telligents Telefonjoker.
#CodeFirst, #TestMore, #CodeDoctor