Le plus gros problème et la racine de l'inefficacité est l'indexation de data.frame, je veux dire toutes ces lignes où vous utilisez temp[,]
.
Essayez d'éviter cela autant que possible. J'ai pris ta fonction, change d'indexation et ici 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)
}
Comme vous pouvez le voir, je crée des vecteurs res
qui rassemblent les résultats. À la fin, je l'ajoute data.frame
et je n'ai pas besoin de jouer avec les noms. Alors, comment est-ce mieux?
Je lance chaque fonction pour data.frame
avec nrow
de 1.000 à 10.000 par 1000 et la mesure du temps avecsystem.time
X <- as.data.frame(matrix(sample(1:10, n*9, TRUE), n, 9))
system.time(dayloop2(X))
Le résultat est
Vous pouvez voir que votre version dépend de façon exponentielle de nrow(X)
. La version modifiée a une relation linéaire, et le lm
modèle simple prévoit que pour 850 000 lignes, le calcul prend 6 minutes et 10 secondes.
Puissance de vectorisation
Comme Shane et Calimo le déclarent dans leurs réponses, la vectorisation est la clé d'une meilleure performance. À partir de votre code, vous pouvez sortir de la boucle:
- conditionnement
- initialisation des résultats (qui sont
temp[i,9]
)
Cela conduit à ce code
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)
}
Comparez le résultat pour ces fonctions, cette fois nrow
de 10 000 à 100 000 par 10 000.
Accorder l'écoute
Un autre ajustement consiste à changer dans une boucle l'indexation temp[i,9]
vers res[i]
(qui sont exactement les mêmes dans l'itération de boucle i-ème). C'est encore une différence entre l'indexation d'un vecteur et l'indexation d'un data.frame
.
Deuxième chose: lorsque vous regardez la boucle, vous pouvez voir qu'il n'est pas nécessaire de boucler sur tout i
, mais seulement pour celles qui correspondent à la condition.
Alors on y va
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)
}
Les performances que vous gagnez dépendent fortement d'une structure de données. Précisément - sur le pourcentage des TRUE
valeurs de la condition. Pour mes données simulées, il faut du temps de calcul pour 850 000 lignes en dessous d'une seconde.
Je veux que tu puisses aller plus loin, je vois au moins deux choses qui peuvent être faites:
- écrire un
C
code pour faire du sperme conditionnel
si vous savez que dans votre séquence de données max n'est pas grande, vous pouvez changer la boucle en while vectorisé, quelque chose comme
while (any(cond)) {
indx <- c(FALSE, cond[-1] & !cond[-n])
res[indx] <- res[indx] + res[which(indx)-1]
cond[indx] <- FALSE
}
Le code utilisé pour les simulations et les figures est disponible sur GitHub .