1. ホーム
  2. r

[解決済み] data.tableにキーを設定する目的は何ですか?

2022-09-12 20:44:03

質問

私は data.table で、キーを設定する必要がある関数がたくさんあります (例: X[Y] ). そのため、データテーブルに適切にキーを設定するために、キーが何をするのかを理解したいと思います。


私が読んだ 1 つのソースは ?setkey .

setkey() をソートします。 data.table で、ソート済みとマークされます。ソートされたカラムがキーとなる。キーは任意のカラムを任意の順序で指定することができる。カラムは常に昇順にソートされる。表は参照によって変更される。1列と同じ大きさの一時的なワーキングメモリ以外には、コピーは全く作成されない。

ここでの私の考えは、キーが data.table を "ソートすることで、次のような非常に似た効果をもたらすということです。 order() . しかし、それはキーを持つ目的を説明していません。


data.tableのFAQ3.2、3.3で説明しています。

3.2 大きなテーブルでキーを持っていないのですが、それでもグループ化は本当に速いです。なぜでしょうか。

data.tableは基数ソートを使用しています。これは他の ソートアルゴリズムより明らかに高速です。基数は特に整数のみを対象としています。 ?base::sort.list(x,method="radix") . これはまた setkey() が速い理由の一つでもある。キーが設定されていない場合、またはキーと異なる順序でグループ化する場合 グループ化する場合、アドホック・バイと呼びます。

3.3 なぜ、キーの列でグループ化する方がad hoc byより速いのですか?

