web-dev-qa-db-ja.com

`data.table`で動的な列名を使用する

別の列でグループ化されたdata.tableのいくつかの列のそれぞれの平均を計算したいと思います。私の質問は、SO( one and two ))に関する他の2つの質問と似ていますが、問題に適用できませんでした。

次に例を示します。

_library(data.table)
dtb <- fread(input = "condition,var1,var2,var3
      one,100,1000,10000
      one,101,1001,10001
      one,102,1002,10002
      two,103,1003,10003
      two,104,1004,10004
      two,105,1005,10005
      three,106,1006,10006
      three,107,1007,10007
      three,108,1008,10008
      four,109,1009,10009
      four,110,1010,10010")

dtb
#    condition var1 var2  var3
# 1:       one  100 1000 10000
# 2:       one  101 1001 10001
# 3:       one  102 1002 10002
# 4:       two  103 1003 10003
# 5:       two  104 1004 10004
# 6:       two  105 1005 10005
# 7:     three  106 1006 10006
# 8:     three  107 1007 10007
# 9:     three  108 1008 10008
# 10:     four  109 1009 10009
# 11:     four  110 1010 10010
_

各単一平均の計算は簡単です。例えば「var1」の場合:dtb[ , mean(var1), by = condition]。しかし、多くの変数があり、それらすべてを記述する必要がある場合、これはすぐに扱いにくくなります。したがって、dtb[, list(mean(var1), mean(var2), mean(var3)), by = condition]は望ましくありません。列名を動的にする必要があり、次のようなものになりたいです。

_   condition  var1   var2    var3
1:       one 101.0 1001.0 10001.0
2:       two 104.0 1004.0 10004.0
3:     three 107.0 1007.0 10007.0
4:      four 109.5 1009.5 10009.5
_
32
David D

.SDcolsを使用する必要があります(特に列が多すぎて、特定の操作を(グループ化変数列を除いて)列のサブセットに対してのみ実行する必要がある場合。

dtb[, lapply(.SD, mean), by=condition, .SDcols=2:4]

#    condition  var1   var2    var3
# 1:       one 101.0 1001.0 10001.0
# 2:       two 104.0 1004.0 10004.0
# 3:     three 107.0 1007.0 10007.0
# 4:      four 109.5 1009.5 10009.5

次のように、最初に変数で取得したいすべての列名を取得し、それを.SDcolsに渡すこともできます。

keys <- setdiff(names(dtb), "condition")
# keys = var1, var2, var3
dtb[, lapply(.SD, mean), by=condition, .SDcols=keys]

編集:Matthew Dowleが正しく指摘したように、conditionでグループ化した後、他のすべての列で平均を計算する必要があるので、ただ:

dtb[, lapply(.SD, mean), by=condition]

Davidの編集:(拒否されました): thisから.SDの詳細を読みますpost 。これはここに関連があると思います。 @Davidに感謝します。

編集2:1000行と301列のdata.tableがあるとします(グループ化用の1列と300数値列):

require(data.table)
set.seed(45)
dt <- data.table(grp = sample(letters[1:15], 1000, replace=T))
m  <- matrix(rnorm(300*1000), ncol=300)
dt <- cbind(dt, m)
setkey(dt, "grp")

そして、列の平均、たとえば251:300だけを見つけたいと思ったとします。

  • すべての列の平均を計算してから、これらの列をサブセット化できます(データ全体で計算するため、これはあまり効率的ではありません)。

    dt.out <- dt[, lapply(.SD, mean), by=grp]
    dim(dt.out) # 15 * 301, not efficient.
    
  • 最初にdata.tableをフィルタリングしてこれらの列だけにしてから、平均を計算できます(特定の列で操作が必要になるたびに追加のサブセット化されたdata.tableを作成する必要があるため、これも必ずしも最良の解決策ではありません。

    dt.sub <- dt[, c(1, 251:300), with=FALSE]
    setkey(dt.sub, "grp")
    dt.out <- dt.sub[, lapply(.SD, mean), by=grp]
    
  • 通常どおりに各列を1つずつ指定できます(ただし、これは小さなdata.tablesに適しています)

    # if you just need one or few columns
    dt.out <- dt[, list(m.v251 = mean(V251)), by = grp]
    

それで、最善の解決策は何ですか?答えは。SDcolsです。

ドキュメントに記載されているように、data.table xの場合、。SDcolsは含まれる列を指定します。SD

これは基本的に、(以前に行ったように)サブセットを作成する代わりに、.SDに渡される列を暗黙的にフィルタリングしますが、それだけが非常に効率的で高速です!

どうすればこれを行うことができますか?

  • 列番号を指定することにより:

    dt.out <- dt[, lapply(.SD, mean), by=grp, .SDcols = 251:300]
    dim(dt.out) # 15 * 51 (what we expect)
    
  • または、列IDを指定します。

    ids <- paste0("V", 251:300) # get column ids
    dt.out <- dt[, lapply(.SD, mean), by=grp, .SDcols = ids]
    dim(dt.out) # 15 * 51 (what we expect)
    

引数として列名と数値の両方を受け入れます。どちらの場合も、.SDには、指定したこれらの列のみが提供されます。

お役に立てれば。

43
Arun