正如OP在评论中建议的那样,最好的方法是创建一个大列表,然后在最后绑定所有内容。这使用lapply()
而不是显式循环,接着使用do.call(rbind, tmp)
:
n = 1000
tmp = lapply(seq_len(n), function(i) data.frame(a = i, b = i))
output = do.call(rbind, tmp)
output = dplyr::bind_rows(tmp)
output = data.table::rbindlist(tmp)
现在,如果我们针对这个特定的例子并要求循环,我们也可以使用一些替代方案。例如,我们知道每次迭代都会产生一个整数,而不是增加数据框列表。因此,我们可以简单地预先分配整数向量,这也容易转换为
rcpp:
n = 1000L
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));
}
')
同样地,
data.frame
调用时存在很多开销。
dplyr::bind_rows()
和
data.table::rbindlist()
默认将
lists
转换为
data.frame
类型的结果:
tmp = lapply(seq_len(n), function(i) list(a = i, b = i))
output = rbindlist(tmp)
setDF(output)
output = bind_rows(tmp)
as.data.frame(output)
性能:
Rcpp是最快的方法,但使用data.table::rbindlist
或dplyr::bind_rows
与列表一起使用是一个相当简单的方法。
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
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
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)
)
bind_rows(output_list)
绑定所有内容? - stevec