web-dev-qa-db-fra.com

Dans R, comment passez-vous rapidement sur les lignes d'un cadre de données?

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?

28
Winston C. Yang

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.

14
John

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 ). 

12
Shane

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)))
0
Mark B.