Rのdata.tableにおける.SDの意味


172

.SD便利そうに見えますが、私はそれで何をしているか本当にわかりません それは何の略ですか?先行期間があるのはなぜですか(フルストップ)。使用するとどうなりますか?

読んだ: .SDあるdata.tableのサブセットを含むxグループ列(複数可)を除く各群についてのデータを、。でグループ化するとき、でグループ化するiときby、キー付きby、および_ad hoc_を使用できます。by

それは、娘data.tablesが次の操作のためにメモリに保持されることを意味しますか?


30
?data.tableこの質問のおかげで、v1.7.10で改善されました。それは今.SD受け入れられた答えに従って名前を説明します。
Matt Dowle

回答:


205

.SD" Subset of Data.table"のようなものを表します。最初のに意味はありません"."が、ユーザー定義の列名との衝突が発生する可能性がさらに低くなります。

これがdata.tableの場合:

DT = data.table(x=rep(c("a","b","c"),each=2), y=c(1,3), v=1:6)
setkey(DT, y)
DT
#    x y v
# 1: a 1 1
# 2: b 1 3
# 3: c 1 5
# 4: a 3 2
# 5: b 3 4
# 6: c 3 6

これを行うと、次のものを確認するのに役立ちます.SD

DT[ , .SD[ , paste(x, v, sep="", collapse="_")], by=y]
#    y       V1
# 1: 1 a1_b3_c5
# 2: 3 a2_b4_c6

基本的に、by=yステートメントは元のdata.tableをこれらの2つのサブdata.tables

DT[ , print(.SD), by=y]
# <1st sub-data.table, called '.SD' while it's being operated on>
#    x v
# 1: a 1
# 2: b 3
# 3: c 5
# <2nd sub-data.table, ALSO called '.SD' while it's being operated on>
#    x v
# 1: a 2
# 2: b 4
# 3: c 6
# <final output, since print() doesn't return anything>
# Empty data.table (0 rows) of 1 col: y

そしてそれらを順番に操作します。

どちらかで動作している間はdata.table、ニックネーム/ハンドル/シンボルを使用して現在のサブを参照できます.SD。あなたが呼ばれる単一data.tableで作業をコマンドラインで座っていたかのように、列にアクセスして操作できるように、非常に便利だと.SD、...ことを除いて、ここでdata.tableすべての単一のサブにこれらの操作を行いますdata.tableによって定義されましたキーの組み合わせ、それらを一緒に「貼り付け」、単一の結果を返すdata.table


12
はい、正確に。別の見方は.SDですDT[,print(.SD),by=y]
Matt Dowle、2011

6
@MatthewDowle-ここにいる間、あなたへの質問。することは、の一部ではないにもかかわらずDT[,print(.SD[,y]),by=y]、の値にアクセスできることを示します。スコープの価値はどこからですか?現在の値であるb / cは利用できますか?y.SDyby
Josh O'Brien

8
@ジョシュニース。はい。.SD[,y]規則的であるdata.tableのでので、サブセットyの列ではありません.SD、それはこのケースであり、それを呼び出した環境に見えますj(の環境DTクエリ)by変数が利用可能です。そこに見つからない場合は、通常のRの方法で親とその親などを調べます。(まあ、継承された結合スコープを介しても、is がないため、これらの例では使用されていません)。
Matt Dowle、2011

1
@Josh FAQ 2.8にはこれについての詳細があり、改善が必要な場合があります(提案を歓迎します)。
Matt Dowle、2011

3
@Joshグループ変数はjでも使用でき、長さが1であるため、by=list(x,y,z)はを意味しxyz使用できますj。一般的なアクセスについては、それら.BYもラップされています。FAQ 2.10にはいくつかの歴史がありますが、いくつかの明確さをに追加することができます?data.table。すばらしい、ドキュメントのヘルプは大歓迎です。プロジェクトに参加して直接変更したい場合はさらに良いでしょう。
Matt Dowle、2011

96

編集:

この回答がどれほど好評だったかを考慮して、ここで入手できるパッケージビネットに変換しました


これがどれほどの頻度で発生するかを考えると、これは、上記のジョシュ・オブライエンによる有益な回答を超えて、もう少し説明が必要だと思います。

