2017-02-13 6 views
0

Mir wurde gesagt, dass es keine Notwendigkeit, eine "für" Schleife in R überhaupt haben. Also, ich mag sehen, wie ich „für“ Schleife dieses Python-wie los in meinem R Code erhalten kann:Wie loswerden für diese Schleife

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 
    } 
+2

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

+7

Wer hat Ihnen gesagt, dass das falsch ist. Einige Operationen erfordern eine Schleife. –

+0

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

Antwort

1

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.

+0

Wow! Das ist fantastisch. Danke für die Einsicht. – whirlaway

+0

@whirlaway - hat das deine Frage beantwortet? –

+0

Ja, hat es getan. Und mehr. Vielen Dank. – whirlaway

0
yrdf <- data.frame(Adj.Close = rnorm(100)) 
numrows <- length(yrdf$Adj.Close) 
diff.vec <- c((yrdf$Adj.Close[1:(numrows-1)]/yrdf$Adj.Close[2:numrows] - 1) * 100, NA) 
0

Sie auch die lead Funktion aus dem dplyr Paket verwenden kann, um das Ergebnis zu erhalten das du willst.

library(dplyr) 
yrdf <- data.frame(Adj.Close = rnorm(100)) 
(yrdf$Adj.Close/lead(yrdf$Adj.Close)-1)*100 

Die Berechnung wurde von (a-b)/b nach a/b-1 vereinfacht. Dies ist eine vektorisierte Operation anstelle einer for-Schleife.