R-Programming: Einsteiger-Tutorial

Die R Programming Language ist ein beliebte Statistik-Programmiersprache, die vor allem in Wissenschaft und Mathematik für Statistical Computing zum Einsatz kommt. R ist eine interessante Sprache mit ausgeprägten Eigenheiten. Hat man sich einmal daran gewöhnt, macht die Arbeit mit der Sprache durchaus Spaß.

Was zeichnet die „R Programming Language“ aus?

Bei R handelt es sich nicht um eine „General purpose“-Programmiersprache wie Java oder Python. Vielmehr bedient die Sprache den speziellen Einsatzbereich des Statistical Computings, zu Deutsch „statistisches Rechnen“. Auf diesem Gebiet hält R seit Jahren trotz starker Konkurrenz einen Platz unter den 20 beliebtesten Programmiersprachen.

Was R besonders macht, ist nicht nur die Sprache an sich, sondern das Gesamtpaket. So findet die R-Programmierung für gewöhnlich in einer interaktiven Umgebung statt, komplett mit Read-Eval-Print Loop (REPL) und integrierter Hilfe. Getragen wird die Open-Source-Sprache von einem weit entwickelten Ökosystem; die Community unterhält das Paket-Repository „The Comprehensive R Archive Network“ (CRAN). Auch Datensätze und wissenschaftliche Whitepaper zu neuen Ansätzen und Paketen werden kontinuierlich beigesteuert.

Zusammengenommen machen diese Eigenschaften R zur perfekten Programmierumgebung für Statistik und Datenwissenschaften. Gerade der interaktive Charakter der Umgebung lädt zum Forschen ein und ermöglicht ein spielerisches Erlernen der Sprache und der zugrunde liegenden Mathematik.

R ist eine Statistik- und Daten-Programmiersprache

R ist eine Statistik-Programmiersprache und kennt Konzepte wie die Normalverteilung, statistische Tests, Modelle und Regression. Neben R existiert eine Reihe vergleichbarer wissenschaftlicher Sprachen – außer dem kommerziellen Produkt Matlab ist hier insbesondere die jüngere Sprache Julia zu nennen. Als weiterer starker Konkurrent der vergangenen Jahre gilt Python.

Im Gegensatz zu Python hat R native Unterstützung für statistische Programmierung an Bord. Der herausragende Unterschied liegt darin begründet, wie die Sprache auf Werten operiert. Daten liegen normalerweise in der Mehrzahl vor, und so rechnet man in R für gewöhnlich mit mehreren Werten auf einmal. Während in fast allen anderen Sprachen der simpelste Wert eine einzelne Zahl ist, ist dies in R ein Spezialfall.

Veranschaulichen wir die Herangehensweise von R in Bezug auf Datenverarbeitung an einem simplen Beispiel. In jeder Programmiersprache lassen sich mathematische Operationen durchführen. Auch in R ist dies der Fall. Wir addieren zwei Zahlen:

# returns 15
10 + 5

So weit nichts Ungewöhnliches. Jedoch lässt sich in R dieselbe Additions-Operation auf eine Liste von Zahlen anwenden. Wir fassen zwei Zahlen zu einer Liste zusammen und addieren einen konstanten Wert:

# returns 15, 25
c(10, 20) + 5

Für gestandene Programmiererinnen und Programmierer ein überraschendes Ergebnis. Selbst eine moderne, dynamische Sprache wie Python erlaubt dies nicht:

# throws an error
[10, 20] + 5

In R lassen sich sogar zwei Listen addieren. Dabei werden nicht etwa die Listenelemente zu einer Liste zusammengefasst, sondern es wird für jedes Element die passende mathematische Operation durchgeführt:

# returns 42, 69
c(40, 60) + c(2, 9)

In älteren Sprachen wie Java oder C++ benötigt man eine Schleife, um mehrere Elemente einer Liste zu verarbeiten. Denn diese Sprachen trennen strikt zwischen einzelnen Werten, den Skalaren, und zusammengesetzten Datenstrukturen, den Vektoren. In R ist der Vektor die grundlegende Einheit; ein Skalar ist in der R-Programmierung als einelementiger Vektor ein Spezialfall.

An der Statistik ist besonders, dass die berühmte mathematische Präzision aufgeweicht ist. Man rechnet mit Ungewissheiten und der Realität entstammenden, imperfekten Daten. Dabei kann immer etwas schief gehen; glücklicherweise ist R in gewissem Maße fehlertolerant. Die Sprache kann mit fehlenden Werten umgehen, ohne dass diese ein laufendes Skript zum Absturz bringen.

Veranschaulichen wir uns die Robustheit der Sprache an einem Beispiel. Normalerweise lässt sich in jeglicher Programmiersprache ein Absturz provozieren, indem man eine Zahl durch Null dividiert. R lässt dies jedoch kalt; als Ergebnis der Division durch Null wird der Wert Inf vermerkt, der sich in einem späteren Bereinigungsschritt einfach aus den Daten filtern lässt:

