Diyelim ki data.frame'in boyutunu önceden bilmiyorsunuz. Birkaç sıra veya birkaç milyon olabilir. Dinamik olarak büyüyen bir tür konteynere ihtiyacınız var. SO'daki deneyimlerimi ve ilgili tüm cevapları dikkate alarak 4 farklı çözümle geliyorum:
rbindlist
data.frame'e
data.table
Hızlı set
çalışmayı kullanın ve gerektiğinde masayı manuel olarak ikiye katlayarak birleştirin.
RSQLite
Hafızada tutulan tabloyu kullanın ve ekleyin.
data.frame
Data.frame'i saklamak için özel ortamı (referans semantiğine sahip) büyütme ve kullanma becerisi, böylece geri dönüşte kopyalanmayacaktır.
Burada, hem küçük hem de çok sayıda eklenen satırlar için tüm yöntemlerin bir testi bulunmaktadır. Her yöntemin kendisiyle ilişkili 3 işlevi vardır:
create(first_element)
bu, yerleştirilmiş uygun destek nesnesini döndürür first_element
.
append(object, element)
element
tablonun sonuna ekler (ile temsil edilir object
).
access(object)
data.frame
eklenen tüm öğelerle birlikte alır .
rbindlist
data.frame'e
Bu oldukça kolay ve anlaşılırdır:
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
+ gerektiğinde masayı manuel olarak ikiye katlama.
Tablonun gerçek uzunluğunu bir rowcount
öznitelikte saklayacağım .
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,]))
}
Hızlı kayıt ekleme için SQL optimize edilmelidir, bu nedenle başlangıçta büyük umutlarım vardı RSQLite
çözüm
Bu temelde Karsten W. yanıtının benzer konuya kopyalanması ve yapıştırılmasıdır .
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.frame
kendi satır ekleme + özel ortamı.
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)
}
Test paketi:
Kolaylık sağlamak için, hepsini dolaylı aramayla kapsayacak şekilde bir test işlevi kullanacağım. (Kontrol ettim: do.call
işlevleri doğrudan çağırmak yerine kullanmak kodun daha uzun süre ölçülebilir çalışmasını sağlamaz).
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)))
}
N = 10 eklemenin performansını görelim.
Ayrıca 0
hiçbir şey yapmayan bir 'plasebo' işlevi (son ekli ) ekledim - sadece test kurulumunun ek yükünü ölçmek için.
r<-microbenchmark(test(0,n=10), test(1,n=10),test(2,n=10),test(3,n=10), test(4,n=10))
autoplot(r)
1E5 satırları için (ölçümler Intel (R) Core (TM) i7-4710HQ CPU @ 2.50GHz'de yapılmıştır):
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
SQLite tabanlı sulandırma, büyük verilerde bir miktar hız kazanmasına rağmen, data.table + manuel üstel büyümeye yakın değil gibi görünüyor. Aradaki fark neredeyse iki kat büyüklüğündedir!
özet
Oldukça az sayıda satır ekleyeceğinizi biliyorsanız (n <= 100), devam edin ve mümkün olan en basit çözümü kullanın: satırları parantez gösterimini kullanarak data.frame'e atayın ve data.frame'in önceden doldurulmamış.
Diğer her şey data.table::set
için data.table'ı üssel olarak kullanın ve büyütün (örneğin benim kodumu kullanarak).