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 é
![desempenho](https://imgur.com/pns7S.png)
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.
![desempenho](https://imgur.com/GCmKf.png)
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.
![desempenho](https://imgur.com/dSc8J.png)
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 .