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" - .SD
dalam kedok paling mendasar referensi refleksif terhadap data.table
dirinya 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 .SD
itu sendiri adata.table
(dengan peringatan bahwa itu tidak memungkinkan penugasan dengan :=
).
Penggunaan sederhana .SD
adalah untuk subset kolom (yaitu, kapan .SDcols
ditentukan); Saya pikir versi ini jauh lebih mudah untuk dipahami, jadi kami akan membahasnya terlebih dahulu di bawah ini. Penafsiran .SD
dalam 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 Pitching
atau Pitching[]
:
identical(Pitching, Pitching[ , .SD])
# [1] TRUE
Dalam hal subsetting, .SD
masih merupakan himpunan bagian dari data, itu hanya sepele (himpunan itu sendiri).
Berlangganan Kolom: .SDcols
Cara pertama untuk memengaruhi apa .SD
adalah membatasi kolom yang terkandung dalam .SD
menggunakan .SDcols
argumen[
:
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, fwrite
tidak dapat secara otomatis membaca Date
atau POSIXct
kolom , dan konversi bolak-balik antara character
/ factor
/ numeric
adalah umum. Kita bisa menggunakan .SD
dan.SDcols
untuk mengonversi grup dalam kolom tersebut secara berkelompok.
Kami perhatikan bahwa kolom berikut disimpan seperti character
dalam Teams
kumpulan 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 sapply
sini, 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 list
mana setiap elemen adalah kolom - dengan demikian, sapply
/ lapply
berlaku FUN
untuk setiap kolom dan mengembalikan hasilnya seperti sapply
/ lapply
biasanya (di sini, FUN == is.character
mengembalikan a logical
dengan panjang 1, jadi sapply
mengembalikan vektor).
Sintaks untuk mengonversi kolom factor
ini sangat mirip - cukup tambahkan :=
operator penugasan
Teams[ , (fkt) := lapply(.SD, factor), .SDcols = fkt]
Perhatikan bahwa kita harus membungkus fkt
tanda kurung ()
untuk memaksa R untuk menafsirkan ini sebagai nama kolom, alih-alih mencoba untuk menetapkan nama fkt
ke RHS.
Fleksibilitas .SDcols
(dan :=
) untuk menerima character
vektor atau suatu integer
vektor posisi kolom juga dapat berguna untuk konversi berbasis pola nama kolom *. Kami dapat mengonversi semua factor
kolom 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 team
kembali 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 Pitching
tabel. Bagaimana hubungan (linear) antara W
(menang) dan ERA
bervariasi tergantung pada kovariat mana yang termasuk dalam spesifikasi?
Berikut ini skrip pendek yang memanfaatkan kekuatan .SD
yang 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.table
sintaksinya indah karena kesederhanaan dan kekokohannya. Sintaks x[i]
fleksibel menangani dua umum pendekatan untuk subsetting - ketika i
adalah logical
vektor, x[i]
akan kembali orang-baris x
yang sesuai dengan mana i
adalah TRUE
; kapan i
yang laindata.table
, a join
dilakukan (dalam bentuk polos, menggunakan key
s dari x
dan 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_performance
pada Pitching
tabel 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 .SD
ada di sebelah kanan Teams[.SD]
(karena RHS :=
dalam kasus ini membutuhkan nrow(Pitching[rank_in_team == 1])
nilai.
.SD
Operasi 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.table
proses j
adalah menganggap Anda data.table
sebagai dibagi menjadi banyak komponen data.table
, yang masing-masing sesuai dengan nilai tunggal by
variabel Anda :
Dalam hal ini, .SD
sifatnya multipel - mengacu pada masing-masing sub-satu ini data.table
, satu per satu (sedikit lebih akurat, cakupannya .SD
adalah 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 .SD
sendiri adalah a data.table
, dan yang .N
mengacu 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 .SDcols
hanya mengembalikan porsi data.table
masing-masing .SD
(dengan peringatan yang .SDcols
harus diperbaiki di berbagai subset)
NB : .SD[1L]
saat ini dioptimalkan oleh GForce
( lihat juga ), data.table
internal yang secara besar-besaran mempercepat operasi yang dikelompokkan yang paling umum seperti sum
atau mean
- lihat ?GForce
untuk 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 ERA
dan 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*teamID
akan 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 .SD
dalam memfasilitasi kode yang indah dan efisien data.table
!
?data.table
ditingkatkan di v1.7.10, berkat pertanyaan ini. Sekarang menjelaskan nama.SD
sesuai jawaban yang diterima.