Daten aus Schleifen nicht verlieren
Verfasst: Mi Okt 12, 2016 3:46 pm
Hallo!
Im Forum tauchen regelmäßig Fragen auf, denen der hier beschriebene Fehler zugrunde liegt. Wenn Du hierher verlinkt worden bist, dann ist Dir das wahrscheinlich auch passiert.
Oft werden in Schleifen viele Werte berechnet, die einzelnen Ergebnisse aber nirgends gespeichert. So steht man dann am Ende der Schleife mit nur einem Ergebnis da und fragt, sich wo sind denn die anderen hin.
Einfaches Beispiel:
führt zu
Hier werden 5 Namen in eine Funktion nachmen toupper gesteckt und der Programmierer erwartet 5 Antworten. Er erhält aber nur die Antwort auf den letzten Namen. Alle fünf Antworten wurden erstellt und der Reihe nach in output gesammelt - aber die alten Ergebnisse wurden immer wieder überschrieben.
Dazu gibt es verschiedene Möglichkeiten
Hier wird die Variable output erst einmal leer erstellt, und dann immer wieder mit c() etwas an sie herangehängt.
Hat man komplexere Rückgabewerte, die nicht in einen Vektor passen, kann man sie z. B. in eine Liste packen:
oder man kann nach und nach einen dataframe aufbauen:
Solch wiederholtes Anhängen ist nicht unbedingt schnell und wenn man das sehr, sehr oft macht, dann merkt man das. Im folgenden Beispiel wird in drei Funktionen ein Vector von 1 bis 1000 gefüllt. Das ist natürlich ein hirnrissiges Beispiel, aber es soll ja nur lehren. Wir füllen den Vektor einmal mit c(), einmal mit dem eckige-Klammer-Operator und wir definieren a einmal als numeric(0) und einmal als numeric(n).
Die Unterschiede in den Laufzeiten sind gewaltig:
8 Millisekunden statt 130, das wird schnell bedeutsam, wenn man es oft macht. Hier gilt es also, erstmals einen funktionierenden und später dann daraus einen schnellen Weg zu finden.
Oftmals ist es einfacher und lesbarer, mit Funktionen höherer Ordnung, in R vor allem mit der apply-Familie, zu rechnen. Den Krimskrams mit dem Speicherplatz anmelden und Daten in eine brauchbare Form abspeichern erledigt dann die Funktion höherer Ordnung. Das erste Beispiel in diesem Thread könnte man auch viel kürzer schreiben als
oder, wenn man eine Liste als Ergebnis möchte:
Ich hoffe, das hat Euch geholfen, sonst fragt einfach im Forum mit einem einfachen und reproduzierbaren Beispiel nochmal nach.
LG,
Bernhard
Im Forum tauchen regelmäßig Fragen auf, denen der hier beschriebene Fehler zugrunde liegt. Wenn Du hierher verlinkt worden bist, dann ist Dir das wahrscheinlich auch passiert.
Oft werden in Schleifen viele Werte berechnet, die einzelnen Ergebnisse aber nirgends gespeichert. So steht man dann am Ende der Schleife mit nur einem Ergebnis da und fragt, sich wo sind denn die anderen hin.
Einfaches Beispiel:
Code: Alles auswählen
input <- c("Anton", "Berta", "Cecilie", "Danton", "Erwin")
for(name in input){
output <- toupper(name)
}
print(output)
Code: Alles auswählen
> print(output)
[1] "ERWIN"
Dazu gibt es verschiedene Möglichkeiten
Code: Alles auswählen
input <- c("Anton", "Berta", "Cecilie", "Danton", "Erwin")
output <- character(0)
for(name in input){
output <- c(output, toupper(name))
}
print(output)
Hat man komplexere Rückgabewerte, die nicht in einen Vektor passen, kann man sie z. B. in eine Liste packen:
Code: Alles auswählen
input <- c("Anton", "Berta", "Cecilie", "Danton", "Erwin")
output <- list(0)
for(name in input){
output <- append(output, toupper(name))
}
print(output)
Code: Alles auswählen
input <- c("Anton", "Berta", "Cecilie", "Danton", "Erwin")
output <- data.frame(name = character(0), len=numeric(0))
for(name in input){
output <- rbind(output, data.frame(toupper(name), nchar(name)))
}
print(output)
Code: Alles auswählen
library(microbenchmark)
n = 10000
eins <- function(n){
a = numeric(0)
for (i in 1:n){
a <- c(a, i)
}
return(a)
}
zwei <- function(n){
a <- numeric(0)
for (i in 1:n){
a[i] <- i
}
return(a)
}
drei <- function(n){
a <- numeric(n)
for (i in 1:n){
a[i] <- i
}
return(a)
}
microbenchmark(eins(n), zwei(n), drei(n), times=100)
Code: Alles auswählen
> microbenchmark(eins(n), zwei(n), drei(n), times=100)
Unit: milliseconds
expr min lq mean median uq max neval
eins(n) 117.614498 123.784574 133.063466 126.265748 129.377602 210.845655 100
zwei(n) 71.479461 75.443645 86.921573 79.417595 82.666511 172.581713 100
drei(n) 7.254649 7.624615 7.951149 7.880363 8.155811 9.487689 100
Oftmals ist es einfacher und lesbarer, mit Funktionen höherer Ordnung, in R vor allem mit der apply-Familie, zu rechnen. Den Krimskrams mit dem Speicherplatz anmelden und Daten in eine brauchbare Form abspeichern erledigt dann die Funktion höherer Ordnung. Das erste Beispiel in diesem Thread könnte man auch viel kürzer schreiben als
Code: Alles auswählen
sapply(input, toupper)
Code: Alles auswählen
Map(toupper, input)
LG,
Bernhard