2013-04-04 9 views
7

Iは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

テーブルのボディに 'Qty'を記入することを意味しますか?重複した組み合わせを合計しますか? – joran

+0

@ joran:私の例では重複した組み合わせがありますが、引数のために、そうでないと仮定できます。私が欲しいのは、カテゴリー・フィールドの各値のための別個の列です。 – Zach

+0

@joranあなたの質問への正しい答えは「はい」だと思います。カテゴリが列になるようにしたい、列ごとに数量、欠落したカテゴリにNAsまたは0sを追加し、重複を合計する必要があります(ただし、再形成)。 – 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、月)、ID2を最初に作成し、すべてのID2カテゴリの組み合わせを作成してから、data.tableを埋めるようにしてから、再構成を行います。

私はこれが最良の解決策ではないと確信していますが、this FRが組み込まれている場合、これらの手順は自動的に行われる可能性があります。

解決策は、スケールがどのように低すぎるのか興味深いですが、おおよそ同じスピードですが、私のマシンは遅すぎますので、これ以上は増やしたくありません。 ;-)

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カテゴリ= 2,400,000,000行の完全なdata.frame。R(2,147,483,648)の最大サイズdata.frameよりも大きい。 – Zach