data.tableにキーを設定する目的は何ですか?


113

私はdata.tableを使用しており、キーを設定する必要がある多くの機能があります(例:)X[Y]。そのため、データテーブルにキーを適切に設定するために、キーの機能を理解したいと考えています。


私が読んだソースの1つはでした?setkey

setkey()a data.tableをソートし、ソート済みとしてマークします。ソートされた列がキーです。キーは、任意の順序で任意の列にすることができます。列は常に昇順でソートされます。参照により表が変更されます。1つの列ほどの一時的な作業メモリーを除いて、コピーはまったく作成されません。

ここで私が覚えているのは、キーがdata.tableを「ソート」して、と非常によく似た結果になるということorder()です。ただし、キーを持つ目的については説明していません。


data.table FAQ 3.2および3.3は次のことを説明しています。

3.2大きなテーブルにキーはありませんが、グループ化はまだ非常に高速です。何故ですか?

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

3.3なぜキーの列によるグループ化はアドホックよりも速いのですか?

各グループはRAM内で隣接しているため、ページフェッチが最小限に抑えられ、メモリはmemcpyCでループするのではなく、一括して(Cで)コピーできます。

ここから、キーを設定することで、Rが他のアルゴリズムよりも「基数ソート」を使用できるようになると思います。そのため、それがより高速です。


10分間のクイックスタートガイドには、キーに関するガイドもあります。

  1. キー

最初に、data.frame、具体的には行名(または英語では行名)を検討します。つまり、単一の行に属する複数の名前です。1つの行に属する複数の名前?それはdata.frameで慣れていることではありません。各行の名前は最大1つであることはわかっています。人は少なくとも2つの名前、名と名を持っています。これは、電話帳を整理するのに便利です。たとえば、姓、次に名でソートされます。ただし、data.frameの各行は1つの名前しか持つことができません。

キーは、行名の1つ以上の列で構成されます。これは、単なる文字ではなく、整数、係数、文字、またはその他のクラスの場合があります。さらに、行はキーでソートされます。したがって、data.tableは複数の方法でソートできないため、最大で1つのキーを持つことができます。

一意性は強制されません。つまり、重複するキー値が許可されます。行はキーでソートされるため、キーの重複は連続して表示されます

電話帳は、キーが何であるかを理解するのに役立ちましたが、キーは、ファクター列を持っていることと比較して、違いがないようです。さらに、キーが必要な理由(特に特定の関数を使用するため)と、キーとして設定する列を選択する方法についても説明していません。また、時間を列として持つdata.tableで、他の列をキーとして設定すると、時間列も混乱する可能性があり、他の列をキー。誰かが私を啓発してくれますか?


「キーを設定すると、どういうわけかRは他のアルゴリズムよりも「基数ソート」を使用できるようになると思います」-ヘルプからはまったく得られません。私が読んだのは、キーを設定するとキーでソートされるということです。キー以外の列で「アドホック」ソートを実行できます。これは高速ですが、すでにソートした場合ほど高速ではありません。
アリB.フリードマン

行を選択するときは、バイナリスキャンの方がベクトルスキャンよりも高速だと思います。私はコンピュータサイエンティストではないので、それが実際に何を意味するのかわかりません。よくある質問のほかに、参照の導入を
フランク・

回答:


125

マイナーアップデート:新しいHTMLビネットも参照してください。この問題は、私たちが計画している他のビネットを強調しています。


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

正確には何をしsetkey(DT, a, b)ますか?

