2012-05-06 15 views
14

Wiem, że nie jest najlepszą praktyką w R używania pętli for, ponieważ nie ma ona lepszej wydajności. W prawie wszystkich przypadkach istnieje funkcja rodziny *apply, która rozwiązuje nasze problemy.Pętle w R - Musisz mimo wszystko użyć indeksu, aby uniknąć "za"?

Jednak mam do czynienia z sytuacją, w której nie widzę obejścia.

muszę obliczyć procent wariancji dla kolejnych wartości:

pv[1] <- 0 
for(i in 2:length(x)) { 
    pv[i] <- (x[i] - x[i-1])/x[i-1] 
} 

Więc, jak widać, muszę używać zarówno element x[i], ale także element x[i-1]. Korzystając z funkcji *apply, po prostu widzę, jak korzystać z x[i]. Czy mimo to mogę ominąć pętle for?

Odpowiedz

18

Co oferowany będzie zmienność ułamkową, ale jeśli pomnożyć przez 100, można uzyskać "odmianę procent":

pv<- vector("numeric",length(x)) 
pv[1] <- 0 
pv[-1] <- 100* (x[-1] - x[-length(x)])/ x[-length(x)] 

rozwiązanie wektorowy. (Powinieneś zauważyć, że pętle for będą równie powolne, jak * zastosowanie rozwiązań ... po prostu nie tak piękne.) Zawsze szukaj wektoryzacji.)

Aby wyjaśnić nieco więcej: x[-length(x)] jest wektor, x[1:(length{x-1)], a x[-1] jest wektorem, x[2:length(x)], a operacje wektorowe w R wykonują te same operacje, co w ciele pętli for, chociaż nie używają jawnej pętli. R najpierw konstruuje różnice w przesuniętych wektorach, , a następnie dzieli przez x[1:(length{x-1)].

+0

Dobra odpowiedź DWin. Nie wiedziałem dokładnie, co osiągnął plakat, ale jestem w 100% zgodna co do wektoryzacji.+1 –

+0

Bardzo ładna odpowiedź! Nie wiedziałem, że wektoryzacja jest najszybsza, pomyślałem "lapply". Ale w ostatnim wierszu kodu nie powinno być "x [-1] - x [-length (x)]"? –

+0

@ JoãoDaniel: Tak, powinno. Edytuj zastosowane. –

20

można uzyskać takie same wyniki z:

pv <- c(0) 
y <- sapply(2:length(x), function(i) {pv <<- (x[i] - x[i-1])/x[i-1]}) 
c(0, y) 

pętli FOR zagadnień, które kiedyś były problemem zostały zoptymalizowane. Często pętla for nie jest wolniejsza i może nawet być szybsza niż zastosowane rozwiązanie. Musisz przetestować oba i zobaczyć. Założę się, że twoja pętla for jest szybsza niż moje rozwiązanie.

EDIT: Żeby zilustrować pętli for vs. zastosować rozwiązanie, jak również co Dwin omawia o wektoryzacji wpadłem benchmarkingu na czterech rozwiązań wykorzystujących microbenchmark na Win 7 maszynę.

Unit: microseconds 
      expr  min  lq median  uq  max 
1 DIFF_Vincent 22.396 25.195 27.061 29.860 2073.848 
2  FOR.LOOP 132.037 137.168 139.968 144.634 56696.989 
3   SAPPLY 146.033 152.099 155.365 162.363 2321.590 
4 VECTORIZED_Dwin 18.196 20.063 21.463 23.328 536.075 

enter image description here

+0

Jaka jest wersja „DIF” i co robi cały testowy wygląda? Rozwiązanie z @VincentZoonekynd działa najszybciej dla mnie. – Tommy

+0

Powinien być DIFF dla diff (Vincent's). Aby być uczciwym w benchmarkingu, nie zapomnij wziąć * 100 z rozwiązania DWin, ponieważ to dodaje dodatkowe obliczenia, które są procentem (nie w proporcji takiej, jak rozwiązanie każdego elesa). –

+0

+1 za piękno wykresu! –

16

Można również użyć diff:

c(0, diff(x)/x[-length(x)]) 
c(0, exp(diff(log(x))) - 1) 
+0

+1 To wydaje się być najszybszym ... I podoba mi się wariant log/exp, choć nie jest tak szybki. – Tommy

+0

Końcówka kapelusza: Muszę przyznać, że podejście diff() jest nieco bardziej eleganckie niż moje dosłowne tłumaczenie na wektoryzowane rozwiązanie. Byłem zaskoczony, że nie udało się lepiej porównywać wyników. –

+0

@DWin - kiedy testuję, używając 'diff' lub nie ma znaczenia. Ale użycie 'c' zamiast wymiany jest znacznie szybsze. Coś jest podejrzanego w sprawie liczby Tylera. Uruchomiłem go na 'x <- runif (1e7)' ... – Tommy

Powiązane problemy