Daten aus Schleifen nicht verlieren

Wiederkehrende Fragen zum Forum

Moderator: EDi

Antworten
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Daten aus Schleifen nicht verlieren

Beitrag von bigben »

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:

Code: Alles auswählen

input <- c("Anton", "Berta", "Cecilie", "Danton", "Erwin")

for(name in input){
  output <- toupper(name)
}

print(output)
führt zu

Code: Alles auswählen

> print(output)
[1] "ERWIN"
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

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)
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:

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)
oder man kann nach und nach einen dataframe aufbauen:

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)
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).

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)
Die Unterschiede in den Laufzeiten sind gewaltig:

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
 
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

Code: Alles auswählen

sapply(input, toupper)
oder, wenn man eine Liste als Ergebnis möchte:

Code: Alles auswählen

Map(toupper, input)
Ich hoffe, das hat Euch geholfen, sonst fragt einfach im Forum mit einem einfachen und reproduzierbaren Beispiel nochmal nach.

LG,
Bernhard
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
Antworten