Эффективный способ привязки строк data.frame к другому data.frame в al oop? - PullRequest
3 голосов
/ 26 марта 2020

Мне известно, что наиболее эффективный способ памяти для добавления вектора или списка в al oop - это предварительное выделение этого вектора / списка перед присвоением его индексам.

Вопрос:

Какой самый эффективный способ памяти (и времени) для привязки data.frames к одному другому в oop? (Примечание: в конечном итоге, я выбрал наиболее эффективный и разумный способ связывания большого количества больших фреймов данных в 10 * *

1011 * Что я знаю до сих пор:

Мы могли бы использовать стандартный rbind, пример:

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

или bind_rows(): (который должен быть быстрее )

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

Я не уверен, если один из они значительно более эффективны (например, для длинных / больших операций), а также, если есть другие, более эффективные альтернативы / лучшие практики?

1 Ответ

2 голосов
/ 26 марта 2020

Как указывалось в комментариях к OP, лучше всего сделать большой список, а затем связать все в конце. Это использует lapply() вместо явного l oop, за которым следует 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)

Теперь, если мы нацеливаемся на этот конкретный пример, требуя al oop, мы также можем использовать пару альтернатив , Например, вместо увеличения списка фреймов данных мы знаем, что каждая итерация приведет к целому числу. Таким образом, мы могли бы просто предварительно выделить целочисленные векторы, которые также легко перевести в :

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));
 } 
')

Аналогично, для вызовов data.frame много служебной информации , dplyr::bind_rows() и data.table::rbindlist() по умолчанию data.frame тип результатов для 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)

Производительность: R cpp, что неудивительно, самый быстрый подход. Но использование data.table::rbindlist или dplyr::bind_rows со списком - довольно простой подход.

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