Największym problemem i źródłem nieefektywności jest indeksowanie danych. Ramka, mam na myśli wszystkie te linie, w których używasz temp[,]
.
Staraj się unikać tego tak bardzo, jak to możliwe. Wziąłem twoją funkcję, zmień indeksowanie i tutaj wersja_A
dayloop2_A <- function(temp){
res <- numeric(nrow(temp))
for (i in 1:nrow(temp)){
res[i] <- i
if (i > 1) {
if ((temp[i,6] == temp[i-1,6]) & (temp[i,3] == temp[i-1,3])) {
res[i] <- temp[i,9] + res[i-1]
} else {
res[i] <- temp[i,9]
}
} else {
res[i] <- temp[i,9]
}
}
temp$`Kumm.` <- res
return(temp)
}
Jak widać, tworzę wektor, res
który zbiera wyniki. Na koniec dodaję go data.frame
i nie muszę się bawić z imionami. Jak to jest lepsze?
Uruchomić każdy funkcję data.frame
z nrow
od 1,000 do 10,000 i 1000 przez pomiar czasu zsystem.time
X <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
system.time(dayloop2(X))
Wynik jest
Możesz zobaczyć, że twoja wersja zależy wykładniczo nrow(X)
. Zmodyfikowana wersja ma zależność liniową, a prosty lm
model przewiduje, że dla 850 000 wierszy obliczenie zajmuje 6 minut i 10 sekund.
Moc wektoryzacji
Jak podają Shane i Calimo w odpowiedzi, wektoryzacja jest kluczem do lepszej wydajności. Z kodu możesz wyjść poza pętlę:
- kondycjonowanie
- inicjalizacja wyników (które są
temp[i,9]
)
To prowadzi do tego kodu
dayloop2_B <- function(temp){
cond <- c(FALSE, (temp[-nrow(temp),6] == temp[-1,6]) & (temp[-nrow(temp),3] == temp[-1,3]))
res <- temp[,9]
for (i in 1:nrow(temp)) {
if (cond[i]) res[i] <- temp[i,9] + res[i-1]
}
temp$`Kumm.` <- res
return(temp)
}
Porównaj wyniki dla tych funkcji, tym razem nrow
od 10 000 do 100 000 na 10 000.
Tuning dostrojony
Kolejną poprawką jest zmiana indeksowania pętli temp[i,9]
na res[i]
(które są dokładnie takie same w iteracji i-tej pętli). To znowu różnica między indeksowaniem wektora a indeksowaniem a data.frame
.
Po drugie: kiedy spojrzysz na pętlę, zobaczysz, że nie ma potrzeby zapętlania wszystkich i
, ale tylko tych, które pasują do warunków.
Więc zaczynamy
dayloop2_D <- function(temp){
cond <- c(FALSE, (temp[-nrow(temp),6] == temp[-1,6]) & (temp[-nrow(temp),3] == temp[-1,3]))
res <- temp[,9]
for (i in (1:nrow(temp))[cond]) {
res[i] <- res[i] + res[i-1]
}
temp$`Kumm.` <- res
return(temp)
}
Wydajność, którą zyskujesz, zależy od struktury danych. Dokładnie - na procent TRUE
wartości w stanie. W przypadku moich danych symulowanych zajmuje to czas obliczeń dla 850 000 wierszy poniżej jednej sekundy.
Chcę, żebyś mógł pójść dalej, widzę co najmniej dwie rzeczy, które można zrobić:
- napisz
C
kod do zrobienia warunkowego sumowania
jeśli wiesz, że w twojej sekwencji maks. sekwencja nie jest duża, możesz zmienić pętlę na wektoryzowaną podczas, coś w tym rodzaju
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
Kod używany do symulacji i liczb jest dostępny na GitHub .