# list of divisors, containing zero
divisors = c(2, 4, 0, 10)
# returns `c(50, 25, Inf, 10)`
quotients = 100 / divisors
# filter out Inf; returns `c(50, 25, 10)`
cleaned_quotients = quotients[quotients != Inf]

R unterstützt OOP und funktionale Programmierung

Die Programmierung mit R gestaltet sich ausgesprochen flexibel; die Sprache lässt sich nicht klar in die Hierarchie der Programmierparadigmen einordnen. Getragen wird sie von einem OOP-System, die üblichen Klassendefinitionen sucht man jedoch vergeblich. Im täglichen Gebrauch kommen in erster Linie funktionale und imperative Ansätze zum Einsatz. Gerade die funktionalen Features, die sich gut für die Datenverarbeitung eignen, sind stark ausgeprägt.

Ähnlich wie in JavaScript glänzt das Objektsystem durch seine Flexibilität. Mit Python vergleichbar sind die generischen Funktionen, die sich auf Objekte unterschiedlichen Typs anwenden lassen. So gibt es in der R-Programmierung die length()-Funktion, analog zu Pythons len().

Wie funktioniert die R-Programmierung?

In der R-Programmierung dreht sich alles um Daten, denn darauf fußt die Statistik. Um eine Problemlösung in R zu entwickeln, benötigt man einen Datensatz. Leider existiert dieser zum Zeitpunkt der Entwicklung oft noch nicht. So beginnt man ein R-Programming-Projekt oft damit, Daten zu simulieren. Wir schreiben den Code, testen die Funktionalität und tauschen die Testdaten später gegen echte Daten aus.

Wie wird R-Code ausgeführt?

Wie Ruby oder Python ist R eine dynamische, interpretierte Skriptsprache. Anders als in der Programmiersprache C gibt es in R also keine Trennung von Quelltext und ausführbarem Code. Die Entwicklung findet meist interaktiv statt, d. h. man füttert den Interpreter zeilenweise mit Quellcode, der sofort ausgeführt wird. Variablen werden bei Bedarf automatisch erzeugt, Namen zur Laufzeit gebunden.

Der Effekt dieser interaktiven und dynamischen Programmierung ist vergleichbar damit, sich innerhalb des laufenden Programms zu befinden. Bereits erzeugte Objekte lassen sich untersuchen und modifizieren, neue Ideen sofort testen. Der help-Befehl gibt Zugriff auf die Dokumentation von Syntax und Funktionen:

# view help for `for` syntax
help('for')
# view help for `c()` function
help(c)

Aus dem Interpreter heraus lassen sich Skriptdateien dynamisch laden. Der source-Befehl funktioniert wie das äquivalente Shell-Kommando. Beim Aufruf wird der Inhalt einer R-Quelltextdatei ausgelesen und in die laufende Session eingespeist:

source('path/to/file.r')

Wie sieht die Syntax der R-Programmiersprache aus?

Die Skriptsprache nutzt die aus C und Java bekannten geschweiften Klammern, um die Körper von Funktionen und Kontrollanweisungen abzugrenzen. Anders als in Python wirkt sich die Einrückung von Code nicht auf dessen Funktion aus. Kommentare beginnen wie in Ruby und Python mit einer Raute, am Ende einer Anweisung wird kein Semikolon benötigt.

Mit etwas Erfahrung erkennt man R-Code sofort, denn die Sprache bringt einige Eigenheiten mit. Neben dem Gleichheitszeichen als Zuweisungsoperator, kommen in der R-Programmierung zwei pfeilartige Operatoren für Zuweisungen zum Einsatz. So lässt sich die Richtung der Zuweisung umdrehen:

# equivalent assignments
age <- 42
'Jack' -> name
person = c(age, name)

Ein weiteres typisches Merkmal von R-Code ist eine Art Pseudo-Objekt-Notation nach dem Muster object.method():

# test if argument is a number
is.numeric(42)

Die Funktion is.numeric erscheint wie eine Methode numeric(), die einem Object namens is gehört. Jedoch ist dies nicht der Fall. In der R-Programmierung ist der Punkt ein reguläres Zeichen; die Funktion könnte statt is.numeric auch is_numeric heißen.

Um die in der R-Programmierung allgegenwärtigen Vektoren zu erzeugen, kommt die Konkatenations-Funktion c() zum Einsatz:

people.ages &lt;- c(42, 51, 69)

Wendet man die Funktion auf Vektoren an, werden diese zu einem zusammenhängenden Vektor zusammengefügt:

# yields `c(1, 2, 3, 4)`
c(c(1, 2), c(3, 4))

