Misalkan Anda tidak mengetahui ukuran data.frame sebelumnya. Bisa jadi beberapa baris, atau beberapa juta. Anda perlu memiliki semacam wadah, yang tumbuh secara dinamis. Mempertimbangkan pengalaman saya dan semua jawaban terkait di SO saya datang dengan 4 solusi berbeda:
rbindlist ke data.frame
Gunakan operasi data.tablecepat setdan pasangkan dengan menggandakan tabel secara manual bila diperlukan.
Gunakan RSQLitedan tambahkan ke tabel yang disimpan dalam memori.
data.framekemampuan sendiri untuk mengembangkan dan menggunakan lingkungan kustom (yang memiliki semantik referensi) untuk menyimpan data.frame sehingga tidak akan disalin saat kembali.
Berikut adalah pengujian semua metode untuk jumlah baris yang ditambahkan kecil dan besar. Setiap metode memiliki 3 fungsi yang terkait dengannya:
create(first_element)yang mengembalikan objek pendukung yang sesuai dengan first_elementdimasukkan.
append(object, element)yang menambahkan elementakhir tabel (diwakili oleh object).
access(object)mendapatkan data.framedengan semua elemen yang disisipkan.
rbindlist ke data.frame
Itu cukup mudah dan tidak berbelit-belit:
create.1<-function(elems)
{
return(as.data.table(elems))
}
append.1<-function(dt, elems)
{
return(rbindlist(list(dt, elems),use.names = TRUE))
}
access.1<-function(dt)
{
return(dt)
}
data.table::set + menggandakan tabel secara manual saat diperlukan.
Saya akan menyimpan panjang sebenarnya dari tabel dalam rowcountatribut.
create.2<-function(elems)
{
return(as.data.table(elems))
}
append.2<-function(dt, elems)
{
n<-attr(dt, 'rowcount')
if (is.null(n))
n<-nrow(dt)
if (n==nrow(dt))
{
tmp<-elems[1]
tmp[[1]]<-rep(NA,n)
dt<-rbindlist(list(dt, tmp), fill=TRUE, use.names=TRUE)
setattr(dt,'rowcount', n)
}
pos<-as.integer(match(names(elems), colnames(dt)))
for (j in seq_along(pos))
{
set(dt, i=as.integer(n+1), pos[[j]], elems[[j]])
}
setattr(dt,'rowcount',n+1)
return(dt)
}
access.2<-function(elems)
{
n<-attr(elems, 'rowcount')
return(as.data.table(elems[1:n,]))
}
SQL harus dioptimalkan untuk penyisipan rekaman cepat, jadi saya awalnya memiliki harapan tinggi untuk RSQLitesolusi
Ini pada dasarnya adalah salin & tempel jawaban Karsten W. di utas serupa.
create.3<-function(elems)
{
con <- RSQLite::dbConnect(RSQLite::SQLite(), ":memory:")
RSQLite::dbWriteTable(con, 't', as.data.frame(elems))
return(con)
}
append.3<-function(con, elems)
{
RSQLite::dbWriteTable(con, 't', as.data.frame(elems), append=TRUE)
return(con)
}
access.3<-function(con)
{
return(RSQLite::dbReadTable(con, "t", row.names=NULL))
}
data.framelingkungan kustom + penambahan baris sendiri.
create.4<-function(elems)
{
env<-new.env()
env$dt<-as.data.frame(elems)
return(env)
}
append.4<-function(env, elems)
{
env$dt[nrow(env$dt)+1,]<-elems
return(env)
}
access.4<-function(env)
{
return(env$dt)
}
Rangkaian pengujian:
Untuk kenyamanan, saya akan menggunakan satu fungsi uji untuk mencakup semuanya dengan panggilan tidak langsung. (Saya memeriksa: menggunakan do.callalih-alih memanggil fungsi secara langsung tidak membuat kode berjalan terukur lebih lama).
test<-function(id, n=1000)
{
n<-n-1
el<-list(a=1,b=2,c=3,d=4)
o<-do.call(paste0('create.',id),list(el))
s<-paste0('append.',id)
for (i in 1:n)
{
o<-do.call(s,list(o,el))
}
return(do.call(paste0('access.', id), list(o)))
}
Mari kita lihat kinerja untuk n = 10 penyisipan.
Saya juga menambahkan fungsi 'plasebo' (dengan akhiran 0) yang tidak melakukan apa-apa - hanya untuk mengukur overhead pengaturan pengujian.
r<-microbenchmark(test(0,n=10), test(1,n=10),test(2,n=10),test(3,n=10), test(4,n=10))
autoplot(r)


Untuk 1E5 baris (pengukuran dilakukan pada Intel (R) Core (TM) i7-4710HQ CPU @ 2.50GHz):
nr function time
4 data.frame 228.251
3 sqlite 133.716
2 data.table 3.059
1 rbindlist 169.998
0 placebo 0.202
Sepertinya sulusi berbasis SQLite, meskipun mendapatkan kembali beberapa kecepatan pada data besar, tidak jauh dari pertumbuhan eksponensial data.table + manual. Perbedaannya hampir dua kali lipat!
Ringkasan
Jika Anda tahu bahwa Anda akan menambahkan jumlah baris yang agak kecil (n <= 100), lanjutkan dan gunakan solusi yang paling sederhana: cukup tetapkan baris ke data.frame menggunakan notasi braket dan abaikan fakta bahwa data.frame adalah tidak diisi sebelumnya.
Untuk semua yang lain gunakan data.table::setdan kembangkan data.table secara eksponensial (misalnya menggunakan kode saya).