Il problema maggiore e la radice dell'inefficacia è l'indicizzazione di data.frame, intendo tutte queste righe in cui si utilizza temp[,]
.
Cerca di evitarlo il più possibile. Ho preso la tua funzione, cambio indicizzazione e qui versione_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)
}
Come puoi vedere, creo un vettore res
che raccoglie risultati. Alla fine lo aggiungo a data.frame
e non ho bisogno di scherzare con i nomi. Quindi quanto è meglio?
Corro ogni funzione per data.frame
con nrow
da 1.000 a 10.000 da 1.000 e misurare il tempo consystem.time
X <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
system.time(dayloop2(X))
Il risultato è
Puoi vedere che la tua versione dipende in modo esponenziale nrow(X)
. La versione modificata ha una relazione lineare e il lm
modello semplice prevede che per 850.000 righe il calcolo richiede 6 minuti e 10 secondi.
Potenza di vettorializzazione
Come affermano Shane e Calimo nelle loro risposte, la vettorializzazione è la chiave per prestazioni migliori. Dal tuo codice potresti spostarti al di fuori del ciclo:
- condizionata
- inizializzazione dei risultati (che sono
temp[i,9]
)
Questo porta a questo codice
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)
}
Confronta i risultati per queste funzioni, questa volta nrow
da 10.000 a 100.000 per 10.000.
Sintonizzazione del sintonizzato
Un altro tweak è a cambiare in un'indicizzazione ciclo temp[i,9]
a res[i]
(che sono esattamente gli stessi in i-esima iterazione del ciclo). È di nuovo la differenza tra indicizzare un vettore e indicizzare a data.frame
.
Seconda cosa: quando guardi il loop puoi vedere che non è necessario eseguire il loop su tutto i
, ma solo per quelli che si adattano alle condizioni.
Quindi eccoci
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)
}
Le prestazioni ottenute dipendono fortemente da una struttura di dati. Precisamente - sulla percentuale di TRUE
valori nella condizione. Per i miei dati simulati ci vuole tempo di calcolo per 850.000 righe al di sotto del secondo.
Voglio che tu possa andare oltre, vedo almeno due cose che possono essere fatte:
- scrivere un
C
codice per fare un cumsum condizionale
se sai che nella sequenza massima dei tuoi dati non è grande, puoi cambiare loop in vettorializzato mentre, qualcosa del genere
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
Il codice utilizzato per simulazioni e figure è disponibile su GitHub .