2013-04-04 3 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) 

카테고리 I 열에 지도록이 data.table 피봇 싶다. 안타깝게도 그룹 내의 카테고리 수가 일정하지 않으므로 this answer을 사용할 수 없습니다.

어떻게 생각하나요?

/편집 : joran의 의견과 flodel의 답변에 따라, 우리는 정말 재편하고있는 data.table 다음

이 모양 변경은 여러 가지 방법을 수행 할 수 있습니다

agg <- DT[, list(Qty = sum(Qty)), by = c("ID", "Month", "Category")] 
(내가 지금까지 좋은 답변을 늘어 놓던). 그러나 내가 정말로 찾고있는 것은 수백만 개의 행과 수천에서 수천 개의 범주가있는 data.table으로 잘 확장되는 것이다.

+1

테이블 본문에'수량 '을 채우겠습니까? 중복 조합 합계? – joran

+0

@ joran : 필자의 예에서는 중복 된 조합이 있지만 인수를 위해 존재하지 않는다고 가정 할 수 있습니다. 내가 원하는 것은 범주 필드의 각 값에 대해 별개의 열로, NA 또는 누락 된 조합에 대해 0입니다. – Zach

+0

@joran 질문에 대한 정답은 예라고 생각합니다. 카테고리가 누락 된 카테고리에 대해 각 열에 수량, 수량 또는 0으로, 그리고 중복이 합산되어야합니다. (하지만 합산을하기 전에 합산해야합니다. 재구성). – Zach

답변

7

data.table은 (C)에 melt/dcast data.table 구체적인 방법 빠른 버전을 구현한다. 용융 및 주조를위한 추가 기능이 추가되었습니다. 다중 열. 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

2

EDIT

제가 data.table에 누락 된 행을 삽입하기위한 더 나은 방법이 포함 SO post을 알 수있다. 따라서 기능 fun_DT은 으로 조정됩니다. 코드가 더 깨끗합니다. 그래도 어떤 속도 향상도 보이지 않는다. .

다른 게시물의 내용을 참조하십시오. Arun의 솔루션도 잘 작동하지만 누락 된 조합을 수동으로 삽입해야합니다. 여기에 식별자 열 (ID, Month)이 더 많으므로 여기서는 (ID2를 먼저 만든 다음 모든 ID2- 범주 조합을 만든 다음 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

+0

@Zach 알려주세요, 재미있을 것입니다. 왜 그 run_DT가 날라 갈 지 생각해? 나는 그 추가 필드가 어쨌든, 한 가지 방법으로 만들어 져야한다고 생각한다. 그래서 나는 그것을 기대하지 않을 것이다. 나는 그것이 올바르게되기를 바란다. 또한, 내 업데이 트를 참조하십시오. 코드가 더 깨끗합니다. –

+0

200,000 개의 ID * 12 개월 * 1,000 범주 = 240,000,000 행의 전체 데이터 프레임으로 R (2,147,483,648)의 최대 크기 data.frame보다 큽니다. – Zach