3 votes

Comment lier efficacement les lignes d'un data.frame à un autre data.frame dans une boucle ?

Je sais que la façon la plus efficace d'ajouter des données à un vecteur ou à une liste dans une boucle est de le faire de la manière suivante pré-affecter ce vecteur/liste, avant de l'affecter à ses indices.

Question :

Quelle est la méthode la plus efficace en termes de mémoire (et de temps) pour rbinding ? data.frames les uns aux autres en boucle ? (Note : en fin de compte, je cherche la manière la plus efficace et la plus sensée de lier un grand nombre de data.frames de grande taille dans une boucle)

Ce que je sais jusqu'à présent :

Nous pourrions utiliser la norme rbind exemple :

output <- data.frame(a=c(), b=c())

for(i in 1:1000) { 
  temp <- data.frame(a=c(i), b=c(i))
  output <- rbind(output, temp)
}

Ou bind_rows() : (qui devrait être plus rapide )

library(dplyr)
output <- data.frame(a=c(), b=c())

for(i in 1:1000) { 
  temp <- data.frame(a=c(i), b=c(i))
  output <- bind_rows(output, temp)
}

Je ne sais pas si l'une d'entre elles est nettement plus efficace (par exemple pour les opérations de longue durée ou de grande envergure), ni s'il existe d'autres solutions ou meilleures pratiques plus efficaces.

2voto

Cole Points 9745

Comme l'a suggéré l'OP dans les commentaires, la meilleure chose à faire est de dresser une grande liste et de tout lier à la fin. Cela permet d'utiliser lapply() au lieu d'une boucle explicite suivie par do.call(rbind, tmp) :

n = 1000
tmp = lapply(seq_len(n), function(i) data.frame(a = i, b = i))
output = do.call(rbind, tmp)
## or 
output = dplyr::bind_rows(tmp)
## or
output = data.table::rbindlist(tmp)

Maintenant, si nous ciblons cet exemple particulier tout en exigeant une boucle, nous pouvons également utiliser quelques alternatives. Par exemple, au lieu de faire croître une liste d'images de données, nous savons que chaque itération aboutira à un entier. Par conséquent, nous pourrions simplement pré-allouer des vecteurs d'entiers, ce qui est également facile à traduire en rcpp :

n = 1000L
a = b = integer(n)
for (i in seq_len(n)) {
  a[i] = b[i] = i
}
data.frame(a = a, b = b)

## or with Rcpp:
rcpp_new_loop = Rcpp::cppFunction(code = 
'DataFrame rcpp_new_loop(int n) {
   IntegerVector a(n);
   IntegerVector b(n);

   for (int i = 0; i < n; i++) {
     a(i) = b(i) = i + 1;
   }
   return(DataFrame::create(Named("a") = a, _["b"] = b));
 } 
')

De même, il y a beaucoup de frais généraux pour les data.frame appels. dplyr::bind_rows() y data.table::rbindlist() par défaut data.frame type de résultats pour lists :

tmp = lapply(seq_len(n), function(i) list(a = i, b = i))

##data.table
output = rbindlist(tmp)
setDF(output)

##dplyr
output = bind_rows(tmp)
as.data.frame(output)

Performance : Rcpp est sans surprise l'approche la plus rapide. Mais l'utilisation de data.table::rbindlist ou dplyr::bind_rows avec une liste est une approche assez simple.

### n = 1,000 
# A tibble: 9 x 13
  expression            min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time
  <bch:expr>       <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm>
1 OP               378.18ms 379.92ms      2.63    15.7MB     2.63     2     2      760ms
2 do_call          254.76ms 254.89ms      3.92   220.7KB     5.88     2     3      510ms
3 bind_rows_df     196.69ms 202.48ms      4.94    16.9KB     3.29     3     2      607ms
4 dt_df            179.41ms 184.76ms      4.52    32.8KB     3.01     3     2      664ms
5 bind_rows_list     2.74ms   2.81ms    321.      16.9KB     3.98   161     2      502ms
6 new_loop           2.56ms   2.63ms    342.      17.6KB     4.00   171     2      500ms
7 dt_list            1.33ms   1.35ms    525.      32.8KB     3.99   263     2      501ms
8 new_loop_fx(n)    270.2us  280.5us   2188.      11.8KB     4.00  1094     2      500ms
9 rcpp_new_loop(n)  217.4us  228.3us   3872.      10.4KB     4.00  1936     2      500ms

### n = 10,000
# A tibble: 9 x 13
  expression            min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time
  <bch:expr>       <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm>
1 OP                  5.69s    5.69s     0.176    1.51GB     5.80     1    33      5.69s
2 do_call             2.67s    2.67s     0.374     2.2MB     3.74     1    10      2.67s
3 bind_rows_df        1.92s    1.92s     0.520  157.52KB     4.16     1     8      1.92s
4 dt_df               2.25s    2.25s     0.444  243.77KB     4.44     1    10      2.25s
5 bind_rows_list    30.73ms  34.57ms    28.5    157.75KB     3.81    15     2   525.49ms
6 new_loop           3.64ms   3.79ms   238.     123.07KB     3.99   119     2   500.85ms
7 dt_list           14.68ms  17.98ms    49.8    243.77KB     5.98    25     3      502ms
8 new_loop_fx(n)      1.2ms   1.24ms   691.     117.28KB     7.99   346     4   500.55ms
9 rcpp_new_loop(n)  299.5us  313.3us  2818.      80.66KB     4.00  1409     2   499.96ms

##code to reproduce::
library(data.table)
library(dplyr)

n = 1000L

new_loop_fx = function(n){
  a = b = integer(n)
  for (i in seq_len(n)) {
    a[i] = b[i] = i
  }
  data.frame(a = a, b = b)
}

rcpp_new_loop = Rcpp::cppFunction(code = 
'DataFrame rcpp_new_loop(int n) {
   IntegerVector a(n);
   IntegerVector b(n);

   for (int i = 0; i < n; i++) {
     a(i) = b(i) = i + 1;
   }
   return(DataFrame::create(Named("a") = a, _["b"] = b));
 } 
')

bench::mark(
  OP = {
    output <- data.frame(a=c(), b=c())

    for(i in seq_len(n)) { 
      temp <- data.frame(a=i, b=i)
      output <- rbind(output, temp)
    }
    output
  }
  ,
  do_call = {
    tmp = lapply(seq_len(n), function(i) data.frame(a = i, b = i))
    output = do.call(rbind, tmp)
  }
  , 
  bind_rows_df = {
    tmp = lapply(seq_len(n), function(i) data.frame(a = i, b = i))
    output = bind_rows(tmp)
    as.data.frame(output)
  }
  ,
  dt_df = {
    tmp = lapply(seq_len(n), function(i) data.frame(a = i, b = i))
    output = rbindlist(tmp)
    setDF(output)
  }
  , 
  bind_rows_list = {
    tmp = lapply(seq_len(n), function(i) list(a = i, b = i))
    output = bind_rows(tmp)
    as.data.frame(output)
  }
  ,
  new_loop = {
    a = b = integer(n)
    for (i in seq_len(n)){
      a[i] = b[i] = i
    }
    data.frame(a = a, b = b)
  }
  ,
  dt_list = {
    tmp = lapply(seq_len(n), function(i) list(a = i, b = i))
    output = rbindlist(tmp)
    setDF(output)
  }

  ,
  new_loop_fx(n),
  rcpp_new_loop(n)
)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X