Hej Leute,

hier geht es um die Vorteile funktionaler Programmierung, denn diese sind so groß, dass jeder R-Programmierer diese kennen sollte. Und keine Angst, das ist gar nicht so schwer.

 

Was ist denn funktionale Programmierung?

Fangen wir mit der iterativen Programmierung an, also dem schrittweise Vorgehen. Damit sind im Wesentlichen Schleifen (for, while) gemeint. Diese sind – vor allem in R – nicht sonderlich effizient, weil jeder Schritt eben nacheinander abgearbeitet werden muss, und das dauern dann bei großen Datensätzen ziemlich lang.

Hier ein ganz einfaches Beispiel, welches zu jedem Element eines Vektors eins addiert.

x = rnorm(1000)
for (i in 1:length(x)) x[i] <- x[i] + 1

 

Ok, die kürzere und viel schnellere Variante kennt ihr natürlich alle:

x <- x + 1

Und das ist schon simples funktionales Vorgehen. Prinzipiell gibt es Funktionen, die eine Funktion auf alle Elemente eines Vektors bzw. Zeilen oder Spalten eines Data.frames  anwenden, was hochgradig parallelisierbar und dementsprechend schnell ist. Natürlich gibt es Situationen, da kommt man um Schleifen nicht herum, aber man kann erstaunlich viele vermeiden.

 

lapply, sapply, vapply

R selber bietet für die funktionale Programmierung die apply-Funktionen:

  • lapply(x, funktion) wendet die angegebene Funktion auf x an und gibt eine Liste zurück
  • sapply (x, funktion) macht das gleiche, gibt aber einen Vektor oder eine Matrix zurück
  • vapply (x, funktion, typ) macht ebenfalls das gleiche, gibt aber den Vektor oder Matrix von dem angegebenen Typ zurück.
  • tapply(x, y, funktion) gruppiert x nach den Ausprägungen von y und wendet auf jede Gruppe die angegebene Funktion an

 

Was können wir damit alles anstellen? Hier ein paar Beispiele

# berechnet den Mittelwert pro Spalte (wie colMeans)
sapply(iris[,-5], mean)
 
# berechnet die Differenz zwischen Maximum und Minimum pro Spalte
vapply(iris[,-5], function(x) max(x) - min(x), double(1))
 
# gruppiert den Datensatz mtcars nach cyl (Anzahl Zylinder) und
# berechnet dann für jede Gruppe den Mittelwert von mpg (Miles per Gallon)
tapply(mtcars$mpg, mtcars$cyl, mean)

 

Und zum Schluss noch der Tipp, vapply zu verwenden, sofern möglich, da der Rückgabetyp klar definiert ist, wie folgendes Beispiel zeigt:

#gibt eine Liste zurück
lapply(1:5, identity)
#gibt einen Integer-Vektor zurück
sapply(1:5, identity)
#gibt eine leere Liste zurück
sapply(integer(), identity)
#gibt einen Integer-Vektor zurück
vapply(1:5, identity, integer(1))
#gibt einen leeren Integer-Vektor zurück
vapply(integer(), identity, integer(1))

Noch etwas eleganter geht es mit dem Package purrr aus dem tidyverse. Dazu aber ein andernmal mehr.
 
Happy coding,

Euer Holger