2013-04-04 24 views
7

Tôi có một bảng dữ liệu lớn trong R:Pivot một lớn data.table

library(data.table) 
set.seed(1234) 
n <- 1e+07*2 
DT <- data.table(
    ID=sample(1:200000, n, replace=TRUE), 
    Month=sample(1:12, n, replace=TRUE), 
    Category=sample(1:1000, n, replace=TRUE), 
    Qty=runif(n)*500, 
    key=c('ID', 'Month') 
) 
dim(DT) 

Tôi muốn trục data.table này, như vậy mà loại trở thành một cột. Thật không may, vì số lượng các loại không phải là hằng số trong các nhóm, tôi không thể sử dụng this answer.

Bất kỳ ý tưởng nào về cách tôi có thể thực hiện việc này?

/chỉnh sửa: Căn cứ vào ý kiến ​​Joran và câu trả lời flodel, chúng tôi đang thực sự định hình lại như sau data.table:

agg <- DT[, list(Qty = sum(Qty)), by = c("ID", "Month", "Category")] 

Reshape Điều này có thể được thực hiện một số cách (Tôi đã nhận được một số câu trả lời tốt cho đến nay), nhưng những gì tôi thực sự tìm kiếm là một cái gì đó mà sẽ quy mô tốt để một data.table với hàng triệu hàng và hàng trăm đến hàng ngàn loại.

+1

Bạn có ý định lấp đầy nội dung của bảng bằng 'Số nguyên' không? Tổng hợp bất kỳ kết hợp trùng lặp nào? – joran

+0

@joran: trong ví dụ của tôi có sự kết hợp trùng lặp, nhưng vì lợi ích của đối số, cho phép giả sử không có. Điều tôi muốn là một cột riêng biệt cho từng giá trị của trường Danh mục, với NA hoặc 0 cho các kết hợp bị thiếu. – Zach

+0

@ joran Tôi nghĩ câu trả lời chính xác cho câu hỏi của bạn là có: Tôi muốn Danh mục trở thành cột, với Số lượng trong mỗi cột, NA hoặc 0 cho Danh mục bị thiếu và các bản sao phải được tổng hợp (nhưng công bằng để làm tổng hợp trước khi chúng tôi định hình lại). – Zach

Trả lời

7

data.table thực hiện phiên bản nhanh hơn của melt/dcast phương pháp cụ thể data.table (trong C). Nó cũng bổ sung thêm các tính năng bổ sung cho việc nung chảy và đúc nhiều cột. Vui lòng xem họa tiết Efficient reshaping using data.tables.

Lưu ý rằng chúng tôi không cần tải gói reshape2.

library(data.table) 
set.seed(1234) 
n <- 1e+07*2 
DT <- data.table(
    ID=sample(1:200000, n, replace=TRUE), 
    Month=sample(1:12, n, replace=TRUE), 
    Category=sample(1:800, n, replace=TRUE), ## to get to <= 2 billion limit 
    Qty=runif(n), 
    key=c('ID', 'Month') 
) 
dim(DT) 

> system.time(ans <- dcast(DT, ID + Month ~ Category, fun=sum)) 
# user system elapsed 
# 65.924 20.577 86.987 
> dim(ans) 
# [1] 2399401  802 
3

Như vậy?

agg <- DT[, list(Qty = sum(Qty)), by = c("ID", "Month", "Category")] 

reshape(agg, v.names = "Qty", idvar = c("ID", "Month"), 
     timevar = "Category", direction = "wide") 
3

Không có data.table phương pháp định hình lại cụ thể.

Đây là một cách tiếp cận sẽ hoạt động, nhưng nó được thay đổi.

Có yêu cầu tính năng #2619 Scoping for LHS in := để giúp thực hiện việc này đơn giản hơn.

Đây là một ví dụ đơn giản

# a data.table 
DD <- data.table(a= letters[4:6], b= rep(letters[1:2],c(4,2)), cc = as.double(1:6)) 
# with not all categories represented 
DDD <- DD[1:5] 
# trying to make `a` columns containing `cc`. retaining `b` as a column 
# the unique values of `a` (you may want to sort this...) 
nn <- unique(DDD[,a]) 
# create the correct wide data.table 
# with NA of the correct class in each created column 
rows <- max(DDD[, .N, by = list(a,b)][,N]) 
DDw <- DDD[, setattr(replicate(length(nn), { 
        # safe version of correct NA 
        z <- cc[1] 
         is.na(z) <-1 
        # using rows value calculated previously 
        # to ensure correct size 
         rep(z,rows)}, 
        simplify = FALSE), 'names', nn), 
      keyby = list(b)] 
