Supposons que vous ayez un cadre de données avec plusieurs lignes et plusieurs colonnes.
Les colonnes ont des noms. Vous souhaitez accéder aux lignes par numéro et aux colonnes par nom.
Par exemple, un moyen (éventuellement lent) de parcourir les lignes est
for (i in 1:nrow(df)) {
print(df[i, "column1"])
# do more things with the data frame...
}
Une autre méthode consiste à créer des "listes" pour des colonnes séparées (telles que column1_list = df[["column1"]
) et à accéder aux listes en une boucle. Cette approche peut être rapide, mais également peu pratique si vous souhaitez accéder à plusieurs colonnes.
Existe-t-il un moyen rapide de boucler sur les lignes d'un bloc de données? Une autre structure de données est-elle préférable pour la mise en boucle rapide?
Je pense que je dois en faire une réponse complète car je trouve les commentaires plus difficiles à suivre et j'ai déjà perdu un commentaire à ce sujet ... Il existe un exemple de nullglob qui montre les différences entre les fonctions for et applique beaucoup mieux que d'autres exemples. Lorsque la fonction est très lente, toute la vitesse est consommée et vous ne trouverez pas de différences entre les variations de boucle. Mais lorsque vous rendez la fonction triviale, vous pouvez voir à quel point la boucle influence les choses.
J'aimerais également ajouter que certains membres de la famille apply inexplorés dans d'autres exemples ont des propriétés de performance intéressantes. Premièrement, je vais montrer les réplications des résultats relatifs de nullglob sur ma machine.
n <- 1e6
system.time(for(i in 1:n) sinI[i] <- sin(i))
user system elapsed
5.721 0.028 5.712
lapply runs much faster for the same result
system.time(sinI <- lapply(1:n,sin))
user system elapsed
1.353 0.012 1.361
Il a également trouvé sapply beaucoup plus lent. Voici quelques autres qui n'ont pas été testés.
Plain old s'appliquent à une version matricielle des données ...
mat <- matrix(1:n,ncol =1),1,sin)
system.time(sinI <- apply(mat,1,sin))
user system elapsed
8.478 0.116 8.531
Ainsi, la commande apply () elle-même est sensiblement plus lente que la boucle for. (La boucle for n'est pas ralentie sensiblement si j'utilise péché (mat [i, 1]).
Un autre qui ne semble pas avoir été testé dans d'autres publications est tapply.
system.time(sinI <- tapply(1:n, 1:n, sin))
user system elapsed
12.908 0.266 13.589
Bien sûr, on n’utiliserait jamais Tapply de cette façon et son utilité dépasse de loin un tel problème de vitesse dans la plupart des cas.
Le moyen le plus rapide consiste à not loop (opérations vectorisées). Une des seules instances dans lesquelles vous devez boucler est lorsqu'il y a des dépendances (c'est-à-dire qu'une itération dépend d'une autre). Sinon, essayez de faire autant de calculs vectorisés que possible en dehors de la boucle.
Si vous do devez boucler, utiliser une boucle for
est essentiellement aussi rapide que tout le reste (lapply
peut être un peu plus rapide, mais les autres fonctions apply
ont tendance à être à peu près à la même vitesse que for
).
En exploitant le fait que les data.frames sont essentiellement des listes de vecteurs de colonnes, on peut utiliser do.call pour appliquer une fonction avec l’arité du nombre de colonnes sur chaque colonne du data.frame (similaire à un "zipping" sur une liste). dans d'autres langues).
do.call(paste, data.frame(x=c(1,2), z=c("a","b"), z=c(5,6)))