For-Loop

Mit Loops (Iterationen) kann man Operationen wiederholen. Die Wiederholungen geschehen dabei n-Mal, wobei n ein ganzzahliger Laufwert ist (integer). Für die Wiederholungen gibt man einen Startwert fest. Wie oben bereits erwähnt, wendet man dies z.B. an, wenn man eigene Rekodierungen schreiben will ohne bereits bestehende Funktionen verwenden zu wollen.

Wir können folgende Arten von Iterationen unterscheiden:

  1. for

  2. while

  3. repeat (do while)

Wir werden in dieser Einheit nur den for-Loop betrachten.

For-Loop

In einem for-Loop werden Operationen so lange durchgeführt, bis das Ende einer vordefinierten Sequenz erreicht ist (z.B. über alle Beobachtungen eines Datensatzes). Mit dieser Schleife setzen wir einen Beginn und ein Ende fest und die Operation werden innerhalb dieser Sequenz ausgeführt.

Im Schaubild ist die Logik des for-loops dargestellt.

for-loop
for-loop


Syntax

Die allgemeine Syntax sieht wie folgt aus:

for (i in 0:5) {
  print(i)
}

Die Struktur ist ähnlich zur if-Struktur:

  1. Wir initiieren die Schleife und setzen die gewünschte Sequenz in Klammern: for (i in 0:5)

  2. Anschließend setzen wir wieder in geschweiften Klammern die Anweisungen, die für jeden Durchgang ausgeführt werden sollen: { print(i) }

Die Sequenz beinhaltet einen sich ändernden Zählindex (i), der mit jedem Durchgang der Schleife steigt und dann die Sequenzangabe (in 0:n). Im Beispiel soll die Schleife also sechsmal durchgeführt werden, von 0 bis 5. i beginnt mit dem Wert 0 und sobald der Wert 6 erreicht, wird die Schleife beendet. Wichtig: In for-Schleifen steigt i iterativ immer um 1.

Man kann alternativ Objekte als Beginn oder Ende angeben. Dafür muss man diese zuvor deklarieren und kann diese anschließend nutzen. Zuerst schaffen wir mit n <-5 einen Zielwert, den wir dann über das Objekt in der Schleife aufgreifen.

# Beispiel Zielwert
n <- 5

for (i in 0:n) {
  print(i)
}

# Beispiel Startwert
start <- 3

for (i in start:n) {
  print(i)
}

Beispiel For-Loop

Wir schaffen eine einfache for-loop und geben dabei immer den Wert eines Vektors teacher aus, der die:den Dozentin:en für einen Kurs beinhaltet. Insgesamt haben wir \(5\) Dozent:innen. Im Beispiel bestimmen wir das Ende der Schleife einfach über die Länge des Vektors, den wir nutzen. Denn wir möchten, dass alle Fälle durchgegangen werden. Wir geben nacheinander die Namen der Dozent:innen aus:

teacher <- c("Baecker", 
             "Mueller",
             "Kaufmann",
             "Bauer",
             "Schuster"
             )

for (i in 1:length(teacher)) {
  print(paste0("Dozent:in ", 
               i, 
               " ist ",
               teacher[i]
               )
        )
}