# set key for binary search 
setkey(DDD, b, a) 
# The possible values of the b column 
ub <- unique(DDw[,b]) 
# nested loop doing things by reference, so should be 
# quick (the feature request would make this possible to 
# speed up using binary search joins. 
for(ii in ub){ 
    for(jj in nn){ 
    DDw[list(ii), {jj} := DDD[list(ii,jj)][['cc']]] 
    } 
} 

DDw 
# b d e f 
# 1: a 1 2 3 
# 2: a 4 2 3 
# 3: b NA 5 NA 
# 4: b NA 5 NA 
+0

Tôi sẽ thử điều này trên data.table mẫu của tôi và cho bạn biết điều gì sẽ xảy ra. – Zach

2

EDIT

tôi thấy SO post này, trong đó bao gồm một cách tốt hơn để chèn hàng mất tích vào một data.table. Chức năng fun_DT được điều chỉnh tương ứng. Mã bây giờ sạch hơn; Tuy nhiên, tôi không thấy bất kỳ cải tiến tốc độ nào trước đây là .

Xem cập nhật của tôi ở bài đăng khác. Arun của giải pháp hoạt động là tốt, nhưng bạn phải tự chèn các kết hợp còn thiếu. Vì bạn có nhiều cột định danh ở đây (ID, Month), tôi chỉ đưa ra giải pháp dơ bẩn ở đây (tạo ID2 trước, sau đó tạo tất cả kết hợp ID2-Category, sau đó điền data.table, sau đó thực hiện định dạng lại).

Tôi chắc chắn đây không phải là giải pháp tốt nhất, nhưng nếu được xây dựng trong this FR, các bước đó có thể được thực hiện tự động.

Các giải pháp có cùng tốc độ như nhau, mặc dù sẽ rất thú vị khi xem quy mô đó (máy của tôi quá chậm, vì vậy tôi không muốn tăng thêm nữa ... máy tính bị rơi thường xuyên ;-)

library(data.table) 
library(rbenchmark) 

fun_reshape <- function(n) { 

    DT <- data.table(
    ID=sample(1:100, n, replace=TRUE), 
    Month=sample(1:12, n, replace=TRUE), 
    Category=sample(1:10, n, replace=TRUE), 
    Qty=runif(n)*500, 
    key=c('ID', 'Month') 
) 
    agg <- DT[, list(Qty = sum(Qty)), by = c("ID", "Month", "Category")] 
    reshape(agg, v.names = "Qty", idvar = c("ID", "Month"), 
      timevar = "Category", direction = "wide") 
} 

#UPDATED! 
fun_DT <- function(n) { 

    DT <- data.table(
    ID=sample(1:100, n, replace=TRUE), 
    Month=sample(1:12, n, replace=TRUE), 
    Category=sample(1:10, n, replace=TRUE), 
    Qty=runif(n)*500, 
    key=c('ID', 'Month') 
) 

    agg <- DT[, list(Qty = sum(Qty)), by = c("ID", "Month", "Category")] 
    agg[, ID2 := paste(ID, Month, sep="_")] 

    setkey(agg, ID2, Category) 
    agg <- agg[CJ(unique(ID2), unique(Category))] 

    agg[, as.list(setattr(Qty, 'names', Category)), by=list(ID2)] 

} 

library(rbenchmark) 

n <- 1e+07 
benchmark(replications=10, 
      fun_reshape(n), 
      fun_DT(n)) 
      test replications elapsed relative user.self sys.self user.child sys.child 
2  fun_DT(n)   10 45.868  1 43.154 2.524   0   0 
1 fun_reshape(n)   10 45.874  1 42.783 2.896   0   0 
+0

Tôi sẽ thử cả hai với 200.000 ID và 1.000 danh mục và cho bạn biết cách thực hiện. Tôi nghi ngờ 'fun_DT' sắp nổ tung, nhưng' fun_reshape' có thể hoạt động. – Zach

+0

@Zach Hãy cho tôi biết, điều đó thật thú vị. Tại sao bạn nghĩ rằng mặc dù 'run_DT' sẽ nổ tung? Tôi nghĩ rằng những lĩnh vực bổ sung phải được tạo ra anyway, một cách này hay cách khác, vì vậy tôi sẽ không mong đợi điều đó. Hy vọng tôi đã nhận nó đúng. Ngoài ra, hãy xem cập nhật của tôi. Mã bây giờ sạch hơn. –

+0

200.000 ID * 12 tháng * 1.000 Danh mục = một dữ liệu đầy đủ. Khung của 2.400.000 hàng, lớn hơn kích thước dữ liệu tối đa. Khung trong R (2.147,483,648). – Zach