2013-04-04 113 views
7

我有一個大的數據表中R:樞軸大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) 

我想樞此data.table,使得分類成爲一列。不幸的是,由於類別的數量在組內不固定,我不能使用this answer

任何想法我可能會這樣做?

/編輯:基於joran的意見和flodel的答案,我們真正重塑以下data.table

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

這種重塑可以實現多種方式(我已經得到了一些很好的答案爲止),但我真正想要的是能夠很好地適應data.table的行爲,其中包含數百萬行和數百到數千個類別。

+1

你的意思是用'Qty'填充表格的正文嗎?總結任何重複的組合? – joran 2013-04-04 22:19:48

+0

@joran:在我的例子中有重複的組合,但爲了爭論,讓我們假設沒有。我想要的是對於類別字段的每個值都有一個不同的列,其中NA或0表示缺失的組合。 – Zach 2013-04-04 22:23:33

+0

@joran我認爲你的問題的正確答案是肯定的:我希望類別成爲一列,每列中包含數量,缺失類別爲NAs或0,重複數據應該總結(但在我們之前進行總結是公平的)重塑)。 – Zach 2013-04-05 14:30:02

回答

7

data.table實現的melt/dcast data.table具體方法(在C)更快的版本。它還增加了熔化和鑄造多列的附加功能。請參閱Efficient reshaping using data.tables小插圖。

請注意,我們不需要加載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

那樣?

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

沒有data.table特定的寬度調整方法。

這是一種可行的方法,但它相當寬鬆。

有一個功能要求#2619 Scoping for LHS in :=,以幫助使這個更簡單。

下面是一個簡單的例子

# 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

我會在我的示例data.table上試試這個,並讓你知道發生了什麼。 – Zach 2013-04-05 14:27:18

2

編輯

我發現這個SO post,其中包括更好的方式向 缺少的行插入到data.table。功能fun_DT相應地調整 。現在代碼更清潔了;雖然我沒有看到速度改善 。

看到其他職位我更新。 Arun的解決方案也適用,但您必須手動插入缺失的組合。由於你在這裏有更多的標識符列(ID,Month),所以我在這裏只提出了一個骯髒的解決方案(首先創建一個ID2,然後創建所有的ID2-Category組合,然後填充data.table,然後進行重塑)。

我敢肯定這是不是最好的解決辦法,但如果this FR是內置的,這些步驟可以自動完成。

的解決方案是大致相同的速度明智的,儘管它會看到如何擴展(我的機器太慢很有趣,所以我不希望任何進一步增加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

我會用200,000個ID和1,000個類別來嘗試這些,並讓你知道它是怎麼回事。我懷疑'fun_DT'會爆炸,但'fun_reshape'可能會起作用。 – Zach 2013-04-05 14:26:36

+0

@Zach讓我知道,這將是有趣的。你爲什麼認爲'run_DT'會爆炸?無論如何,我認爲這些附加領域必須以某種方式創建,所以我不會期望這一點。希望我說得對。另請參閱我的更新。現在代碼更清潔。 – 2013-04-06 01:22:02

+0

200,000個ID * 12個月* 1,000個類別= 2,400,000,000行的完整數據幀,大於R(2,147,483,648)中的最大數據量。 – Zach 2013-04-06 01:33:48