15

data.table(優れたパッケージ!!!)でいくつかの集計を行っていますが、.SD変数は多くのことに非常に役立つことがわかりました。ただし、グループが多い場合は、これを使用すると計算速度が大幅に低下します。例に従います:

# A moderately big data.table
x = data.table(id=sample(1e4,1e5,replace=T),
               code=factor(sample(2,1e5,replace=T)),
               z=runif(1e5)
              )

setkey(x,id,code)

system.time(x[,list(code2=nrow(.SD[code==2]), total=.N), by=id])
##  user  system elapsed 
##  6.226   0.000   6.242

system.time(x[,list(code2=sum(code==2), total=.N), by=id])
## user  system elapsed 
## 0.497   0.000   0.498

system.time(x[,list(code2=.SD[code==2,.N], total=.N), by=id])
## user  system elapsed 
## 6.152   0.000   6.168

私は何か間違ったことをしていますか?個々の列を優先して.SDを回避する必要がありますか?前もって感謝します。

4

2 に答える 2

14

私は何か間違ったことをしていますか?つまり.SD、個々の列を優先して避けるべきですか?

はい、正確に。.SD内のすべてのデータを実際に使用している場合にのみ使用してください.SD。また、 insidenrow()への呼び出しとサブクエリの呼び出しも原因であることがわかる場合があります。確認に使用してください。[.data.tablejRprof

FAQ2.1の最後の数文を参照してください

FAQ 2.1本当に長いj式を書かないようにするにはどうすればよいですか?列名を使用する必要があるとおっしゃっていましたが、列がたくさんあります。
グループ化する場合、jご存知のように、式は列名を変数として使用できますが、.SD各グループ(グループ化列を除く)のData.tableのサブセットを参照する予約済みの記号を使用することもできます。したがって、すべての列を合計すると、それはただのこと DT[,lapply(.SD,sum),by=grp]です。トリッキーに思えるかもしれませんが、書き込みと実行は高速です。匿名関数を作成する必要がないことに注意してください。他の方法との比較については、タイミングビネットとwikiを参照してください。オブジェクトは.SD内部で効率的に実装され、関数に引数を渡すよりも効率的です。ただし、これは行わないでください。DT[,sum(.SD[,"sales",with=FALSE]),by=grp]。それは機能しますが、非常に非効率的でエレガントではありません。これが意図されたものです: DT[,sum(sales),by=grp]そして何百倍も速くなる可能性があります。

FAQ3.1の最初の箇条書きも参照してください。

FAQ3.1私は20列と多数の行を持っています。1つの列の表現が非常に速いのはなぜですか?
いくつかの理由: -その列のみがグループ化され、式を検査し て他の列を使用していないことがわかる
ため、他の19個は無視されます。data.tablej

シンボルをdata.table調べjて見ると.SD、その効率の向上は窓の外に出ます。.SDすべての列を使用しなくても、各グループのサブセット全体にデータを入力する必要があります。実際に使用してdata.tableいる列を知ることは非常に困難です(たとえば、sを含めることができます)。ただし、とにかくそれらをすべて必要とする場合は、のように、もちろん問題ではありません。それはの理想的な使用法です。.SDjifDT[,lapply(.SD,sum),by=...].SD

したがって、はい、.SD可能な限り避けてください。列名を直接使用して、data.tableが最適化するj可能性を最大限に高めます。のシンボルの単なる存在.SDj重要です。

.SDcolsこれが導入された理由です。したがって、サブセットのみが必要な場合data.tableは、どの列を含めるべきかを判断できます。.SDそれ以外の場合は、必要な場合に備えて、すべての列data.tableを入力します。.SDj

于 2013-03-07T14:44:00.497 に答える
4

計算を 2 つのステップに分割し、結果のデータ フレームをマージして、これを解決してみてください。

system.time({
  x2 <- x[code==2, list(code2=.N), by=id]
  xt <- x[, list(total=.N), by=id]
  print(x2[xt])
})

私のマシンでは、7.42 秒ではなく 0.04 秒で実行されます。つまり、元のコードよりも約 200 倍高速です。

         id code2 total
   1:     1     6    14
   2:     2     8    10
   3:     3     7    13
   4:     4     5    13
   5:     5     9    18
  ---                  
9995:  9996     4     9
9996:  9997     3     6
9997:  9998     6    10
9998:  9999     3     4
9999: 10000     3     6
   user  system elapsed 
   0.05    0.00    0.04 
于 2013-03-07T14:24:29.980 に答える