回答:
.SD
" S
ubset of D
ata.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
!
.SD
ですDT[,print(.SD),by=y]
。
DT[,print(.SD[,y]),by=y]
、の値にアクセスできることを示します。スコープの価値はどこからですか?現在の値であるb / cは利用できますか?y
.SD
y
by
.SD[,y]
規則的であるdata.table
のでので、サブセットy
の列ではありません.SD
、それはこのケースであり、それを呼び出した環境に見えますj
(の環境DT
クエリ)by
変数が利用可能です。そこに見つからない場合は、通常のRの方法で親とその親などを調べます。(まあ、継承された結合スコープを介しても、i
s がないため、これらの例では使用されていません)。
by=list(x,y,z)
はを意味しx
、y
でz
使用できますj
。一般的なアクセスについては、それら.BY
もラップされています。FAQ 2.10にはいくつかの歴史がありますが、いくつかの明確さをに追加することができます?data.table
。すばらしい、ドキュメントのヘルプは大歓迎です。プロジェクトに参加して直接変更したい場合はさらに良いでしょう。
この回答がどれほど好評だったかを考慮して、ここで入手できるパッケージビネットに変換しました
これがどれほどの頻度で発生するかを考えると、これは、上記のジョシュ・オブライエンによる有益な回答を超えて、もう少し説明が必要だと思います。
ジョシュによって通常引用/作成されるD ata頭字語のSサブセットに加えて、「S」を「Selfsame」または「Self-reference」を表すと考えることも役立つと思います。これは最も基本的な装いです。自身への再帰的参照 -以下の例でわかるように、これは「クエリ」(を使用した抽出/サブセット/など)を連鎖させる場合に特に役立ちます。特に、これはそれ自体がaであることも意味します(での割り当てが許可されていないという警告があります)。.SD
data.table
[
.SD
data.table
:=
のより簡単な使用法.SD
は、列のサブセット化です(つまり、.SDcols
が指定されている場合)。このバージョンは理解がはるかに簡単だと思うので、最初に以下で説明します。.SD
2番目の使用法のグループ化シナリオ(つまり、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
、すなわち、これは過度に執筆の途中冗長でしたPitching
かPitching[]
。
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
自動的に読み取ることはできずDate
POSIXct
、character
/ 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.frame
a list
も各要素が列であると見なすことができることを思い出すことです。したがって、sapply
/ lapply
はFUN
各列に適用され、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)]
することは悪い習慣であり、列の位置が変化すると、時間の経過とともにコードが暗黙のうちに破損する可能性があります。番号付きインデックスを作成するときと使用するときの順序付けをスマート/厳密に制御しないと、暗黙的に数値を使用することも危険です。
変動モデル仕様は、堅牢な統計分析のコア機能です。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)
係数は常に予想される符号を持っています(より良い投手はより多くの勝利とより少ないランを許容する傾向があります)が、マグニチュードは他に何を制御するかによって大幅に変化する可能性があります。
data.table
構文は、その単純さと堅牢性のために美しいです。構文はx[i]
柔軟に2つの共通のサブセットに近づくハンドル-場合i
であるlogical
ベクトルは、x[i]
のこれらの行を返すであろうx
場所に対応i
ですTRUE
。i
が別のdata.table
である場合、a join
が実行されます(プレーン形式でkey
はx
とのを使用します。i
それ以外の場合on =
は、指定された場合、それらの列の一致を使用します)。
これは一般的には優れていますが、条件付き結合を実行する場合には不十分です。テーブル間の関係の正確な性質は、1つ以上の列の行の特性によって異なります。
この例は少し工夫されていますが、アイデアを示しています。(ここを参照してください1、2以上のため)。
目標は、各チームの最高の投手のチームのパフォーマンス(ランク)を記録する列team_performance
をPitching
テーブルに追加することです(最低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]
もちろん、このアプローチを組み合わせて.SDcols
、data.table
forの一部のみを返すこともできます.SD
(.SDcols
さまざまなサブセットで修正する必要がある警告)
NB:.SD[1L]
現時点で最適化されているGForce
(も参照、)data.table
大規模のような最も一般的なグループ化された操作を高速内部sum
またはmean
-を参照して?GForce
詳細かつ上/音声目を離さないこの前の更新のための機能改善の要求のためのサポート:1、2、3、4、5、6
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
でしょう。
マットSDについて.SDについて話をした後、私はこれについてビデオを作りました、あなたはそれをYouTubeで見ることができます:https : //www.youtube.com/watch?v= DwEzQuYfMsI
?data.table
この質問のおかげで、v1.7.10で改善されました。それは今.SD
受け入れられた答えに従って名前を説明します。