web-dev-qa-db-ja.com

昇順/降順でdata.tableを高速にソートします

約300万行と40列のdata.tableがあります。次のSQLモックコードのように、グループ内で降順でこのテーブルを並べ替えたいと思います。

_sort by ascending Year, ascending MemberID, descending Month 
_

Data.tableでこれを行うための同等の方法はありますか?これまでのところ、私はそれを2つのステップに分解しなければなりません:

_setkey(X, Year, MemberID)
_

これは非常に高速で、数秒しかかかりません。

_X <- X[,.SD[order(-Month)],by=list(Year, MemberID)]
_

このステップには非常に長い時間がかかります(5分)。

更新:誰かがX <- X[sort(Year, MemberID, -Month)]を行うためのコメントを作成し、後で削除しました。このアプローチははるかに速いようです:

_user  system elapsed 
5.560  11.242  66.236 
_

私のアプローチ:setkey()then order(-Month)

_   user  system elapsed 
816.144   9.648 848.798 
_

私の質問は今です:sort(Year、MemberID、Month)の後にYear、MemberIdおよびMonthで要約したい場合、data.tableはソート順を認識しますか?

更新2:Matthew Dowleへの応答:

Year、MemberID、Monthを設定したsetkeyの後、グループごとに複数のレコードがあります。私が望むのは、グループごとに要約することです。つまり、X [order(Year、MemberID、Month)]を使用すると、合計はdata.tableのバイナリ検索機能を利用します。

_monthly.X <- X[, lapply(.SD[], sum), by = list(Year, MemberID, Month)]
_

更新3:マシューDはいくつかのアプローチを提案しました。最初のアプローチの実行時間は、order()アプローチよりも高速です。

_   user  system elapsed 
  7.910   7.750  53.916 
_

マシュー:私が驚いたのは、ほとんどの場合、月の記号を変換することです。それがなければ、setkeyは非常に高速です。

50
AdamNYC

2014年6月5日更新:

Data.table v1.9.3の現在の開発バージョンには、2つの新しい関数、つまりsetordersetordervが実装されており、まさに必要なことを行います。これらの関数は、data.table_を参照順に並べ替えます。各列の昇順または降順を選択して、並べ替えを行います。詳細については_?setorder_をご覧ください。

さらに、DT[order(.)]はデフォルトで、_data.table_の代わりに_base:::order_のinternal fast orderを使用するように最適化されています。これは、setorderとは異なり、データのコピー全体を作成するため、メモリ効率が低下しますが、ベースの順序を使用して操作するよりも桁違いに高速です。

ベンチマーク:

以下に、setorder、data.tableの内部高速順序、および_base:::order_を使用した速度の違いの図を示します。

_require(data.table) ## 1.9.3
set.seed(1L)
DT <- data.table(Year     = sample(1950:2000, 3e6, TRUE), 
                 memberID = sample(paste0("V", 1:1e4), 3e6, TRUE), 
                 month    = sample(12, 3e6, TRUE))

## using base:::order
system.time(ans1 <- DT[base:::order(Year, memberID, -month)])
#   user  system elapsed 
# 76.909   0.262  81.266 

## optimised to use data.table's fast order
system.time(ans2 <- DT[order(Year, memberID, -month)])
#   user  system elapsed 
#  0.985   0.030   1.027

## reorders by reference
system.time(setorder(DT, Year, memberID, -month))
#   user  system elapsed 
#  0.585   0.013   0.600 

## or alternatively
## setorderv(DT, c("Year", "memberID", "month"), c(1,1,-1))

## are they equal?
identical(ans2, DT)    # [1] TRUE
identical(ans1, ans2)  # [1] TRUE
_

このデータでは、ベンチマークはdata.tableの順序が約〜79x速い _base:::order_であり、setorder〜135x速い _base:::order_こちら。

_data.table_は常にCロケールでソート/順序付けします。別のロケールで注文する必要がある場合のみ、DT[base:::order(.)]を使用する必要があります。

これらの新しい最適化と機能はすべて一緒に FR#2405 を構成します。 bit64 :: integer64サポートも追加されました


注:以前の回答と更新については、履歴/改訂を参照してください。

73
Matt Dowle

コメントは私のものだったので、答えを投稿します。既に持っているものと同等かどうかをテストできなかったため、削除しました。早く聞いて嬉しいです。

X <- X[order(Year, MemberID, -Month)]

要約は、行の順序に依存するべきではありません。

13
Matthew Plourde