แก้ไข:
กำหนดวิธีการดีที่ได้รับคำตอบนี้ที่ผมได้แปลงลงในบทความแพคเกจนี้สามารถใช้ได้ที่นี่
เมื่อพิจารณาว่าสิ่งนี้เกิดขึ้นบ่อยแค่ไหนฉันคิดว่าสิ่งนี้รับประกันได้มากกว่าคำอธิบายที่เป็นประโยชน์นอกเหนือจากคำตอบที่มีประโยชน์ของ Josh O'Brien ด้านบน
นอกจากตัวย่อS ubset ของD ata ที่มักจะอ้างถึง / สร้างโดย Josh ฉันคิดว่าการพิจารณา "S" เพื่อยืนสำหรับ "Selfsame" หรือ "Self-reference" .SD
นั้นเป็นประโยชน์ในการสะท้อนการอ้างอิงถึงdata.table
ตัวเอง - ดังที่เราจะเห็นในตัวอย่างด้านล่างสิ่งนี้มีประโยชน์อย่างยิ่งสำหรับการผูกมัด "ข้อความค้นหา" ร่วมกัน (การแยก / ส่วนย่อย / ฯลฯ โดยใช้[
) โดยเฉพาะอย่างยิ่งยังนี้หมายความว่า.SD
เป็นตัวเองdata.table
(มีข้อแม้ที่ว่ามันไม่ได้ช่วยให้ได้รับมอบหมายด้วย:=
)
การใช้งานที่ง่ายขึ้นของใช้.SD
สำหรับการแบ่งคอลัมน์ (เช่นเมื่อ.SDcols
มีการระบุ); ฉันคิดว่ารุ่นนี้ตรงไปตรงมามากกว่าที่จะเข้าใจดังนั้นเราจะพูดถึงเรื่องนี้ก่อน การตีความ.SD
ในการใช้งานครั้งที่สองสถานการณ์การจัดกลุ่ม (เช่นเมื่อby =
หรือkeyby =
มีการระบุ) แตกต่างกันเล็กน้อยในเชิงแนวคิด (แม้ว่าที่แกนกลางจะเหมือนกันเนื่องจากหลังจากทั้งหมดการดำเนินการที่ไม่จัดกลุ่มเป็นกรณีขอบของการจัดกลุ่มที่มีเพียง กลุ่มหนึ่ง)
นี่คือตัวอย่างที่แสดงให้เห็นและตัวอย่างของประเพณีที่ฉันใช้บ่อย ๆ :
กำลังโหลดข้อมูล Lahman
เพื่อให้ความรู้สึกในโลกแห่งความเป็นจริงมากกว่าการสร้างข้อมูลลองโหลดชุดข้อมูลบางอย่างเกี่ยวกับกีฬาเบสบอลจาก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
กุญแจสำคัญในการทำความเข้าใจกับไวยากรณ์นี้คือการจำได้ว่าdata.table
(เช่นเดียวกับdata.frame
) ถือได้ว่าเป็นlist
ที่ที่แต่ละองค์ประกอบเป็นคอลัมน์ - ดังนั้นsapply
/ lapply
นำFUN
ไปใช้กับแต่ละคอลัมน์และส่งกลับผลลัพธ์เป็นsapply
/ lapply
มักจะ (ที่นี่FUN == is.character
กลับlogical
ความยาว 1 ดังนั้นsapply
ส่งคืนเวกเตอร์)
ไวยากรณ์ในการแปลงคอลัมน์เหล่านี้factor
คล้ายกันมาก - เพียงแค่เพิ่มตัว:=
ดำเนินการที่ได้รับมอบหมาย
Teams[ , (fkt) := lapply(.SD, factor), .SDcols = fkt]
โปรดทราบว่าเราต้องห่อfkt
ในวงเล็บ()
เพื่อบังคับให้ R แปลความหมายนี้เป็นชื่อคอลัมน์แทนที่จะพยายามกำหนดชื่อfkt
ให้กับ RHS
ความยืดหยุ่นของ.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 ของแบบจำลอง
ข้อมูลจำเพาะของรุ่นที่หลากหลายนั้นเป็นคุณสมบัติหลักของการวิเคราะห์ทางสถิติ ลองและทำนาย ERA ของเหยือก (Earned Runs Average ซึ่งเป็นเครื่องวัดประสิทธิภาพ) โดยใช้ covariates ขนาดเล็กที่มีอยู่ในPitching
ตาราง ความสัมพันธ์ (เชิงเส้น) ระหว่างW
(ชนะ) และ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]
มีความยืดหยุ่นจับสองทั่วไปวิธีการ Subsetting - เมื่อi
เป็นlogical
เวกเตอร์x[i]
จะกลับมาแถวเหล่านั้นx
สอดคล้องกับที่i
เป็นTRUE
; เมื่อi
เป็นอีกdata.table
ที่join
จะดำเนินการ (ในรูปแบบธรรมดาโดยใช้key
ของx
และi
มิฉะนั้นเมื่อon =
มีการระบุโดยใช้การแข่งขันของคอลัมน์เหล่านั้น)
โดยทั่วไปแล้วจะดีมาก แต่จะสั้นเมื่อเราต้องการทำการเชื่อมแบบมีเงื่อนไขโดยธรรมชาติของความสัมพันธ์ระหว่างตารางนั้นขึ้นอยู่กับลักษณะของแถวในคอลัมน์หนึ่งคอลัมน์หรือมากกว่านั้น
ตัวอย่างนี้เป็นแบบตาด แต่แสดงให้เห็นถึงความคิด; ดูที่นี่ ( 1 , 2 ) เพื่อเพิ่มเติม
เป้าหมายคือการเพิ่มคอลัมน์team_performance
ลงในPitching
ตารางที่บันทึกประสิทธิภาพการทำงานของทีม (อันดับ) ของเหยือกที่ดีที่สุดในแต่ละทีม (วัดจาก ERA ที่ต่ำที่สุดในบรรดาเหยือกที่มีเกมที่บันทึกไว้อย่างน้อย 6 เกม)
# 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
s, หนึ่งที่-a-เวลา (เล็กน้อยขึ้นอย่างถูกต้องขอบเขตของ.SD
เป็นอนุกรรมการเดียวdata.table
) สิ่งนี้ช่วยให้เราสามารถแสดงการดำเนินการอย่างย่อที่เราต้องการดำเนินการในแต่ละรายการย่อยdata.table
ก่อนที่ผลลัพธ์ที่ประกอบใหม่จะถูกส่งกลับมาให้เรา
สิ่งนี้มีประโยชน์ในการตั้งค่าที่หลากหลายซึ่งส่วนใหญ่จะแสดงที่นี่:
การจัดกลุ่มเป็นกลุ่ม
มาดูฤดูกาลล่าสุดของข้อมูลสำหรับแต่ละทีมในข้อมูล Lahman สามารถทำได้ค่อนข้างง่ายด้วย:
# 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]
แทนการสังเกตครั้งแรกสำหรับแต่ละกลุ่ม
กลุ่ม Optima
สมมติว่าเราต้องการคืนปีที่ดีที่สุดสำหรับแต่ละทีมโดยวัดจากจำนวนการวิ่งทั้งหมด ( R
เราสามารถปรับได้อย่างง่ายดายเพื่ออ้างอิงตัวชี้วัดอื่น ๆ แน่นอน) แทนที่จะใช้องค์ประกอบคงที่จากแต่ละย่อยdata.table
ตอนนี้เรากำหนดดัชนีที่ต้องการแบบไดนามิกดังนี้:
Teams[ , .SD[which.max(R)], by = teamID]
โปรดทราบว่าวิธีการนี้สามารถใช้ร่วมกับ.SDcols
การส่งคืนเฉพาะบางส่วนของdata.table
แต่ละรายการ.SD
(ด้วยคำเตือนที่.SDcols
ควรได้รับการแก้ไขในส่วนย่อยต่างๆ)
หมายเหตุ : .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
!
?data.table
ได้รับการปรับปรุงใน v1.7.10 ขอบคุณคำถามนี้ ตอนนี้อธิบายชื่อ.SD
ตามคำตอบที่ยอมรับแล้ว