ジョシュによって通常引用/作成されるD ata頭字語のSサブセットに加えて、「S」を「Selfsame」または「Self-reference」を表すと考えることも役立つと思います。これは最も基本的な装いです。自身への再帰的参照 -以下の例でわかるように、これは「クエリ」(を使用した抽出/サブセット/など)を連鎖させる場合に特に役立ちます。特に、これはそれ自体がaであることも意味します(での割り当てが許可されていないという警告があります)。.SDdata.table[.SDdata.table:=

のより簡単な使用法.SDは、列のサブセット化です(つまり、.SDcolsが指定されている場合)。このバージョンは理解がはるかに簡単だと思うので、最初に以下で説明します。.SD2番目の使用法のグループ化シナリオ(つまり、by =またはkeyby =指定されたとき)の解釈は、概念的には少し異なります(結局のところ、グループ化されていない操作は、 1つのグループ)。


以下に、私自身が頻繁に実装するいくつかの例とその他の使用例を示します。

ラーマンデータの読み込み

データを作成するのではなく、より現実的な感覚を与えるために、baseballに関するいくつかのデータセットを以下からロードしてみましょうLahman

library(data.table) 
library(magrittr) # some piping can be beautiful
library(Lahman)
Teams = as.data.table(Teams)
# *I'm selectively suppressing the printed output of tables here*
Teams
Pitching = as.data.table(Pitching)
# subset for conciseness
Pitching = Pitching[ , .(playerID, yearID, teamID, W, L, G, ERA)]
Pitching

.SD

の再帰的な性質について私が何を意味するのかを説明するために.SD、その最もありふれた使用法を考えてみましょう。

Pitching[ , .SD]
#         playerID yearID teamID  W  L  G   ERA
#     1: bechtge01   1871    PH1  1  2  3  7.96
#     2: brainas01   1871    WS3 12 15 30  4.50
#     3: fergubo01   1871    NY2  0  0  1 27.00
#     4: fishech01   1871    RC1  4 16 24  4.35
#     5: fleetfr01   1871    NY2  0  1  1 10.00
#    ---                                       
# 44959: zastrro01   2016    CHN  1  0  8  1.13
# 44960: zieglbr01   2016    ARI  2  3 36  2.82
# 44961: zieglbr01   2016    BOS  2  4 33  1.52
# 44962: zimmejo02   2016    DET  9  7 19  4.87
# 44963:  zychto01   2016    SEA  1  0 12  3.29

、私達はちょうど戻ってきていることPitching、すなわち、これは過度に執筆の途中冗長でしたPitchingPitching[]

identical(Pitching, Pitching[ , .SD])
# [1] TRUE

サブセット化に関しては.SD、データのサブセットですが、それは些細なもの(セット自体)です。

列のサブセット化: .SDcols

に影響を与える最初の方法は、への引数の使用に含まれる.SDを制限することです。.SD.SDcols[

Pitching[ , .SD, .SDcols = c('W', 'L', 'G')]
#         W  L  G
#     1:  1  2  3
#     2: 12 15 30
#     3:  0  0  1
#     4:  4 16 24
#     5:  0  1  1
# ---         
# 44959:  1  0  8
# 44960:  2  3 36
# 44961:  2  4 33
# 44962:  9  7 19
# 44963:  1  0 12

これは単に説明のためであり、かなり退屈でした。しかし、この単純な使用法でさえ、さまざまな非常に有益な、ユビキタスなデータ操作操作に役立ちます。

列タイプの変換

列タイプの変換は、データを変更する上での現実です。これを書いている時点では、列をfwrite自動的に読み取ることはできずDatePOSIXctcharacter/ factor/ 間での変換numericは一般的です。を使用.SD.SDcolsて、そのような列のグループをバッチ変換できます。

次の列がデータセットと同じようcharacterに保存されていることがわかりますTeams

# see ?Teams for explanation; these are various IDs
#   used to identify the multitude of teams from
#   across the long history of baseball
fkt = c('teamIDBR', 'teamIDlahman45', 'teamIDretro')
# confirm that they're stored as `character`
Teams[ , sapply(.SD, is.character), .SDcols = fkt]
# teamIDBR teamIDlahman45    teamIDretro 
#     TRUE           TRUE           TRUE 

sapplyここでの使用に混乱している場合は、ベースRと同じであることに注意してくださいdata.frames

setDF(Teams) # convert to data.frame for illustration
sapply(Teams[ , fkt], is.character)
# teamIDBR teamIDlahman45    teamIDretro 
#     TRUE           TRUE           TRUE 
setDT(Teams) # convert back to data.table

この構文を理解するための鍵は、a data.tableだけでなくdata.framea listも各要素が列であると見なすことができることを思い出すことです。したがって、sapply/ lapplyFUN列に適用され、sapply/とlapply通常どおりに結果をFUN == is.character返します(ここではlogical長さ1なのでsapply、ベクトルを返します)。

これらの列を変換する構文factorは非常に似ています- :=代入演算子を追加するだけです

Teams[ , (fkt) := lapply(.SD, factor), .SDcols = fkt]

RHSに名前を割り当てようとする代わりに、Rにこれを列名として解釈させるにfktは、括弧で囲む必要があることに注意してください。()fkt

柔軟性.SDcols(と:=受け入れるように)characterベクトルまたはinteger列位置のベクターはまた、*列名のパターンベースの変換のために便利になることができます。すべてのfactor列をcharacter次のように変換できます。

fkt_idx = which(sapply(Teams, is.factor))
Teams[ , (fkt_idx) := lapply(.SD, as.character), .SDcols = fkt_idx]

そして、含まれているすべての列の変換teamに背をfactor

team_idx = grep('team', names(Teams), value = TRUE)
Teams[ , (team_idx) := lapply(.SD, factor), .SDcols = team_idx]

** 列番号(など)を明示的に使用DT[ , (1) := rnorm(.N)]することは悪い習慣であり、列の位置が変化すると、時間の経過とともにコードが暗黙のうちに破損する可能性があります。番号付きインデックスを作成するときと使用するときの順序付けをスマート/厳密に制御しないと、暗黙的に数値を使用することも危険です。

モデルのRHSの制御

変動モデル仕様は、堅牢な統計分析のコア機能です。Pitching表で利用可能な共変量の小さなセットを使用して、ピッチャーのERA(アーンドランの平均、パフォーマンスの尺度)を試してみましょう。W(wins)間の(線形)関係ERAは、仕様に含まれている他の共変量によってどのように異なりますか?

以下.SDは、この質問を探求する力を活用した短いスクリプトです。

# this generates a list of the 2^k possible extra variables
#   for models of the form ERA ~ G + (...)
extra_var = c('yearID', 'teamID', 'G', 'L')
models =
  lapply(0L:length(extra_var), combn, x = extra_var, simplify = FALSE) %>%
  unlist(recursive = FALSE)

# here are 16 visually distinct colors, taken from the list of 20 here:
#   https://sashat.me/2017/01/11/list-of-20-simple-distinct-colors/
col16 = c('#e6194b', '#3cb44b', '#ffe119', '#0082c8', '#f58231', '#911eb4',
          '#46f0f0', '#f032e6', '#d2f53c', '#fabebe', '#008080', '#e6beff',
          '#aa6e28', '#fffac8', '#800000', '#aaffc3')

par(oma = c(2, 0, 0, 0))
sapply(models, function(rhs) {
  # using ERA ~ . and data = .SD, then varying which
  #   columns are included in .SD allows us to perform this
  #   iteration over 16 models succinctly.
  #   coef(.)['W'] extracts the W coefficient from each model fit
  Pitching[ , coef(lm(ERA ~ ., data = .SD))['W'], .SDcols = c('W', rhs)]
}) %>% barplot(names.arg = sapply(models, paste, collapse = '/'),
               main = 'Wins Coefficient with Various Covariates',
               col = col16, las = 2L, cex.names = .8)

WのOLS係数に適合、さまざまな仕様

係数は常に予想される符号を持っています(より良い投手はより多くの勝利とより少ないランを許容する傾向があります)が、マグニチュードは他に何を制御するかによって大幅に変化する可能性があります。

条件付き結合

data.table構文は、その単純さと堅牢性のために美しいです。構文はx[i]柔軟に2つの共通のサブセットに近づくハンドル-場合iであるlogicalベクトルは、x[i]のこれらの行を返すであろうx場所に対応iですTRUEi別のdata.tableである場合、a joinが実行されます(プレーン形式でkeyxとのを使用します。iそれ以外の場合on =は、指定された場合、それらの列の一致を使用します)。

これは一般的には優れていますが、条件付き結合を実行する場合には不十分です。テーブル間の関係の正確な性質は、1つ以上の列の行の特性によって異なります。

この例は少し工夫されていますが、アイデアを示しています。(ここを参照してください12以上のため)。

目標は、各チームの最高の投手のチームのパフォーマンス(ランク)を記録する列team_performancePitchingテーブルに追加することです(最低6試合記録された投手のうち、最低のERAで測定)。

# to exclude pitchers with exceptional performance in a few games,
#   subset first; then define rank of pitchers within their team each year
#   (in general, we should put more care into the 'ties.method'
Pitching[G > 5, rank_in_team := frank(ERA), by = .(teamID, yearID)]
Pitching[rank_in_team == 1, team_performance := 
           # this should work without needing copy(); 
           #   that it doesn't appears to be a bug: 
           #   https://github.com/Rdatatable/data.table/issues/1926
           Teams[copy(.SD), Rank, .(teamID, yearID)]]

x[y]構文はnrow(y)値を返すことに注意してください。これが.SD右側にある理由ですTeams[.SD]:=この場合のRHS はnrow(Pitching[rank_in_team == 1])値を必要とするためです)。

グループ化された.SD操作

多くの場合、データに対してグループレベルでなんらかの操作を実行する必要があります。私たちがby =(またはkeyby =)を指定するとき、data.tableプロセスjがあなたのdata.tableことを多くのコンポーネントのサブに分割されていると考えることで、プロセスがどうなるかについてのメンタルモデルはdata.table、それぞれがby変数の単一の値に対応します。

グループ化の図解

この場合、.SDは本質的に複数です-これらのサブdata.tableのそれぞれを1つずつ参照します(少し正確に言えば、スコープ.SDは単一のサブですdata.table)。これにより、再構成された結果が返される前に、各サブdata.tableに対して実行する操作を簡潔に表現できます。

これは、さまざまな設定で役立ちます。最も一般的な設定を次に示します。

グループのサブセット化

ラーマンデータで各チームの最新シーズンのデータを取得しましょう。これは非常に簡単に行うことができます:

# the data is already sorted by year; if it weren't
#   we could do Teams[order(yearID), .SD[.N], by = teamID]
Teams[ , .SD[.N], by = teamID]

リコールそれ.SD自体でありdata.table、それは.N基(それに等しいです内の行の総数を指すnrow(.SD)ように、各グループ内で).SD[.N]返すの全体.SD各々に関連付けられた最終行のをteamID

これの別の一般的なバージョンは、.SD[1L]代わりにを使用して、各グループの最初の観測値を取得することです。

グループオプティマ

スコアリングされた実行の合計数で測定すると、各チームの最高の年を返したいと想定しRます(もちろん、これを簡単に調整して、他のメトリックを参照することもできます)。各サブから固定要素を取得する代わりにdata.table、目的のインデックスを次のように動的に定義します。

Teams[ , .SD[which.max(R)], by = teamID]

もちろん、このアプローチを組み合わせて.SDcolsdata.tableforの一部のみを返すこともできます.SD.SDcolsさまざまなサブセットで修正する必要がある警告)

