Giả sử bạn chỉ đơn giản là không biết trước kích thước của data.frame. Nó cũng có thể là một vài hàng, hoặc một vài triệu. Bạn cần phải có một số loại thùng chứa, phát triển linh hoạt. Xem xét kinh nghiệm của tôi và tất cả các câu trả lời liên quan trong SO, tôi đưa ra 4 giải pháp riêng biệt:
rbindlist
vào data.frame
Sử dụng thao tác data.table
nhanh set
và ghép nối nó bằng cách nhân đôi bảng theo cách thủ công khi cần thiết.
Sử dụng RSQLite
và thêm vào bảng được lưu trong bộ nhớ.
data.frame
khả năng của riêng mình để phát triển và sử dụng môi trường tùy chỉnh (có ngữ nghĩa tham chiếu) để lưu trữ data.frame, do đó nó sẽ không bị sao chép ngược lại.
Đây là một thử nghiệm của tất cả các phương pháp cho cả số lượng nhỏ và lớn các hàng được nối thêm. Mỗi phương thức có 3 chức năng được liên kết với nó:
create(first_element)
trả về đối tượng sao lưu thích hợp với first_element
đưa vào.
append(object, element)
gắn element
vào cuối bảng (được đại diện bởiobject
).
access(object)
nhận được data.frame
với tất cả các phần tử được chèn.
rbindlist
vào data.frame
Điều đó khá dễ dàng và dễ hiểu:
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
+ nhân đôi bàn thủ công khi cần thiết.
Tôi sẽ lưu trữ độ dài thực của bảng trong một rowcount
thuộc tính.
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 nên được tối ưu hóa để chèn bản ghi nhanh, vì vậy ban đầu tôi rất hy vọng RSQLite
giải pháp
Về cơ bản, đây là bản sao và dán câu trả lời của Karsten W. trên chủ đề tương tự.
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
môi trường tùy chỉnh + bổ sung hàng của riêng.
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)
}
Bộ thử nghiệm:
Để thuận tiện, tôi sẽ sử dụng một chức năng kiểm tra để bao gồm tất cả chúng bằng cách gọi gián tiếp. (Tôi đã kiểm tra: sử dụng do.call
thay vì gọi trực tiếp các hàm không làm cho mã chạy lâu hơn có thể đo được).
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)))
}
Hãy xem hiệu suất của n = 10 lần chèn.
Tôi cũng đã thêm các chức năng 'giả dược' (có hậu tố 0
) không thực hiện bất kỳ điều gì - chỉ để đo lường chi phí của thiết lập thử nghiệm.
r<-microbenchmark(test(0,n=10), test(1,n=10),test(2,n=10),test(3,n=10), test(4,n=10))
autoplot(r)
Đối với hàng 1E5 (các phép đo được thực hiện trên CPU Intel (R) Core (TM) i7-4710HQ @ 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
Có vẻ như sulution dựa trên SQLite, mặc dù lấy lại một số tốc độ trên dữ liệu lớn, nhưng không bằng data.table + tăng trưởng theo cấp số nhân thủ công. Sự khác biệt gần như là hai bậc của độ lớn!
Tóm lược
Nếu bạn biết rằng bạn sẽ thêm một số lượng hàng khá nhỏ (n <= 100), hãy tiếp tục và sử dụng giải pháp đơn giản nhất có thể: chỉ cần gán các hàng cho data.frame bằng cách sử dụng ký hiệu ngoặc và bỏ qua thực tế rằng data.frame là không được điền trước.
Đối với mọi thứ khác, hãy sử dụng data.table::set
và phát triển data.table theo cấp số nhân (ví dụ: sử dụng mã của tôi).