Anders als in den meisten Programmiersprachen beginnt in R die Indizierung von Elementen eines Vektors bei 1. Das ist zunächst gewöhnungsbedürftig, hilft jedoch, die gefürchteten Off-by-one-Fehler zu vermeiden. Der höchste Index eines Vektors entspricht der Länge des Vektors:

# create a vector of names
people <- c('Jack', 'Jim', 'John')
# access the first name
people[1] == 'Jack'
# access the last name
people[length(people)] == 'John'

Ähnlich wie in Python gibt es auch in der R-Programmierung das Konzept des Slicings. Mit einer Slice lässt sich ein Teilbereich eines Vektors indizieren. Dem liegen Sequenzen zugrunde, die in R nativ unterstützt werden. Wir erzeugen eine Sequenz von Zahlen und wählen einen Teil aus:

# create vector of numbers between 42 and 69
nums = seq(42, 69)
# equivalent assignment using sequence notation
nums = 42:69
# using a sequence, slice elements 3 through 7
sliced = nums[3:7]

Wie funktionieren Kontrollstrukturen in der R-Programmierung?

Die grundlegenden Operationen in der R-Programmierung sind für Vektoren definiert. So benötigt man oft keine Schleifen, sondern führt eine Operation direkt auf dem gesamten Vektor durch, wobei die einzelnen Elemente modifiziert werden. Wir quadrieren die ersten zehn positiven Zahlen ohne Schleife:

nums <- seq(10)
squares <- nums ** 2
squares[3] == 9

Beim Einsatz der R For-Loop gilt zu beachten, dass diese nicht so funktioniert wie in C, Java oder JavaScript. Ohne den Umweg über eine Schleifenvariable wird wie in Python direkt über den Elementen iteriert:

people = c('Jim', 'Jack', 'John')
for (person in people) {
  print(paste('Here comes', person, sep = ' '))
}

Selbstverständlich gibt es die If-else-Verzweigung in R als grundlegende Kontrollstruktur. Jedoch lässt sich auch diese in vielen Fällen durch Filter-Funktionen oder die logische Indizierung von Vektoren ersetzen. Wir erzeugen einen Vektor mit Altersangaben und filtern jeweils die über und unter 18-Jährigen in zwei Variablen, ohne eine Schleife oder Verzweigung zu benötigen:

# create 20 ages between 1 and 99
ages = as.integer(runif(20, 1, 99))
# filter adults
adults = ages[ages > 18]
# filter children
children = ages[ages < 18]
# make sure everyone is accounted for
length(adults) + length(children) == length(ages)

Der Vollständigkeit halber der äquivalente Ansatz mit Kontrollstrukturen:

# create 20 ages between 1 and 99
ages = as.integer(runif(20, 1, 99))
# start with empty vectors
adults = c()
children = c()
# populate vectors
for (age in ages) {
  if (age > 18) {
    adults = c(adults, age)
  }
  else {
    children = c(children, age)
  }
}

Was wird für den Einstieg in die R-Programmierung benötigt?

Um mit der R-Programmierung loszulegen, benötigt man lediglich eine lokale R-Installation. Es stehen Installer für alle großen Betriebssysteme zum Download bereit. Eine Standard-R-Installation umfasst einen GUI-Interpreter mit REPL, integrierter Hilfe und Editor. Für produktives Coden sollte man auf einen der etablierten Code-Editoren zurückgreifen. Mit R-Studio steht eine attraktive Alternative zu R-Umgebung bereit.

Für welche Projekte eignet sich R?

Besonders häufig findet die R-Programmierung Anwendung in Wissenschaft und Forschung, etwa in der Bioinformatik und beim maschinellen Lernen. Jedoch eignet sich die Sprache für alle Projekte, die statistische Modellierungen oder mathematische Modelle nutzen. Schwach ist R lediglich für reine Textverarbeitung; hier hat Python klar die Nase vorn.

Die üblichen Berechnungen und Visualisierungen in Tabellenkalkulationen lassen sich durch R-Code ersetzen. Dabei profitiert man von einer sauberen Trennung der Belange, denn Daten und Code werden nicht in Zellen vermischt. So lässt sich Code einmal schreiben und auf mehrere Datensätze anwenden. Ferner gibt es so keine Gefahr, die Formel einer Zelle bei manuellen Änderungen zu überschreiben.

Für wissenschaftliche Publikationen gilt R als Goldstandard. Die Trennung von Code und Daten ermöglicht erst die wissenschaftliche Reproduzierbarkeit. Das ausgereifte Ökosystem aus Tools und Paketen erlaubt die Erstellung effizienter Publikations-Pipelines. Aus Code und Daten werden automatisiert Auswertungen und Visualisierungen erzeugt und in hochqualitative LaTeX- oder RMarkdown-Dokumente eingebunden.

Tipp

Die perfekte Basis, für Ihre Website: Jetzt günstigen Webspace kaufen bei IONOS!