2016-09-17 16 views
8
df %>% split(.$x) 

trở nên chậm đối với số lượng lớn các giá trị duy nhất của x. Thay vào đó, nếu chúng ta chia khung dữ liệu theo cách thủ công thành các tập con nhỏ hơn và sau đó thực hiện chia nhỏ trên mỗi tập hợp con, chúng tôi sẽ giảm thời gian xuống ít nhất một bậc độ lớn.Tại sao phân chia không hiệu quả trên các khung dữ liệu lớn với nhiều nhóm?

library(dplyr) 
library(microbenchmark) 
library(caret) 
library(purrr) 

N  <- 10^6 
groups <- 10^5 
df  <- data.frame(x = sample(1:groups, N, replace = TRUE), 
        y = sample(letters, N, replace = TRUE)) 
ids  <- df$x %>% unique 
folds10 <- createFolds(ids, 10) 
folds100 <- createFolds(ids, 100) 

Chạy microbenchmark cho chúng ta

## Unit: seconds 

## expr             mean 
l1 <- df %>% split(.$x)        # 242.11805 

l2 <- lapply(folds10, function(id) df %>% 
     filter(x %in% id) %>% split(.$x)) %>% flatten # 50.45156 

l3 <- lapply(folds100, function(id) df %>% 
     filter(x %in% id) %>% split(.$x)) %>% flatten # 12.83866 

split không được thiết kế cho các nhóm lớn? Có bất kỳ lựa chọn thay thế nào ngoài đặt giá thầu ban đầu thủ công không?

Máy tính xách tay của tôi là một macbook pro vào cuối năm 2013, 2.4GHz 8GB

+0

Tôi muốn xử lý các mục danh sách kết quả song song, tức là 'list_of_dataframes%>% map (sequentially_process_each_row_of_df)' – Rickard

+0

Hãy xem xét, cũng 'order'ing' df' trước 'split'ting, để' .Internal (split()) 'truy cập bộ nhớ liên tục hơn -' system.time ({a = split (df, df $ x)}); system.time ({odf = df [thứ tự (df $ x),]; b = phân tách (odf, odf $ x)}); giống hệt nhau (a, b) ' –

+0

@alexis_laz thực sự, thứ tự tạo tên hàng, thay vì cải thiện mẫu truy cập bộ nhớ - so sánh' .row_names_info (df) 'và' .row_names_info (df [order (df $ x),]) ' ; giá trị âm trong trường hợp đầu tiên chỉ ra rằng các tên hàng được lưu trữ gọn gàng như 'c (NA, 1000000)', giá trị dương trong trường hợp thứ hai mà chúng được lưu trữ theo nghĩa đen là một vector nguyên. –

Trả lời

2

Đây không phải là vấn đề nghiêm trọng split.data.frame, có một vấn đề chung về khả năng mở rộng của dữ liệu.frame cho nhiều nhóm.
Bạn có thể tăng tốc khá tốt nếu sử dụng split.data.table. Tôi đã phát triển phương pháp này trên các phương thức data.table thông thường và có vẻ như quy mô khá tốt ở đây.

system.time(
    l1 <- df %>% split(.$x) 
) 
# user system elapsed 
#200.936 0.000 217.496 
library(data.table) 
dt = as.data.table(df) 
system.time(
    l2 <- split(dt, by="x") 
) 
# user system elapsed 
# 7.372 0.000 6.875 
system.time(
    l3 <- split(dt, by="x", sorted=TRUE) 
) 
# user system elapsed 
# 9.068 0.000 8.200 

sorted=TRUE sẽ trở lại danh sách các thứ tự như phương pháp data.frame, bằng phương pháp data.table mặc định sẽ giữ gìn trật tự hiện diện trong dữ liệu đầu vào. Nếu bạn muốn dính vào data.frame bạn có thể ở cuối sử dụng lapply(l2, setDF).

PS. split.data.table đã được thêm vào 1.9.7, lắp đặt các phiên bản devel là khá đơn giản

install.packages("data.table", type="source", repos="http://Rdatatable.github.io/data.table") 

Thông tin thêm về rằng trong Installation wiki.

+1

split.data.table nhanh hơn đáng kể. Tôi đã kết thúc viết lại các phần của mã bằng cách sử dụng data.table. – Rickard

8

Giải thích chi tiết hơn câu trả lời. Cài đặt một khung dữ liệu lớn tốn kém hơn so với cài đặt phụ một khung dữ liệu nhỏ

> df100 = df[1:100,] 
> idx = c(1, 10, 20) 
> microbenchmark(df[idx,], df100[idx,], times=10) 
Unit: microseconds 
     expr  min  lq  mean median  uq  max neval 
    df[idx, ] 428.921 441.217 445.3281 442.893 448.022 475.364 10 
df100[idx, ] 32.082 32.307 35.2815 34.935 37.107 42.199 10 

split() thanh toán chi phí này cho mỗi nhóm.

Lý do có thể được nhìn thấy bằng cách chạy Rprof()

> Rprof(); for (i in 1:1000) df[idx,]; Rprof(NULL); summaryRprof() 
$by.self 
     self.time self.pct total.time total.pct 
"attr"  1.26  100  1.26  100 

$by.total 
       total.time total.pct self.time self.pct 
"attr"    1.26  100  1.26  100 
"[.data.frame"  1.26  100  0.00  0 
"["     1.26  100  0.00  0 

$sample.interval 
[1] 0.02 

$sampling.time 
[1] 1.26 

Tất cả thời gian đang được chi tiêu trong một cuộc gọi đến attr(). Bước qua mã sử dụng debug("[.data.frame") cho thấy rằng sự đau đớn liên quan đến một cuộc gọi như

attr(df, "row.names") 

ví dụ nhỏ này cho thấy một trick mà R sử dụng để tránh đại diện cho tên hàng đó không có mặt: sử dụng c(NA, -5L), chứ không phải là 1:5.

> dput(data.frame(x=1:5)) 
structure(list(x = 1:5), .Names = "x", row.names = c(NA, -5L), class = "data.frame") 

Lưu ý rằng attr() trả về một vector - các row.names được tạo ra một cách nhanh chóng, và cho một data.frame lớn một số lượng lớn các row.names được tạo ra

> attr(data.frame(x=1:5), "row.names") 
[1] 1 2 3 4 5 

Vì vậy, một có thể mong đợi rằng thậm chí cả các tên vô nghĩa sẽ làm tăng tốc độ tính toán

> dfns = df; rownames(dfns) = rev(seq_len(nrow(dfns))) 
> system.time(split(dfns, dfns$x)) 
    user system elapsed 
    4.048 0.000 4.048 
> system.time(split(df, df$x)) 
    user system elapsed 
87.772 16.312 104.100 

Tách một vector hoặc ma trận cũng sẽ nhanh.

Các vấn đề liên quan