NB.SD[1L]現時点で最適化されているGForceも参照、)data.table大規模のような最も一般的なグループ化された操作を高速内部sumまたはmean-を参照して?GForce詳細かつ上/音声目を離さないこの前の更新のための機能改善の要求のためのサポート:123456

グループ化された回帰

ERAとの関係に関する上記の問い合わせに戻り、Wこの関係がチームごとに異なると予想するとします(つまり、チームごとに異なる勾配があります)。次のように、この回帰を簡単に再実行して、この関係の不均一性を調べることができます(このアプローチの標準エラーは一般に正しくないことに注意してください-仕様ERA ~ W*teamIDは改善されます-このアプローチは読みやすく、係数はOKです) :

# use the .N > 20 filter to exclude teams with few observations
Pitching[ , if (.N > 20) .(w_coef = coef(lm(ERA ~ W))['W']), by = teamID
          ][ , hist(w_coef, 20, xlab = 'Fitted Coefficient on W',
                    ylab = 'Number of Teams', col = 'darkgreen',
                    main = 'Distribution of Team-Level Win Coefficients on ERA')]

当てはめられた係数の分布

かなりの量の不均一性がありますが、観測された全体的な値の周りに明確な集中があります

うまくいけば、これがで.SD美しく効率的なコードを促進する力を解明したことdata.tableでしょう。


5
良い説明。ただ1つのコメント:Teams [、.SD [which.max(R)]の代わりに= teamID]を使用すると、高速なdata.table注文関数を活用できます。Teams[order(teamID、-R)、.SD [ 1L]、keyby = teamID]、これはより速いはずです。
bartleby

@bartlebyありがとう、確かにこれは正しいですが、一般的なアプローチは、たとえばグループに依存する数量で注文する必要がある場合など、知っておくと役に立ちます。多分私は例を変更します。
MichaelChirico

3

マットSDについて.SDについて話をした後、私はこれについてビデオを作りました、あなたはそれをYouTubeで見ることができます:https : //www.youtube.com/watch?v= DwEzQuYfMsI


2
残念ながら、Stack Overflowは通常、本質的に外部リンクで構成される回答を歓迎しません。テキストとコード情報をさらに編集したいですか?Fyi Mattが動画をwikiに追加しました:github.com/Rdatatable/data.table/wiki/Presentations
Frank
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.