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 votre fonction, modifié l'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 un vecteur res
qui recueillent des résultats. À la fin, je l'ajoute à data.frame
et je n'ai pas besoin de m'embêter avec les noms. Alors, en quoi c'est mieux ?
J'exécute chaque fonction pour data.frame
avec nrow
de 1 000 à 10 000 par 1 000 et mesure le temps avec system.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 manière exponentielle de nrow(X)
. La version modifiée a une relation linéaire, et simple lm
prévoit que pour 850 000 lignes, le calcul prend 6 minutes et 10 secondes.
La puissance de la vectorisation
Comme Shane et Calimo le disent dans leurs réponses, la vectorisation est une clé pour de meilleures performances. A partir de votre code, vous pourriez vous déplacer en dehors 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 pour nrow
de 10.000 à 100.000 par 10.000.
Accorder l'accordéon
Une autre modification consiste à changer dans une boucle d'indexation temp[i,9]
à res[i]
(qui sont exactement les mêmes dans la i-ème itération de la boucle). C'est encore une fois la différence entre l'indexation d'un vecteur et l'indexation d'un objet data.frame
.
Deuxièmement, lorsque vous regardez la boucle, vous pouvez voir qu'il n'y a pas besoin de boucler sur tous les éléments de l'échantillon. i
mais seulement pour ceux qui sont en bonne condition.
Donc, nous y voilà
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 obtenez dépendent fortement d'une structure de données. Précisément - sur le pourcentage de TRUE
dans la condition. Pour mes données simulées, le temps de calcul pour 850 000 lignes est inférieur à une seconde.
Si vous voulez, vous pouvez aller plus loin, je vois au moins deux choses qui peuvent être faites :
- écrire un
C
code pour faire un cumul conditionnel
-
si vous savez que dans vos données la séquence maximale n'est pas grande, vous pouvez changer la boucle en un 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
}
0 votes
Envisagez d'ajouter quelque chose comme
if(i%%1000) {print(i)}
tout en testant votre fonction pour avoir une idée approximative du temps d'exécution.