各グループはRAM上で連続であるため、ページフェッチを最小限に抑えることができます。 フェッチを最小化し、メモリを一括コピーすることができるからです ( memcpy Cでは でループさせるのではなく、一括コピーすることができるからです。

ここから推測すると、キーを設定することで、何らかの方法でRが他のアルゴリズムよりも"radix sorting"を使うことができ、そのために高速化されるのだと思います。


10分でわかるクイックスタートガイドにも、キーに関するガイドがあります。

  1. キーについて

まず、data.frameについて、特にrownames(英語で言うと 英語ではrow names)について考えてみましょう。つまり、1つの行に属する複数の名前です。 行に属する複数の名前です。1つの行に属する複数の名前?それは data.frameで慣れていることではありません。各行が持つ名前はせいぜい1つです。 の名前を持ちます。一人の人間は少なくとも2つの名前、rst nameとsecond nameを持っています。 これは、例えば電話帳を整理するのに便利です。 は、姓、名の順でソートされます。しかし、data.frame の各行には の各行は1つの名前しか持つことができません。

キーは1つまたは複数の 列で構成されます。 他のクラス、単に文字である必要はありません。さらに、行はキーによってソートされます。 でソートされます。したがって、data.tableは最大でも1つのキーを持つことができます。 は複数の方法でソートすることはできないからです。

一意性は強制されません。 すなわち、キーの重複は許されます。行はキーでソートされるため キーでソートされるため、キーに重複がある場合、連続して表示されます。

電話帳はキーが何であるかを理解するのに役に立ちましたが、キーは要因列を持つことと比較すると違いはないようです。さらに、なぜキーが必要なのか(特に特定の関数を使用するため)、キーとして設定する列をどのように選択するのかについては説明されていません。また、data.tableにtimeカラムがある場合、他のカラムをキーに設定すると、timeカラムも混乱してしまうようです。どなたか教えていただけませんか?

どのように解決するのですか?

この回答に加えて、ビネットを参照してください。 セカンダリインデックスとオートインデックス および キーと高速バイナリサーチベースのサブセット も同様です。

この問題 を予定している他のビネットをハイライトしています。


を踏まえて、この回答を再度更新しました(2016年2月)。 on= 機能を使用すると アドホック の結合も可能になります。以前の (古い) 回答は履歴を参照してください。

具体的にどのような setkey(DT, a, b) は何をするのでしょうか?

2つのことができます。

  1. の行を並べ替えます。 data.tableの行を並べ替えます。 DT を、提供された列によって ( a , b ) 参照により で、常に 増加する の順となる。
  2. はそれらのカラムを キー という属性を設定することで、それらのカラムを sortedDT .

並べ替えは高速に行われます (これは データ.テーブル の内部基数ソートによる)高速で、かつ、メモリ効率も良い(1つの余分な列の型は ダブル 型が割り当てられる)。

はいつ setkey() は必要ですか?

グループ化操作のため。 setkey() は決して絶対的な要件ではありませんでした。つまり、グループ化するために コールドバイ または アドホック・バイ .

## "cold" by
require(data.table)
DT <- data.table(x=rep(1:5, each=2), y=1:10)
DT[, mean(y), by=x] # no key is set, order of groups preserved in result

しかし、その前に v1.9.6 という形式の結合は x[i] 必須 key に設定する必要があります。 x . で、新しい on= 引数は v1.9.6+ から で、これはもう正しくなくなり、キーを設定することは ではなく はここでも絶対条件ではありません。

## joins using < v1.9.6 
setkey(X, a) # absolutely required
setkey(Y, a) # not absolutely required as long as 'a' is the first column
X[Y]

## joins using v1.9.6+
X[Y, on="a"]
# or if the column names are x_a and y_a respectively
X[Y, on=c("x_a" = "y_a")]

なお on= にも明示的に指定することができます。 keyed の結合でも明示的に指定できます。

を必要とする唯一の操作は key が絶対的にセットされていなければならないのは foverlaps() 関数です。しかし、私たちはさらにいくつかの機能を開発中で、それが完成すれば、この要件はなくなります。

  • では、何のために on= の引数を実装する理由は何でしょうか?

    かなり多くの理由があります。

    1. を含む操作であることを明確に区別することができる。 data.tables . ただ X[Y] とするだけでは、変数に適切な名前をつけることで明確になりますが、これも区別がつきません。

    2. がある列を理解することもできます。 の結合/サブセット が実行されているカラムを、そのコード行を見ることですぐに理解することができます(そして、対応する setkey() 行までトレースバックする必要はありません)。

    3. カラムが追加または更新される操作において 参照によって , on= の操作は、カラムを追加/更新するためだけに data.table 全体を並べ替える必要がないため、より高いパフォーマンスを発揮します。例えば

       ## compare 
       setkey(X, a, b) # why physically reorder X to just add/update a column?
       X[Y, col := i.val]
      
       ## to
       X[Y, col := i.val, on=c("a", "b")]
      
      

      2番目のケースでは、順序を変更する必要はありませんでした。時間がかかるのは順番を計算することではなく、RAM上のdata.tableを物理的に並べ替えることであり、それを避けることで元の順番を保持し、パフォーマンスも良くなっています。

    4. そうでない場合でも、繰り返しjoinを実行するのでない限りは、の間に顕著な性能差はないはずです。 キー付き アドホック を結合します。

このことから、キーボードで data.table はもうないのでしょうか?

  • data.tableをキーにするメリットはありますか?

    キーイングは data.table は、RAM上のこれらのカラムに基づいて物理的に順序を変更します。通常、順序を計算することは時間のかかる作業ではありません。 並べ替え そのものです。しかし、いったん RAM 上でデータをソートすると、同じグループに属する行は RAM 上ですべて連続するため、非常にキャッシュ効率がよくなります。キーが設定された data.tables の操作を高速化するのは、ソートされた状態だからです。

    したがって、キャッシュ効率の良い結合/集計を行うために、data.table全体の再順序付けに費やす時間が割に合うかどうかを見極めることが重要です。通常、グループ化/結合操作を繰り返し行う場合を除き、同じ キーとなる に繰り返しグループ化/結合操作が行われない限り、顕著な差は生じないはずです。

したがって、ほとんどの場合、キーを設定する必要はもうないはずです。私たちは on= を使用することをお勧めします。ただし、キーを設定することでパフォーマンスが劇的に向上し、それを利用したい場合はこの限りではありません。

質問です。 と比較して、どのような性能になると思われますか? キー付き を使用した場合 setorder() を並べ替えるには データテーブル を使用し on= ? ここまでくれば、わかるはずです :-).