58

約 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() 次に order(-Month)

   user  system elapsed 
816.144   9.648 848.798 

私の質問は次のとおりです。並べ替え後、年、メンバー ID、および月 (年、メンバー ID、月) で要約したい場合、data.table は並べ替え順序を認識しますか?

更新 2: Matthew Dowle への返信:

Year、MemberID、Month を使用してキーを設定した後も、グループごとに複数のレコードが残っています。それぞれのグループについてまとめたいと思います。つまり、X[order(Year, MemberID, Month)] を使用する場合、合計は data.table のバイナリ検索機能を利用しますか?

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

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

   user  system elapsed 
  7.910   7.750  53.916 

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

4

2 に答える 2

86

2014 年 6 月 5 日更新:

data.table v1.9.3 の現在の開発バージョンには、次の 2 つの新しい関数が実装されsetordersetordervいます。これらの関数は、並べ替える各列で昇順または降順のいずれかを選択するオプションを使用してdata.table 、参照によって並べ替えます。詳細?setorderについては、こちらをご覧ください。

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

ベンチマーク:

setorderこれは、data.table の内部高速順序と withを使用した場合の速度の違いを示してい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 の順序がここより約79倍速く、135倍速いことを示しています。base:::ordersetorderbase:::order

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

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


注:以前の回答と更新については、履歴/リビジョンを参照してください。

于 2012-12-03T15:47:49.957 に答える
14

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

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

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

于 2012-12-03T15:45:05.347 に答える