2012-07-14 3 views
36

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ớ

+0

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. –

+0

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. –

Trả lời

38

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:= 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 

benchmarks

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ự.

+3

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

+0

@Andrie. Rất tiếc. Sửa lỗi đó. Cảm ơn đã chỉ ra điều đó. –

+3

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. –

5

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.

+0

Ý 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. –

5

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.

5

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.colondt.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)