2013-12-08 10 views
30

data.tableの複数の列に共通の機能を適用する質問によく似ています。.SDcolsanswered thoroughly hereです。別の列(グループ内)に異なる機能を適用しながら、列のサブセット(.SDcols)に関数を適用

.SDサブセットの一部ではない別の列に異なる機能を同時に適用したいという違いがあります。

dt = data.table(grp = sample(letters[1:3],100, replace = TRUE), 
       v1 = rnorm(100), 
       v2 = rnorm(100), 
       v3 = rnorm(100)) 
sd.cols = c("v2", "v3") 
dt.out = dt[, list(v1 = sum(v1), lapply(.SD,mean)), by = grp, .SDcols = sd.cols] 

次のエラー利回り:

Error in `[.data.table`(dt, , list(v1 = sum(v1), lapply(.SD, mean)), by = grp, 
: object 'v1' not found 

v1列が列のサブセットに含まれていないので、今これは理にかなっているが、私はこの問題を解決するために私の試みを表示するには、以下の簡単な例を投稿します最初に評価する必要があります。だから私は、列の私のサブセットに含めて更なる探求:

sd.cols = c("v1","v2", "v3") 
dt.out = dt[, list(sum(v1), lapply(.SD,mean)), by = grp, .SDcols = sd.cols] 

今は、このエラーは発生しませんが、合計が列V1に三度繰り返して、それは、(3グループの場合)9行を含む答えを提供し、すべての3つの列(期待しかし望まれないように)、以下に示すようV2内に配置するための手段:

> dt.out 
    grp  V1     V2 
1: c -1.070608 -0.0486639841313638 
2: c -1.070608 -0.178154270921521 
3: c -1.070608 -0.137625003604012 
4: b -2.782252 -0.0794929150464099 
5: b -2.782252 -0.149529237116445 
6: b -2.782252 0.199925178109264 
7: a 6.091355 0.141659419355985 
8: a 6.091355 -0.0272192037753071 
9: a 6.091355 0.00815760216214876 

策ソリューション2を使用すると明らかに

ステップ次のように列のサブセットのグループによってmeanを計算し、単一の列のグループによってsumにそれを接合することで、複数の段階で問題を解決することが可能である:

dt.out1 = dt[, sum(v1), by = grp] 
dt.out2 = dt[, lapply(.SD,mean), by = grp, .SDcols = sd.cols] 
dt.out = merge(dt.out1, dt.out2, by = "grp") 

> dt.out 
    grp  V1   v2   v3 
1: a 6.091355 -0.0272192 0.008157602 
2: b -2.782252 -0.1495292 0.199925178 
3: c -1.070608 -0.1781543 -0.137625004 

イム必ずそれはかなり単純なことだI行方不明です。ご指導いただきありがとうございます。

+0

最初の式がうまくいかないという事実はバグですので、マージ構文と実行可能な解決策のバグ報告 – eddi

答えて

23

更新:問題#495this recent commitで解決されました、私たちは今、うまくこれを行うことができます。

require(data.table) # v1.9.7+ 
set.seed(1L) 
dt = data.table(grp = sample(letters[1:3],100, replace = TRUE), 
       v1 = rnorm(100), 
       v2 = rnorm(100), 
       v3 = rnorm(100)) 
sd.cols = c("v2", "v3") 
dt.out = dt[, list(v1 = sum(v1), lapply(.SD,mean)), by = grp, .SDcols = sd.cols] 

ただし、この場合には、v2がリストとして返されることに注意してください。それはあなたがlist(val, list())を効果的にやっているからです。あなたはおそらくするつもりは次のとおりです。

dt[, c(list(v1=sum(v1)), lapply(.SD, mean)), by=grp, .SDcols = sd.cols] 
# grp  v1   v2   v3 
# 1: a -6.440273 0.16993940 0.2173324 
# 2: b 4.304350 -0.02553813 0.3381612 
# 3: c 0.377974 -0.03828672 -0.2489067 

が古い答えの履歴を参照してください。

+0

Arun、私はこのケースで '.SD'ボトルネックが当てはまるとは思わない - 普通の' .SD'ボトルネックは '' .data.table''のオーバーヘッドと関係しています。 – eddi

+0

あなたは正しいのですが、それは*遅いですし、なぜatmなのか分かりません - これは別の場所で別の大規模なオーバーヘッド計算があることを意味します(または別の言い方をすれば - ボトルネックがCdogroups ) – eddi

+4

'' lapply'の 'eval'は何度も遅く、' .SD'ではありません。 'base :: lapply'のソースをCレベルで見てください。とにかく、 'list(...)'呼び出しを構築し、それを評価することでそれを行います。 'lapply'がループされているとき、同じ構築が無駄に繰り返されます。ですから、最適化はその構築を一度前にすることです(Rレベルでは '.data.table'の中で行い、それを' dogroups'に渡します)。しかし、 'lapply'への単純な呼び出しは現在のところ最適化されています。 'c()'と組み合わされたものは取り出されません。 cc @eddi –

6

これを試してください。第二引数にlist()を使用してdata.table

dt[,list(sum(v1), mean(v2), mean(v3)), by=grp] 

は、あなたが最終data.tableの結果列のセットを記述することができます。

.SDは非常に遅く[^ 1]ですので、より洗練された機能のように、サブセットdata.tableで提供されるすべてのデータが本当に必要な場合を除き、避けたいかもしれません。

.SDcolsの列が多い場合は、data.tableマージ構文を使用して1行でマージすることもできます。例えば

dt[, sum(v1), by=grp][dt[,lapply(.SD,mean), by=grp, .SDcols=sd.cols]] 

data.tableからmergeを利用するためには、まずそれが物事を一致させる方法を知っているあなたのdata.tablesetkey()を使用する必要があります。

だから本当に、最初に次のものが必要です。

setkey(dt, grp) 

次にあなたが同等の結果を生成するために上記の行を使用することができます。

[^ 1]:これは、グループの数が合計行数に近づくにつれて、特に当てはまります。たとえば、あなたのキーが個人IDであり、多くの個人が1つまたは2つの観測値を持つ場合、これが起こる可能性があります。

+0

+1を提出してください –

+0

'wmean'を使用すると、 '.SDcols'部分に指定されている重み付けカラムが必要ですが、私はそれを使いたくありません!私はすでにその列に 'sum'を使っているので、列に' weighted.mean'を計算するのは苦痛です... 'data.tableを実行する前にその列を除外しなければならないと思います'マージする。 –