Edit:
Mengingat seberapa baik jawaban ini diterima, saya telah mengubahnya menjadi sketsa paket yang sekarang tersedia di sini
Mengingat seberapa sering ini muncul, saya pikir ini memerlukan penjelasan yang sedikit lebih banyak, di luar jawaban yang bermanfaat yang diberikan oleh Josh O'Brien di atas.
Selain S ubset dari akronim D ata yang biasanya dikutip / dibuat oleh Josh, saya pikir itu juga membantu untuk mempertimbangkan "S" untuk singkatan "Selfsame" atau "Self-reference" - .SDdalam kedok paling mendasar referensi refleksif terhadap data.tabledirinya sendiri - seperti yang akan kita lihat dalam contoh di bawah ini, ini sangat membantu untuk merantai bersama "pertanyaan" (ekstraksi / subset / penggunaan menggunakan dll [). Secara khusus, ini juga berarti bahwa .SDitu sendiri adata.table (dengan peringatan bahwa itu tidak memungkinkan penugasan dengan :=).
Penggunaan sederhana .SDadalah untuk subset kolom (yaitu, kapan .SDcolsditentukan); Saya pikir versi ini jauh lebih mudah untuk dipahami, jadi kami akan membahasnya terlebih dahulu di bawah ini. Penafsiran .SDdalam penggunaan kedua, skenario pengelompokan (yaitu, ketika by =atau keyby =ditentukan), sedikit berbeda, secara konseptual (meskipun pada intinya sama, karena, bagaimanapun, operasi non-kelompok adalah kasus tepi pengelompokan dengan hanya satu kelompok).
Berikut adalah beberapa contoh ilustrasi dan beberapa contoh penggunaan lain yang sering saya terapkan:
Memuat Data Lahman
Untuk memberikan kesan yang lebih nyata, daripada membuat data, mari kita memuat beberapa set data tentang baseball dari 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
Telanjang .SD
Untuk menggambarkan apa yang saya maksudkan tentang sifat refleksif .SD, pertimbangkan penggunaannya yang paling dangkal:
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
Artinya, kami baru saja kembali Pitching , yaitu, ini adalah cara penulisan yang terlalu bertele-tele Pitchingatau Pitching[]:
identical(Pitching, Pitching[ , .SD])
# [1] TRUE
Dalam hal subsetting, .SDmasih merupakan himpunan bagian dari data, itu hanya sepele (himpunan itu sendiri).
Berlangganan Kolom: .SDcols
Cara pertama untuk memengaruhi apa .SDadalah membatasi kolom yang terkandung dalam .SDmenggunakan .SDcolsargumen[ :
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
Ini hanya untuk ilustrasi dan cukup membosankan. Tetapi bahkan penggunaan sederhana ini cocok untuk berbagai operasi manipulasi data yang sangat bermanfaat / di mana-mana:
Konversi Jenis Kolom
Konversi jenis kolom adalah fakta kehidupan untuk munging data - pada tulisan ini, fwritetidak dapat secara otomatis membaca Dateatau POSIXctkolom , dan konversi bolak-balik antara character/ factor/ numericadalah umum. Kita bisa menggunakan .SDdan.SDcols untuk mengonversi grup dalam kolom tersebut secara berkelompok.
Kami perhatikan bahwa kolom berikut disimpan seperti characterdalam Teamskumpulan data:
# 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
Jika Anda bingung dengan penggunaan di sapplysini, perhatikan bahwa itu sama seperti untuk basis 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
Kunci untuk memahami sintaks ini adalah untuk mengingat bahwa a data.table(dan juga a data.frame) dapat dianggap sebagai di listmana setiap elemen adalah kolom - dengan demikian, sapply/ lapplyberlaku FUNuntuk setiap kolom dan mengembalikan hasilnya seperti sapply/ lapplybiasanya (di sini, FUN == is.charactermengembalikan a logicaldengan panjang 1, jadi sapplymengembalikan vektor).
Sintaks untuk mengonversi kolom factorini sangat mirip - cukup tambahkan :=operator penugasan
Teams[ , (fkt) := lapply(.SD, factor), .SDcols = fkt]
Perhatikan bahwa kita harus membungkus fkttanda kurung ()untuk memaksa R untuk menafsirkan ini sebagai nama kolom, alih-alih mencoba untuk menetapkan nama fktke RHS.
Fleksibilitas .SDcols(dan :=) untuk menerima charactervektor atau suatu integervektor posisi kolom juga dapat berguna untuk konversi berbasis pola nama kolom *. Kami dapat mengonversi semua factorkolom menjadi character:
fkt_idx = which(sapply(Teams, is.factor))
Teams[ , (fkt_idx) := lapply(.SD, as.character), .SDcols = fkt_idx]
Dan kemudian mengonversi semua kolom yang berisi teamkembali ke factor:
team_idx = grep('team', names(Teams), value = TRUE)
Teams[ , (team_idx) := lapply(.SD, factor), .SDcols = team_idx]
** Secara eksplisit menggunakan nomor kolom (seperti DT[ , (1) := rnorm(.N)]) adalah praktik yang buruk dan dapat menyebabkan kode rusak secara diam-diam seiring waktu jika posisi kolom berubah. Bahkan secara implisit menggunakan angka bisa berbahaya jika kita tidak tetap pintar / ketat mengontrol urutan ketika kita membuat indeks bernomor dan kapan kita menggunakannya.
Mengontrol RHS Model
Memvariasikan spesifikasi model adalah fitur inti dari analisis statistik yang kuat. Mari kita coba dan prediksi ERA pitcher (Earned Runs Average, ukuran kinerja) menggunakan seperangkat kovariat kecil yang tersedia di Pitchingtabel. Bagaimana hubungan (linear) antara W(menang) dan ERAbervariasi tergantung pada kovariat mana yang termasuk dalam spesifikasi?
Berikut ini skrip pendek yang memanfaatkan kekuatan .SDyang mengeksplorasi pertanyaan ini:
# 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)

Koefisien selalu memiliki tanda yang diharapkan (pitcher yang lebih baik cenderung memiliki lebih banyak kemenangan dan lebih sedikit run yang diizinkan), tetapi besarnya dapat bervariasi secara substansial tergantung pada apa lagi yang kami kontrol.
Bergabung Bersyarat
data.tablesintaksinya indah karena kesederhanaan dan kekokohannya. Sintaks x[i]fleksibel menangani dua umum pendekatan untuk subsetting - ketika iadalah logicalvektor, x[i]akan kembali orang-baris xyang sesuai dengan mana iadalah TRUE; kapan iyang laindata.table , a joindilakukan (dalam bentuk polos, menggunakan keys dari xdan i, jika tidak, ketika on =ditentukan, menggunakan kecocokan dari kolom-kolom itu).
Ini bagus secara umum, tetapi gagal ketika kita ingin melakukan join bersyarat , di mana sifat tepat hubungan di antara tabel tergantung pada beberapa karakteristik baris dalam satu atau beberapa kolom.
Contoh ini sedikit dibuat-buat, tetapi menggambarkan ide; lihat di sini ( 1 , 2 ) untuk lebih lanjut.
Tujuannya adalah untuk menambahkan kolom team_performancepada Pitchingtabel yang mencatat kinerja tim (peringkat) dari pitcher terbaik di setiap tim (yang diukur dengan ERA terendah, di antara pitcher dengan setidaknya 6 pertandingan yang direkam).
# 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)]]
Perhatikan bahwa x[y]sintaks mengembalikan nrow(y)nilai, itulah sebabnya .SDada di sebelah kanan Teams[.SD](karena RHS :=dalam kasus ini membutuhkan nrow(Pitching[rank_in_team == 1])nilai.
.SDOperasi yang dikelompokkan
Seringkali, kami ingin melakukan beberapa operasi pada data kami di tingkat grup . Ketika kami menentukan by =(atau keyby =), model mental untuk apa yang terjadi ketika data.tableproses jadalah menganggap Anda data.tablesebagai dibagi menjadi banyak komponen data.table, yang masing-masing sesuai dengan nilai tunggal byvariabel Anda :

Dalam hal ini, .SDsifatnya multipel - mengacu pada masing-masing sub-satu ini data.table, satu per satu (sedikit lebih akurat, cakupannya .SDadalah satu sub- data.table). Ini memungkinkan kami untuk secara singkat mengekspresikan operasi yang ingin kami lakukan pada setiap sub-data.table sebelum hasil yang dirakit dikembalikan kepada kami.
Ini berguna dalam berbagai pengaturan, yang paling umum disajikan di sini:
Berlangganan Grup
Mari kita dapatkan data musim terbaru untuk setiap tim di data Lahman. Ini dapat dilakukan cukup sederhana dengan:
# 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]
Ingat itu .SDsendiri adalah a data.table, dan yang .Nmengacu pada jumlah total baris dalam suatu kelompok (sama dengan nrow(.SD)dalam setiap grup), jadi .SD[.N]kembalikan keseluruhan.SD untuk baris terakhir yang terkait dengan masing-masing teamID.
Versi umum lain dari ini adalah menggunakan .SD[1L]sebagai gantinya untuk mendapatkan pengamatan pertama untuk setiap kelompok.
Optima Grup
Misalkan kita ingin mengembalikan tahun terbaik untuk masing-masing tim, yang diukur dengan jumlah total skor lari mereka ( R; kita dapat dengan mudah menyesuaikan ini dengan merujuk pada metrik lain, tentu saja). Alih-alih mengambil elemen tetap dari setiap sub- data.table, kami sekarang mendefinisikan indeks yang diinginkan secara dinamis sebagai berikut:
Teams[ , .SD[which.max(R)], by = teamID]
Perhatikan bahwa pendekatan ini tentu saja dapat digabungkan dengan .SDcolshanya mengembalikan porsi data.tablemasing-masing .SD(dengan peringatan yang .SDcolsharus diperbaiki di berbagai subset)
NB : .SD[1L]saat ini dioptimalkan oleh GForce( lihat juga ), data.tableinternal yang secara besar-besaran mempercepat operasi yang dikelompokkan yang paling umum seperti sumatau mean- lihat ?GForceuntuk detail lebih lanjut dan perhatikan / dukungan suara untuk permintaan peningkatan fitur untuk pembaruan di bagian depan ini: 1 , 2 , 3 , 4 , 5 , 6
Regresi Kelompok
Kembali ke pertanyaan di atas mengenai hubungan antara ERAdan W, misalkan kita mengharapkan hubungan ini berbeda oleh tim (yaitu, ada kemiringan yang berbeda untuk setiap tim). Kita dapat dengan mudah menjalankan kembali regresi ini untuk mengeksplorasi heterogenitas dalam hubungan ini sebagai berikut (mencatat bahwa kesalahan standar dari pendekatan ini umumnya salah - spesifikasinya ERA ~ W*teamIDakan lebih baik - pendekatan ini lebih mudah dibaca - koefisiennya lebih baik) :
# 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')]

Meskipun ada cukup heterogenitas, ada konsentrasi yang berbeda di sekitar nilai keseluruhan yang diamati
Semoga ini telah menjelaskan kekuatan .SDdalam memfasilitasi kode yang indah dan efisien data.table!
?data.tableditingkatkan di v1.7.10, berkat pertanyaan ini. Sekarang menjelaskan nama.SDsesuai jawaban yang diterima.