次の2つのことを行います。

  1. 参照によって提供される列(abによって常にdata.table の行を並べ替え、常に昇順で並べ替えます。DT
  2. に呼び出される属性を設定して、これらの列をキー列としてマークsortedDTます。

並べ替えは高速であり(data.tableの内部基数によるソートにより)、メモリ効率も高くなります(doubleタイプの追加の列が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がありましたxv1.9.6 +の新しいon=引数により、これはもう当てはまりません。したがって、ここでもキーの設定は絶対的な要件ではありません

## 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. これにより、2つの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. あなたがしているパフォーマンスを繰り返し参加していない限りでもそうでない場合、間には顕著なパフォーマンスの違いがあってはならないキー付きアドホック参加します。

これは、data.tableにキーを設定することにはどのような利点があるのでしょうか?

  • data.tableをキーイングすることには利点がありますか?

    data.tableにキーを付けると、RAM内のそれらの列に基づいて物理的に並べ替えられます。通常、順序の計算は時間のかかる部分ではなく、再順序付け自体です。ただし、RAMでデータを並べ替えると、同じグループに属する行はすべてRAMで隣接しているため、キャッシュ効率が非常に高くなります。キー付きのdata.tablesでの操作を高速化するのはソートです。

    したがって、data.table全体の並べ替えに費やされた時間が、キャッシュ効率の高い結合/集計を行う時間に見合うかどうかを把握することが不可欠です。通常、同じキー設定されたdata.tableに対して繰り返されるグループ化/結合操作がない限り、顕著な違いはありません。

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

質問:data.tableを並べ替えて使用する場合、キー付き結合と比較してどのようなパフォーマンスになると思いますか?ここまで進んでいれば、それを理解できるはずです:-)。setorder()on=


3
よろしくお願いします。これまで、私は「バイナリ検索」が実際に何を意味するのかについて考えていませんでしたし、ハッシュの代わりにそれが使用された理由を本当に理解していませんでした。
フランク・

@Arun、DT[J(1e4:1e5)]本当に同等DF[DF$x > 1e4 & DF$x < 1e5, ]ですか?どういうJ意味か教えてもらえますか?また、sample(1e4, 1e7, TRUE)1e4を超える数値が含まれていないため、この検索で​​は行が返されません。
フィッシュタンク、2015

@fishtank、この場合、それは、>=そして<=-修正されるべきです。J(および.)はのエイリアスですlist(つまり、これらは同等です)。内部的にiがリストである場合、それはdata.tableに変換され、その後、行インデックスの計算にバイナリ検索が使用されます。混乱を避けるために修正1e41e5れました。見つけてくれてありがとう。on=キーを設定するのではなく、引数を直接使用してバイナリサブセットを実行できることに注意してください。詳細については、新しいHTMLビネットをご覧ください。結合のビネットについては、そのページに注目してください。
2015

おそらく、これはより徹底的なアップデートに行くことができますか?「必要な」セクションには、例えば、古くなっているようだ
MichaelChirico

使用されているキーを教えてくれる機能は何ですか?
skan 2018

20

キーは基本的にはデータセットへのインデックスであり、非常に高速で効率的な並べ替え、フィルター、結合操作を可能にします。これらはおそらくデータフレームの代わりにデータテーブルを使用する最良の理由です(データテーブルを使用する構文もはるかにユーザーフレンドリーですが、キーとは関係ありません)。

インデックスがわからない場合は、次のことを考慮してください。電話帳は名前で「インデックス付け」されています。だから私が誰かの電話番号を調べたいなら、それはかなり簡単です。しかし、電話番号で検索したいとします(たとえば、特定の電話番号を持つ人を検索します)。電話番号で電話帳の「インデックスを再作成」できない限り、非常に長い時間がかかります。

次の例を考えてみます。米国のすべての郵便番号(> 33,000)のテーブルZIPと、関連情報(市、州、人口、収入の中央値など)があるとします。特定の郵便番号の情報を検索する場合、setkey(ZIP,zipcode)最初に検索(フィルター)を実行すると、約1000倍高速になります。

もう1つの利点は、結合に関係しています。データテーブルに人とその郵便番号のリスト( "PPL"と呼びます)があり、ZIPテーブルからの情報(都市、州など)を追加するとします。次のコードはそれを行います:

setkey(ZIP,zipcode)
setkey(PPL,zipcode)
full.info <- PPL[ZIP, nomatch=F]

これは、共通フィールド(郵便番号)に基づいて2つのテーブルからの情報を結合するという意味で「結合」です。このような非常に大きなテーブルでの結合は、データフレームでは非常に遅く、データテーブルでは非常に高速です。実際の例では、郵便番号の完全なテーブルでこのように20,000以上の結合を行わなければなりませんでした。データテーブルを使用すると、スクリプトの所要時間は約20分です。走る。2週間以上かかっていたので、データフレームを使用することすらしませんでした。

私見あなたはただ読むべきではなく、FAQとイントロの資料を研究すべきです。これを適用する実際の問題があるかどうかを理解する方が簡単です。

[@フランクのコメントへの返信]

日時:インデックス対ソート -への回答に基づいて、この質問、それが表示されsetkey(...)、実際に(例えば、物理的なソート)テーブル内の列を並べ替えず、データベース感覚でインデックスを作成しません。これにはいくつかの実際的な影響があります。1つは、テーブルでキーを設定してsetkey(...)から、キー列の値を変更すると、data.tableがテーブルをソートしないことを宣言するだけです(sorted属性をオフにすることにより)。(データベースで発生するように)適切なソート順を維持するために動的に再インデックスを行うことはありませ。また、を使用setky(DT,NULL)して「キーを削除」して、テーブルが元のソートされていない順序に復元されませ

再:フィルターと結合 -実際の違いは、フィルター処理は単一のデータセットからサブセットを抽出するのに対し、結合は共通のフィールドに基づいて2つのデータセットのデータを結合することです。結合にはさまざまな種類があります(内部、外部、左)。上記の例は内部結合であり(両方のテーブルに共通のキーを持つレコードのみが返されます)、これにはフィルタリングと多くの類似点があります。


1
+1。あなたの最初の文について...それはすでに正しくソートされていますか?そして、結合はフィルターの特別なケースではありませんか(または最初のステップとしてフィルターを取る操作)?「より良いフィルタリング」のように思えるのは、全体的なメリットです。
フランク

1
または、私が思うより良いスキャン。
ウェットフィート

1
@jlhowardありがとう。私の以前の考えでは、並べ替えはキーを設定することの利点の1つではなく(並べ替えを行う場合は並べ替えるだけでよいため)、setkey実際には行を並べ替えることができないためです。表示のみの場合は、「setkey」の前に表示されていた「真の」順序に従って最初の10行をどのように印刷しますか?私はsetkey(DT,NULL)これをしないと確信しています...(続き)
フランク

...(続き)また、パッケージのコードは確認していませんが、joinするX[Y,...]には、キーを使用してXの行を「フィルタリング」する必要があります。確かに、その後他のことが起こります(Yの列が利用可能になり、暗黙のby-without-byがあります)が、概念的に異なる利点としてはまだわかりません。あなたの答えは、あなたがやりたいかもしれない操作に関して書かれていると思いますが、区別が役立つかもしれません。
フランク・

1
@フランク- setkey(DT,NULL)キーを削除しますが、ソート順には影響しません。これについての質問をここに提起し。どれどれ。
jlhoward 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.