Theo Creating an R dataframe row-by-row, không phải là lý tưởng để gắn thêm một data.frame
bằng cách sử dụng rbind
, vì nó tạo bản sao của toàn bộ dữ liệu.frame mỗi lần. Làm thế nào để tôi tích lũy dữ liệu trong số R
dẫn đến một số data.frame
mà không bị phạt này? Định dạng trung gian không cần phải là data.frame
.Trồng một data.frame theo cách hiệu quả bộ nhớ
Trả lời
tiếp cận đầu tiên
tôi đã cố gắng truy cập vào mỗi yếu tố của một tiền phân bổ data.frame:
res <- data.frame(x=rep(NA,1000), y=rep(NA,1000))
tracemem(res)
for(i in 1:1000) {
res[i,"x"] <- runif(1)
res[i,"y"] <- rnorm(1)
}
Nhưng tracemem đi điên (ví dụ như data.frame đã được sao chép vào một địa chỉ mới mỗi lần).
cách tiếp cận thay thế (không hoạt động hoặc)
Một cách tiếp cận (không chắc chắn đó là nhanh như tôi đã không làm chuẩn chưa) là để tạo ra một danh sách các data.frames, sau đó stack
tất cả chúng với nhau:
makeRow <- function() data.frame(x=runif(1),y=rnorm(1))
res <- replicate(1000, makeRow(), simplify=FALSE) # returns a list of data.frames
library(taRifx)
res.df <- stack(res)
Thật không may khi tạo danh sách Tôi cho rằng bạn sẽ khó ép trước khi phân bổ. Ví dụ:
> tracemem(res)
[1] "<0x79b98b0>"
> res[[2]] <- data.frame()
tracemem[0x79b98b0 -> 0x71da500]:
Nói cách khác, thay thế thành phần trong danh sách sẽ sao chép danh sách. Tôi giả sử toàn bộ danh sách, nhưng có thể nó chỉ là yếu tố của danh sách. Tôi không quen thuộc với các chi tiết về quản lý bộ nhớ của R.
Có lẽ phương pháp tốt nhất
Như với nhiều tốc độ hoặc các quá trình bộ nhớ hạn chế những ngày này, phương pháp tốt nhất cũng có thể được sử dụng data.table
thay vì một data.frame
. Kể từ data.table
có :=
assign bởi nhà điều hành tài liệu tham khảo, nó có thể cập nhật mà không cần sao chép:
library(data.table)
dt <- data.table(x=rep(0,1000), y=rep(0,1000))
tracemem(dt)
for(i in 1:1000) {
dt[i,x := runif(1)]
dt[i,y := rnorm(1)]
}
# note no message from tracemem
Nhưng khi @MatthewDowle chỉ ra, set()
là cách thích hợp để làm điều này bên trong một vòng lặp. Làm như vậy làm cho nó nhanh hơn vẫn:
library(data.table)
n <- 10^6
dt <- data.table(x=rep(0,n), y=rep(0,n))
dt.colon <- function(dt) {
for(i in 1:n) {
dt[i,x := runif(1)]
dt[i,y := rnorm(1)]
}
}
dt.set <- function(dt) {
for(i in 1:n) {
set(dt,i,1L, runif(1))
set(dt,i,2L, rnorm(1))
}
}
library(microbenchmark)
m <- microbenchmark(dt.colon(dt), dt.set(dt),times=2)
(Kết quả hiển thị dưới đây)
Benchmarking
Với sự chạy vòng 10.000 lần, bảng dữ liệu gần như là một trật tự toàn bộ độ lớn nhanh hơn:
Unit: seconds
expr min lq median uq max
1 test.df() 523.49057 523.49057 524.52408 525.55759 525.55759
2 test.dt() 62.06398 62.06398 62.98622 63.90845 63.90845
3 test.stack() 1196.30135 1196.30135 1258.79879 1321.29622 1321.29622
Và so sánh với :=
set()
:
> m
Unit: milliseconds
expr min lq median uq max
1 dt.colon(dt) 654.54996 654.54996 656.43429 658.3186 658.3186
2 dt.set(dt) 13.29612 13.29612 15.02891 16.7617 16.7617
Lưu ý rằng n
đây là 10^6 chứ không phải 10^5 như trong tiêu chuẩn âm mưu trên.Vì vậy, có một thứ tự độ lớn hơn, và kết quả được đo bằng mili giây không phải giây. Ấn tượng thực sự.
Theo như tôi có thể nói, ví dụ cuối cùng của bạn không phát triển dữ liệu. Bạn chỉ cần ghi đè hàng đầu tiên 1.000 lần. – Andrie
@Andrie. Rất tiếc. Sửa lỗi đó. Cảm ơn đã chỉ ra điều đó. –
Tốt nhưng bạn đã thấy ví dụ tốc độ ở dưới cùng của '?": = "' So sánh ': =' trong vòng lặp tới 'set()' trong vòng lặp. ': =' có chi phí (ví dụ:kiểm tra sự tồn tại và kiểu của các đối số được truyền cho '[.data.table'), đó là lý do tại sao' set() 'được cung cấp để sử dụng bên trong các vòng lặp. –
Tôi thích RSQLite
cho vấn đề đó: dbWriteTable(...,append=TRUE)
câu trong khi thu thập và tuyên bố dbReadTable
ở cuối.
Nếu dữ liệu đủ nhỏ, người ta có thể sử dụng tệp ": memory:", nếu nó lớn, đĩa cứng.
Tất nhiên, nó không thể cạnh tranh về tốc độ:
makeRow <- function() data.frame(x=runif(1),y=rnorm(1))
library(RSQLite)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
collect1 <- function(n) {
for (i in 1:n) dbWriteTable(con, "test", makeRow(), append=TRUE)
dbReadTable(con, "test", row.names=NULL)
}
collect2 <- function(n) {
res <- data.frame(x=rep(NA, n), y=rep(NA, n))
for(i in 1:n) res[i,] <- makeRow()[1,]
res
}
> system.time(collect1(1000))
User System verstrichen
7.01 0.00 7.05
> system.time(collect2(1000))
User System verstrichen
0.80 0.01 0.81
Nhưng nó có thể trông đẹp hơn nếu data.frame
s có nhiều hơn một hàng. Và bạn không cần phải biết số lượng hàng trước.
Ý tưởng thật tuyệt, nhưng nó [không hiệu quả] (http://stackoverflow.com/questions/20689650/how-to-append-rows-to-an-r-data-frame/38052208#38052208). Tôi đặt nó vào một thử nghiệm trên một sợi khác. –
Bạn cũng có thể có đối tượng danh sách trống trong đó các phần tử được lấp đầy bằng các khung dữ liệu; sau đó thu thập các kết quả ở cuối bằng một cách tương tự hoặc tương tự. Một ví dụ có thể được tìm thấy here. Điều này sẽ không phải chịu các hình phạt phát triển một đối tượng.
Vâng, tôi rất ngạc nhiên mà không ai đề cập việc chuyển đổi sang một ma trận chưa ...
So sánh với dt.colon và dt.set chức năng xác định bởi Ari B. Friedman, việc chuyển đổi sang một ma trận có thời gian chạy tốt nhất (hơi nhanh hơn dt.colon). Tất cả các ảnh hưởng bên trong một ma trận được thực hiện bằng cách tham chiếu, do đó không có bản sao bộ nhớ không cần thiết được thực hiện trong mã này.
Mã sản phẩm:
library(data.table)
n <- 10^4
dt <- data.table(x=rep(0,n), y=rep(0,n))
use.matrix <- function(dt) {
mat = as.matrix(dt) # converting to matrix
for(i in 1:n) {
mat[i,1] = runif(1)
mat[i,2] = rnorm(1)
}
return(as.data.frame(mat)) # converting back to a data.frame
}
dt.colon <- function(dt) { # same as Ari's function
for(i in 1:n) {
dt[i,x := runif(1)]
dt[i,y := rnorm(1)]
}
}
dt.set <- function(dt) { # same as Ari's function
for(i in 1:n) {
set(dt,i,1L, runif(1))
set(dt,i,2L, rnorm(1))
}
}
library(microbenchmark)
microbenchmark(dt.colon(dt), dt.set(dt), use.matrix(dt),times=10)
KẾT QUẢ:
Unit: milliseconds
expr min lq median uq max neval
dt.colon(dt) 7107.68494 7193.54792 7262.76720 7277.24841 7472.41726 10
dt.set(dt) 93.25954 94.10291 95.07181 97.09725 99.18583 10
use.matrix(dt) 48.15595 51.71100 52.39375 54.59252 55.04192 10
Ưu điểm của việc sử dụng một ma trận:
- này là phương pháp nhanh nhất cho đến nay
- bạn không cần phải tìm hiểu/sử dụng các đối tượng data.table
Con của việc sử dụng một ma trận:
- bạn chỉ có thể xử lý một kiểu dữ liệu trong một ma trận (đặc biệt là nếu bạn có loại hỗn hợp trong các cột của data.frame của bạn, sau đó tất cả họ sẽ được chuyển đổi để nhân vật bởi dòng: mat = as.matrix (dt) # chuyển sang ma trận)
Edited để làm gì rõ ràng tôi khá chắc chắn rằng bạn có nghĩa. Xin vui lòng trở lại nếu tôi sai lầm. –
Nếu bạn vẫn quan tâm, [đây là một điểm chuẩn khác của tập hợp các cách khác nhau để phát triển data.frame] (http://stackoverflow.com/questions/20689650/how-to-append-rows-to-an-r -data-frame/38052208 # 38052208) khi bạn không biết kích thước trước. –