Meiner Erfahrung nach gibt es drei Gründe, eine for
Schleife zu vermeiden. Die erste ist, dass sie von anderen schwer zu lesen sein können (wenn Sie Ihren Code teilen), und die apply
Familie von Funktionen kann dies verbessern (und expliziter bei Rückgaben). Der zweite ist ein Geschwindigkeitsvorteil, der unter bestimmten Umständen möglich ist, insbesondere wenn Sie den Code parallel laufen lassen (z. B. die meisten apply
Funktionen sind peinlich parallel, während for
Schleifen viel mehr Arbeit benötigen, um auseinander zu brechen).
Es ist jedoch der dritte Grund, der Ihnen hier dient: vektorisierte Lösungen sind oft besser als alle der oben genannten, weil es wiederholte Anrufe vermeidet (zB Ihre c
am Ende der Schleife, die if
überprüfen, etc.) . Hier können Sie alles mit einem einzigen vektorisierten Aufruf erreichen.
Zunächst einige Beispieldaten
set.seed(8675309)
yrdf <- data.frame(Adj.Close = rnorm(5))
Dann multiplizieren wir alles durch 100
, nehmen Sie die diff
der benachbarten Einträge in Adj.Close
und vektorisiert Division verwenden, indem Sie den folgenden Eintrag zu teilen. Beachten Sie, dass ich mit NA
füllen muss, wenn (und nur wenn) das Ergebnis die gleiche Länge wie die Eingabe haben soll. Wenn Sie das NA
am Ende des Vektors nicht wollen/brauchen, kann es sogar einfacher sein.
100 * c(diff(yrdf$Adj.Close),NA)/c(yrdf$Adj.Close[2:nrow(yrdf)], NA)
Returns
[1] 238.06442 216.94975 130.41349 -90.47879 NA
Und, um explizit zu sein, hier ist der microbenchmark
Vergleich:
myForLoop <- function(){
numrows = nrow(yrdf)
diff.vec = c() # vector of differences
for (index in 1:nrow(yrdf)) { # yrdf is a data frame
if (index == numrows) {
diff = NA # because there is no entry "below" it
} else {
val_index = yrdf$Adj.Close[index]
val_next = yrdf$Adj.Close[index+1]
diff = val_index - val_next # diff between two adjacent values
diff = diff/yrdf$Adj.Close[index+1] * 100.0
}
diff.vec<-c(diff.vec,diff) # append to vector of differences
}
return(diff.vec)
}
microbenchmark::microbenchmark(
forLoop = myForLoop()
, vector = 100 * c(diff(yrdf$Adj.Close),NA)/c(yrdf$Adj.Close[2:nrow(yrdf)], NA)
)
gibt:
Unit: microseconds
expr min lq mean median uq max neval
forLoop 74.238 78.184 82.06786 81.287 84.3740 104.190 100
vector 20.193 21.718 23.91824 22.716 24.0535 80.754 100
Beachten Sie, dass die vector
Ansatz s etwa 30% der Zeit der for
Schleife. Dies wird umso wichtiger, als die Größe der Daten erhöht sich um:
set.seed(8675309)
yrdf <- data.frame(Adj.Close = rnorm(10000))
microbenchmark::microbenchmark(
forLoop = myForLoop()
, vector = 100 * c(diff(yrdf$Adj.Close),NA)/c(yrdf$Adj.Close[2:nrow(yrdf)], NA)
)
Unit: microseconds
expr min lq mean median uq max neval
forLoop 306883.977 315116.446 351183.7997 325211.743 361479.6835 545383.457 100
vector 176.704 194.948 326.6135 219.512 236.9685 4989.051 100
Hinweis gibt, dass der Unterschied darin, wie diese Skala ist massiven - der Vektor-Version dauert weniger als 0,1% die Zeit zu laufen. Dies ist wahrscheinlich der Fall, da jeder Aufruf von c
zum Hinzufügen des neuen Eintrags das erneute Lesen des vollständigen Vektors erfordert.Eine leichte Veränderung kann die for-Schleife nach oben ein bisschen, beschleunigt aber nicht den Weg zum Geschwindigkeitsvektor alle erhalten:
myForLoopAlt <- function(){
numrows = nrow(yrdf)
diff.vec = numeric(numrows) # vector of differences
for (index in 1:nrow(yrdf)) { # yrdf is a data frame
if (index == numrows) {
diff = NA # because there is no entry "below" it
} else {
val_index = yrdf$Adj.Close[index]
val_next = yrdf$Adj.Close[index+1]
diff = val_index - val_next # diff between two adjacent values
diff = diff/yrdf$Adj.Close[index+1] * 100.0
}
diff.vec[index] <- diff # append to vector of differences
}
return(diff.vec)
}
microbenchmark::microbenchmark(
forLoop = myForLoop()
, newLoop = myForLoopAlt()
, vector = 100 * c(diff(yrdf$Adj.Close),NA)/c(yrdf$Adj.Close[2:nrow(yrdf)], NA)
)
gibt
Unit: microseconds
expr min lq mean median uq max neval
forLoop 304751.250 315433.802 354605.5850 325944.9075 368584.2065 528732.259 100
newLoop 168014.142 179579.984 186882.7679 181843.7465 188654.5325 318431.949 100
vector 169.569 208.193 331.2579 219.9125 233.3115 2956.646 100
dass die Hälfte der Zeit vor dem for
Schleife Ansatz gespeichert , ist aber immer noch viel langsamer als die vektorisierte Lösung.
Es gibt eine 'diff'-Funktion, um die Differenz benachbarter Elemente in' R' zu erhalten. Überprüfen Sie auch die 'Lead'- und' Lag'-Funktionen in 'dplyr' – akrun
Wer hat Ihnen gesagt, dass das falsch ist. Einige Operationen erfordern eine Schleife. –
Es gibt Zeiten, in denen 'for-Schleifen' die bevorzugte Methode sind. In [post] (http://stackoverflow.com/a/6466415/4408538) finden Sie eine detaillierte Erklärung, wann eine 'for-Schleife 'implementiert werden soll. Sehen Sie sich diese Posts an, um die Schleifenkonstrukte von R besser zu verstehen: [post1] (http://stackoverflow.com/a/2276001/4408538) und [post2] (http://stackoverflow.com/q/28983292/4408538). –