web-dev-qa-db-ja.com

列のサブセット(.SDcols)に関数を適用し、別の列(グループ内)に異なる関数を適用します

これは、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

それは私が行方不明になっているかなり単純なものであると確信しています。ガイダンスを事前に感謝します。

33
Matt Weller

更新:問題 #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
_

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

28
Arun

これを試して:

_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つの観測値しかない場合に発生する可能性があります。

8
JBecker