我读到: .SD 是 data.table ,其中包含每组的 x 数据的子集,不包括组列 . 当按 i 分组时,可以使用它,当按 by 分组,键入 by 和_ad hoc_ by 时
这是否意味着女儿 data.table 被保存在内存中以进行下一次操作?
2 回答
163
.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
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
# 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 返回长度为1的 logical ,因此 sapply 返回一个向量) .
不同的模型规范是稳健统计分析的核心特征 . 使用 Pitching 表中提供的一小组协变量,让's try and predict a pitcher' s 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)
# 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)]]
注意: .SD[1L] 目前由GForce(see also), data.table 内部优化,大大加快了最常见的分组操作,如 sum 或 mean - 有关详细信息,请参阅 ?GForce 并密切关注/语音支持功能改进请求更新此问题前:1,2,3,4,5,6
分组回归
回到上面关于 ERA 和 W 之间关系的调查,假设我们期望这种关系因团队而异(即,每个团队的坡度不同) . 我们可以轻松地重新运行此回归探索这种关系中的异质性如下(注意这种方法的标准误差通常是不正确的 - 规范 ERA ~ W*teamID 会更好 - 这种方法更容易阅读,系数也可以):
# 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')]
2 回答
.SD
代表类似“S
ubset ofD
ata.table”的内容 . 对于最初的"."
没有意义,除了它使得与用户定义的列名称发生冲突的可能性更小 .如果这是你的data.table:
这样做可能对你有帮助 see 是什么
.SD
:基本上,
by=y
语句将原始data.table分解为这两个子data.tables
并依次对它们进行操作 .
当它在任何一个上运行时,它允许您通过使用昵称/句柄/符号
.SD
来引用当前子data.table
. 这非常方便,因为您可以访问和操作列,就像您在命令行中使用单个data.table一样调用.SD
...除了在这里,data.table
将在每个子上执行这些操作data.table
由键组合定义,"pasting"它们一起返回并将结果返回到单个data.table
!考虑到这种情况经常发生,我认为除了Josh O'Brien上面给出的有用答案之外,还需要更多的阐述 .
除了通常由Josh引用/创建的 D ata首字母缩略词的 S ubset之外,我认为将"S"代表"Selfsame"或"Self-reference" -
.SD
也是有帮助的,其最基本的形式是对data.table
本身的反身性引用 - - 正如我们在下面的示例中看到的,这对于将"queries"(使用[
的提取/子集/等)链接在一起特别有用 . 特别是,这也意味着.SD
本身就是data.table
(需要注意的是它不允许使用:=
进行赋值) ..SD
的简单用法是用于列子集化(即,当指定.SDcols
时);我认为这个版本更容易理解,所以我们将在下面首先介绍 ..SD
在其第二次使用中的解释,分组场景(即,当指定by =
或keyby =
时)在概念上略有不同(尽管在核心它是相同的,因为,毕竟,非分组操作是边缘情况只用一组进行分组) .以下是我自己经常实现的一些说明性示例和其他一些用法示例:
加载拉曼数据
为了给这个更真实的感觉,而不是编制数据,让我们从
Lahman
加载一些关于棒球的数据集:裸体.SD
为了说明我对
.SD
的反身性质的意义,请考虑其最平庸的用法:也就是说,我们刚刚返回
Pitching
,即这是一种过于冗长的写作方式Pitching
或Pitching[]
:在子集化方面,
.SD
仍然是数据的子集,它只是一个简单的(集合本身) .列子集:.SDcols
影响
.SD
的第一种方法是使用.SDcols
参数将.SD
中包含的列限制为[
:这仅仅是为了说明而且非常无聊 . 但即使这种简单的用法也适用于各种高度有益/无处不在的数据操作操作:
列类型转换
列类型转换对于数据修改是生命中的事实 - 在撰写本文时,fwrite cannot automatically read Date or POSIXct columns,并且在
character
/factor
/numeric
之间来回转换是常见的 . 我们可以使用.SD
和.SDcols
批量转换此类列的组 .我们注意到以下列在
Teams
数据集中存储为character
:如果您对此处使用
sapply
感到困惑,请注意它与基数Rdata.frames
相同:理解这种语法的关键是回想一下
data.table
(以及data.frame
)可以被认为是list
,其中每个元素都是一列 - 因此,sapply
/lapply
将FUN
应用于每一列并将结果返回为sapply
/lapply
通常会(这里,FUN == is.character
返回长度为1的logical
,因此sapply
返回一个向量) .将这些列转换为
factor
的语法非常相似 - 只需添加:=
赋值运算符即可请注意,我们必须在括号
()
中包装fkt
以强制R将其解释为列名,而不是尝试将名称fkt
分配给RHS ..SDcols
(和:=
)的灵活性接受character
向量或列的integer
向量对于列名*的基于模式的转换,位置也可以派上用场 . 我们可以将所有factor
列转换为character
:然后将包含
team
的所有列转换回factor
:**明确使用列号(如
DT[ , (1) := rnorm(.N)]
)是不好的做法,如果列位置发生变化,可能导致代码随着时间的推移而无声地损坏 . 如果我们在创建编号索引时以及使用它时不保持智能/严格控制的顺序,即使隐式使用数字也是危险的 .控制模型的RHS
不同的模型规范是稳健统计分析的核心特征 . 使用
Pitching
表中提供的一小组协变量,让's try and predict a pitcher' s ERA(获得的运行平均值,衡量性能) .W
(wins)和ERA
之间的(线性)关系如何根据规范中包含的其他协变量而变化?这是一个利用
.SD
的力量的简短脚本,它探讨了这个问题:系数始终具有预期的符号(更好的投手倾向于拥有更多的胜利和更少的跑步),但是幅度可以根据我们控制的其他内容而有很大差异 .
有条件的连接
data.table
语法因其简单性和健壮性而美观 . 语法x[i]
灵活地处理两种常用的子集方法 - 当i
是logical
向量时,x[i]
将返回x
对应于i
是TRUE
的那些行 . 当i
是另一个data.table
时,执行join
(以普通形式,使用x
和x
的key
,否则,当指定on =
时,使用这些列的匹配) .这通常是很好的,但是当我们希望执行条件连接时不足,其中表之间的关系的确切性质取决于一列或多列中的行的一些特性 .
这个例子有点人为,但说明了这个想法;请参阅此处(1,2)了解更多信息 .
目标是在
Pitching
表中添加一列team_performance
,记录球队在每支球队中最佳投手的表现(排名)(以最低的ERA衡量,在至少有6场比赛的投手中) .请注意
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
变量的单个值(s ):在这种情况下,
.SD
本质上是多个 - 它指的是这些子中的每一个,一次一个(稍微更准确地说,.SD
的范围是单个子data.table
) . 这使我们能够在重新组装结果返回给我们之前简明地表达我们想要在每个子_724502上执行的操作 .这在各种设置中都很有用,其中最常见的是:
组子集
让我们在拉赫曼数据中获得每个团队最新的数据季节 . 这可以通过以下方式完成:
回想
.SD
本身是data.table
,.N
指的是组中的总行数(它等于每个组中的nrow(.SD)
),因此.SD[.N]
返回与每个teamID
相关联的最后一行的.SD
的全部内容 .另一个常见的版本是使用
.SD[1L]
代替获得每个组的第一个观察 .Group Optima
假设我们希望为每个团队返回最佳年份,以其得分总数来衡量(
R
;当然,我们可以轻松调整这个以指代其他指标) . 我们现在动态定义所需的索引,而不是从每个子data.table
中获取固定元素,如下所示:请注意,这种方法当然可以与
.SDcols
结合使用,以便仅为每个.SD
返回部分data.table
(需要注意的是.SDcols
应该在各个子集中修复)注意:
.SD[1L]
目前由GForce(see also),data.table
内部优化,大大加快了最常见的分组操作,如sum
或mean
- 有关详细信息,请参阅?GForce
并密切关注/语音支持功能改进请求更新此问题前:1,2,3,4,5,6分组回归
回到上面关于
ERA
和W
之间关系的调查,假设我们期望这种关系因团队而异(即,每个团队的坡度不同) . 我们可以轻松地重新运行此回归探索这种关系中的异质性如下(注意这种方法的标准误差通常是不正确的 - 规范ERA ~ W*teamID
会更好 - 这种方法更容易阅读,系数也可以):虽然存在相当多的异质性,但是在观察到的整体 Value 方面存在明显的集中
希望这已经阐明了
.SD
在data.table
中促进美观,高效的代码的力量!