Wir bedienen uns hierbei der Funktion paste0(), die Text mit Objekten als Ausgabe verbinden kann. Die Funktion paste0() hat zwischen den Elementen in der Funktion keine Leerstelle (die Funktion paste() setzt zwischen allen Elementen ein Leerzeichen (oder ein im Argument sep angegebenes Trennzeichen.)

For-Loop mit Else-If-Ausdruck

Nehmen wir das Beispiel aus den if-Ausdrücken. Wir möchten nun eine neue Variable im Datensatz schaffen, die eine Wertebeschreibung nutzt. Wir benötigen also die else if-Bedingung aus dem vorherigen Kapitel und bilden nun um diese einen for-loop durch die Länge des Datensatzes. Das Ergebnis speichern wir in einer neuen Variable motText und lassen es für jeden Fall einzeln ausgeben (print()). Zur Anschaulichkeit gehen wir hier zwei Schritte: Wir speichern erst die Beschreibung in motivation und übertragen es dann an den Wert in statistics$motText. Wichtig ist, dass wir nicht den Laufindex bei statistics$motText vergessen, da wir ja nur den spezifischen Fall mit dem neuen Wert belegen möchten.

for (i in 1:length(statistics$mot)) {
  if (statistics$mot[i] >= 7) {
    motivation <- "sehr motiviert"
    statistics$motText[i] <-  motivation
  } else if (statistics$mot[i] >= 4 & statistics$mot[i] < 7) {
    motivation <- "motiviert"
    statistics$motText[i] <-  motivation
  } else if (statistics$mot[i] >= 0 & statistics$mot[i] < 4) {
    motivation <- "nicht motiviert"
    statistics$motText[i] <-  motivation
  }
  
  print(paste0("Student:in ", 
               i, 
               " ist ",
               motivation
               )
        )
}

Eine kleine Aufgabe: Wie oben genannt, haben wir unnötig viele Zeilen hier geschrieben. Probiere einmal die Schleife zu verschlanken!

For-Loop mit Next

Als zweites Beispiel möchten wir nun eine neue Variable schaffen, die Beschreibungen von grade2 in Abhängigkeit des Notenwertes inkludiert. Zur Erinnerung: Die Variable grade2 weist NA's auf. Dies ist nur ein Funktionsbeispiel für die Kombination von for-loops und if-Ausdrücken. Es empfiehlt sich zum Rekodieren die Funktionen aus dem Paket dplyr (case_when()) oder car (recode()) zu nutzen.

table(statistics$grade2, 
      useNA = "ifany"
      )

In der for-loop möchten wir nur Beschriftungen in einer neuen Variable einfügen, sofern kein NA vorliegt. Dies können wir über einen else if-Ausdruck steuern.

Im Beispiel sieht man, dass wir zwei if-Ausdrücke genutzt haben. Zuerst prüfen wir, ob die Beobachtung NA in grade2 aufweist. Die Funktion is.na() liefert einen logischen Wert (TRUE, FALSE). Trifft das zu (TRUE), weisen wir NA auf der neuen Variable zu und springen mit dem Befehl next zum nächsten Fall (Schleife beginnt mit nächstem i von vorne). Mit next überspringen wir die nächsten Anweisungen und die Schleife beginnt mit der nächsten Iteration wieder von vorne. Wenn kein NA vorliegt, wird der zweite if-Ausdruck durchgeprüft, in dem wir mit mehreren else if-Ausdrücken die Textbeschriftungen zuweisen.

for (i in 1:length(statistics$grade2)) {
  if (is.na(statistics$grade2[i])) {
    statistics$g2text[i] <- NA
    next
  } 
  
  if (statistics$grade2[i] >= 13) {
    statistics$g2text[i] <- "sehr gut"
  } else if (statistics$grade2[i] >= 10 & statistics$grade2[i] < 13) {
    statistics$g2text[i] <- "gut"
  } else if (statistics$grade2[i] >= 7 & statistics$grade2[i] < 10) {
    statistics$g2text[i] <- "befriedigend"
  } else if (statistics$grade2[i] >= 5 & statistics$grade2[i] < 7) {
    statistics$g2text[i] <- "ausreichend"
  } else {
    statistics$g2text[i] <- "nicht bestanden"
  }
}

table(statistics$g2text)

Eine kleine Aufgabe: Anstatt next zu verwenden, könnte man hier auch mit nur mit if-Ausdrücken zum selben Ergebnis kommen. Schreibe die Schleife so um, dass diese ohne next auskommt.


Vor- & Nachteile

for-loops werden recht häufig genutzt und können auch verschachtelt werden. Nicht angewendet werden können for-loops, wenn die Anzahl der Durchläufe nicht bestimmbar ist (\(\rightarrow\) while-loop). Ein Nachteil von for-loops ist, dass diese in der Regel sehr langsam sind. Auch ist es in verschachtelten for-loops oft nur schwer nachvollziehbar, was genau gemacht wird. Das heißt auch, dass die Fehlersuche manchmal mühsam sein kann.

Als Alternativen zu for-loops kann man die apply()-Funktionen nutzen (Funktionalität ist durch Entwickler geprüft). Nähere Informationen zu den apply()-Funktionen finden sich hier. Dennoch können in manchen Situationen eigene geschrieben Funktionen auf for-loops zurückgreifen, so dass es sinnvoll ist, diese Grundfunktionen auch zu kennen.