web-dev-qa-db-ja.com

data.tableを再構成するための適切/最速の方法

私はRに データテーブル を持っています:

library(data.table)
set.seed(1234)
DT <- data.table(x=rep(c(1,2,3),each=4), y=c("A","B"), v=sample(1:100,12))
DT
      x y  v
 [1,] 1 A 12
 [2,] 1 B 62
 [3,] 1 A 60
 [4,] 1 B 61
 [5,] 2 A 83
 [6,] 2 B 97
 [7,] 2 A  1
 [8,] 2 B 22
 [9,] 3 A 99
[10,] 3 B 47
[11,] 3 A 63
[12,] 3 B 49

Data.tableのグループによって変数vを簡単に合計できます。

out <- DT[,list(SUM=sum(v)),by=list(x,y)]
out
     x  y SUM
[1,] 1 A  72
[2,] 1 B 123
[3,] 2 A  84
[4,] 2 B 119
[5,] 3 A 162
[6,] 3 B  96

ただし、行ではなく列としてグループ(y)が必要です。 reshapeを使用してこれを実現できます。

out <- reshape(out,direction='wide',idvar='x', timevar='y')
out
     x SUM.A SUM.B
[1,] 1    72   123
[2,] 2    84   119
[3,] 3   162    96

データを集約した後にデータを再構成するより効率的な方法はありますか? data.table操作を使用して、これらの操作を1つのステップに結合する方法はありますか?

66
Zach

_data.table_パッケージは、より高速な_melt/dcast_関数を実装します(Cで)。また、溶融およびキャストを許可することで追加機能があります複数列。 Githubの新しい data.tablesを使用した効率的な再形成 をご覧ください。

data.tableのmelt/dcast関数はv1.9.0から使用可能になり、次の機能が含まれます。

  • キャストする前に_reshape2_パッケージをロードする必要はありません。ただし、他の操作用にロードする場合は、ロードしてくださいbefore loading _data.table_。

  • dcastもS3ジェネリックです。 dcast.data.table()はもうありません。 dcast()を使用してください。

  • melt

    • 「リスト」タイプのカラムで溶けることができます。

    • _variable.factor_と_value.factor_を獲得します。これらは、デフォルトで_reshape2_との互換性のためにそれぞれTRUEFALSEです。これにより、variable列とvalue列の出力タイプを(因子として、または因子として)直接制御できます。

    • _melt.data.table_の_na.rm = TRUE_パラメーターは、溶解中にNAを直接削除するように内部的に最適化されているため、はるかに効率的です。

    • 新規:meltは_measure.vars_のリストを受け入れることができ、リストの各要素で指定された列は結合されます。これは、patterns()の使用によりさらに容易になります。ビネットまたは_?melt_を参照してください。

  • dcast

    • 複数の_fun.aggregate_および複数の_value.var_を受け入れます。ビネットまたは_?dcast_を参照してください。

    • 式でrowid()関数を直接使用してid-columnを生成します。これは、行を一意に識別するために必要になる場合があります。 dcastを参照してください。

  • 古いベンチマーク:

    • melt:1000万行と5列、61.3秒が1.2秒に短縮されました。
    • dcast:100万行と4列、192秒が3.6秒に短縮されました。

ケルンのリマインダー(2013年12月)プレゼンテーションスライド32: なぜdcastプルリクエストを_reshape2_?に送信しないのか

73
Zach

上記のZachの回答に見られるように、この機能はdata.table(バージョン1.8.11以降)に実装されました。

Arun SO からこの素晴らしいコードの塊を見たところです。だから私はdata.tableソリューションがあると思います。この問題に適用:

library(data.table)
set.seed(1234)
DT <- data.table(x=rep(c(1,2,3),each=1e6), 
                  y=c("A","B"), 
                  v=sample(1:100,12))

out <- DT[,list(SUM=sum(v)),by=list(x,y)]
# edit (mnel) to avoid setNames which creates a copy
# when calling `names<-` inside the function
out[, as.list(setattr(SUM, 'names', y)), by=list(x)]
})
   x        A        B
1: 1 26499966 28166677
2: 2 26499978 28166673
3: 3 26500056 28166650

これにより、DWinのアプローチと同じ結果が得られます。

tapply(DT$v,list(DT$x, DT$y), FUN=sum)
         A        B
1 26499966 28166677
2 26499978 28166673
3 26500056 28166650

また、高速です。

system.time({ 
   out <- DT[,list(SUM=sum(v)),by=list(x,y)]
   out[, as.list(setattr(SUM, 'names', y)), by=list(x)]})
##  user  system elapsed 
## 0.64    0.05    0.70 
system.time(tapply(DT$v,list(DT$x, DT$y), FUN=sum))
## user  system elapsed 
## 7.23    0.16    7.39 

[〜#〜] update [〜#〜]

このソリューションが不均衡なデータセットでも機能するように(つまり、一部の組み合わせが存在しない場合)、最初にデータテーブルにそれらを入力する必要があります。

library(data.table)
set.seed(1234)
DT <- data.table(x=c(rep(c(1,2,3),each=4),3,4), y=c("A","B"), v=sample(1:100,14))

out <- DT[,list(SUM=sum(v)),by=list(x,y)]
setkey(out, x, y)

intDT <- expand.grid(unique(out[,x]), unique(out[,y]))
setnames(intDT, c("x", "y"))
out <- out[intDT]

out[, as.list(setattr(SUM, 'names', y)), by=list(x)]

概要

上記のコメントと組み合わせて、1行のソリューションを次に示します。

DT[, sum(v), keyby = list(x,y)][CJ(unique(x), unique(y)), allow.cartesian = T][,
   setNames(as.list(V1), paste(y)), by = x]

これを簡単に変更して、合計以上のものにすることもできます。例:

DT[, list(sum(v), mean(v)), keyby = list(x,y)][CJ(unique(x), unique(y)), allow.cartesian = T][,
   setNames(as.list(c(V1, V2)), c(paste0(y,".sum"), paste0(y,".mean"))), by = x]
#   x A.sum B.sum   A.mean B.mean
#1: 1    72   123 36.00000   61.5
#2: 2    84   119 42.00000   59.5
#3: 3   187    96 62.33333   48.0
#4: 4    NA    81       NA   81.0
32
Christoph_J

Data.tableオブジェクトは 'data.frame'を継承するため、tapplyを使用できます。

> tapply(DT$v,list(DT$x, DT$y), FUN=sum)
   AA  BB
a  72 123
b  84 119
c 162  96
21
42-

reshape2ライブラリのdcastを使用できます。ここにコードがあります

# DUMMY DATA
library(data.table)
mydf = data.table(
  x = rep(1:3, each = 4),
  y = rep(c('A', 'B'), times = 2),
  v = rpois(12, 30)
)

# USE RESHAPE2
library(reshape2)
dcast(mydf, x ~ y, fun = sum, value_var = "v")

注:tapplyソリューションははるかに高速です。

7
Ramnath