これは、data.table
uning .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]
これによりエラーは発生しませんが、9行(3グループ)を含む回答が提供され、合計が列V1
で3回繰り返され、以下に示すようにV2
に配置された3つの列すべて(平均ではなく)の平均が返されます:
> 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
by groupを計算し、それを単一の列のsum
by by groupに結合することにより、明らかに複数のステップで問題を解決できます。
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
それは私が行方不明になっているかなり単純なものであると確信しています。ガイダンスを事前に感謝します。
更新:問題 #495 は この最近のコミット で解決され、これができるようになりました結構:
_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
_
古い回答の履歴を参照してください。
これを試して:
_dt[,list(sum(v1), mean(v2), mean(v3)), by=grp]
_
_data.table
_では、2番目の引数でlist()
を使用すると、最終的な_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.table
_でsetkey()
を使用する必要があります。
本当に、最初に必要なのは:
_setkey(dt, grp)
_
次に、上記の行を使用して同等の結果を生成できます。
[^ 1]:グループの数が合計行の数に近づくにつれて、これが特に当てはまると思います。たとえば、これは、キーが個人IDであり、多くの個人が1つまたは2つの観測値しかない場合に発生する可能性があります。