O maior problema e raiz da ineficácia é a indexação de data.frame, quero dizer todas essas linhas em que você usa temp[,]
.
Tente evitar isso o máximo possível. Peguei sua função, mudei de indexação e aqui version_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)
}
Como você pode ver, crio um vetor res
que reúne resultados. No final, eu o adiciono data.frame
e não preciso mexer em nomes. Então, como é melhor?
Eu corro cada função data.frame
com nrow
de 1.000 a 10.000 por 1.000 e meço o tempo comsystem.time
X <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
system.time(dayloop2(X))
O resultado é
Você pode ver que sua versão depende exponencialmente de nrow(X)
. A versão modificada possui relação linear e o lm
modelo simples prevê que, para 850.000 linhas, a computação leva 6 minutos e 10 segundos.
Poder da vetorização
Como Shane e Calimo declaram em suas respostas, a vetorização é a chave para um melhor desempenho. No seu código, você pode sair do loop:
- condicionamento
- inicialização dos resultados (que são
temp[i,9]
)
Isso leva a esse código
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)
}
Compare o resultado dessas funções, desta vez nrow
de 10.000 a 100.000 por 10.000.
Ajustando o sintonizado
Outro ajuste é a alteração de uma indexação de loop temp[i,9]
para res[i]
(que são exatamente iguais na i-ésima iteração de loop). Novamente, é uma diferença entre indexar um vetor e indexar a data.frame
.
Segunda coisa: quando você olha no loop, pode ver que não há necessidade de repetir tudo i
, mas apenas os que se encaixam na condição.
Aqui vamos nos
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)
}
O desempenho que você obtém altamente depende de uma estrutura de dados. Precisamente - em porcentagem de TRUE
valores na condição. Para meus dados simulados, leva tempo de computação para 850.000 linhas abaixo de um segundo.
Eu quero que você possa ir mais longe, vejo pelo menos duas coisas que podem ser feitas:
- escreva um
C
código para fazer cumsum condicional
se você sabe que na sequência máxima de dados não é grande, pode alterar o loop para vetorizado enquanto, algo como
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
O código usado para simulações e figuras está disponível no GitHub .