r - .SD 在 data.table 操作中的兴趣

标签 r dataframe data.table

我有一个关于 data.table 的问题。 我喜欢它,但我认为我曾经/现在有时会滥用 .SD,我希望能澄清一下何时可以在 data.table 中使用它。

以下是我认为自己滥用 .SD 的两个示例:

第一个discussed here (感谢亨利的评论)

library(microbenchmark)
library(data.table)

DTlength <- 2000
DT <-
  data.table(
    id = rep(sapply(combn(LETTERS, 6, simplify = FALSE), function(x) {
      paste(x, collapse = "")
    }), each = 4)[1:DTlength],
    replicate(10, sample(1001, DTlength, replace = TRUE)),
    Answer = sample(c("Yes", "No"), DTlength, TRUE)
  )

microbenchmark(
  "without SD" = {
    b <- DT[, Answer[1], by = id][, V1]
  },
  "without SD alternative" = {
    b <- DT[DT[, .I[1], by = id][, V1], Answer]
  },
  "with SD" = {
    b <- DT[, .SD[1, Answer], by = id][, V1]
  }
)

Unit: microseconds
                   expr        min         lq        mean     median         uq        max neval
             without SD    455.795    493.949    569.4979    529.847    558.564   2323.283   100
 without Sd alternative    961.231   1010.667   1160.9114   1060.513   1113.641   7783.798   100
                with SD 121217.691 123557.590 131071.5699 127495.437 130340.977 240317.227   100

.SD 操作与分组操作中的替代方案相比相当慢。 即使你想对整个 data.table 进行分组,替代方法也会稍微快一些(尽管这里的时间差异可能不值得失去语法的清晰度):

microbenchmark(
  "with SD" = {b <-DT[,.SD[1], by = id]},
  "Without SD" = {b <- DT[DT[,.I[1],by = id][,V1]]}
)

Unit: milliseconds
       expr      min       lq     mean   median       uq      max neval
    with SD 1.058872 1.361436 1.560866 1.643078 1.741540 1.960206   100
 Without SD 1.067898 1.169642 1.279443 1.233437 1.348719 1.781334   100

第二个例子说明了这样一个事实,即您不能真正使用 .SD 将新变量分配给具有组内条件的值(或者我没有找到方法):

DT[, .SD[V1 - V1[1] > 100][, plouf2 := Answer], by = id] # doesn't assign plouf2
DT[DT[, .I[V1 - V1[1] > 100], by = id][, V1], plouf2 := Answer] # this does

我发现在两种情况下使用 .SD 很有用:DT[,lapply(.SD,fun),.SDcols = ] 使用的是非常方便,当一个人想要将组中的所有值分配给满足组内特定条件的特定值时:

DT[, plouf3 := .SD[V1 - V1[1] > 100, Answer][1], by = id] 
# all values are assigned, which is actually different from 
DT[DT[, .I[V1 - V1[1] > 100][1], by = id][, V1], plouf2 := Answer] 
# where only the values that match the condition V1-V1[1]>100 are assigned

所以我的问题是:在其他情况下是否需要/有兴趣使用 .SD?

提前感谢您的帮助。

最佳答案

关于您的第一个问题

只有当所有三种方法都产生相同的输出时,基准测试才是公平的。 “无 SD 替代”方法会产生不同的结果,因此让我们将其放在一边。

“with SD”和“without SD”方法生成相同的输出,但后者效率更高。原因如下:当您执行 ... .SD[1, Answer] ... 时您基本上是对匹配行的 所有 列进行子集化,然后对该子集执行下一个操作(即获取向量 Answer 的第一个值)。但是,在“无 SD”方法中,您只是子集一个向量(不是所有向量),然后获取该向量的第一个值。 “with SD”方法中额外的、未使用的列的不必要的子集化是导致速度变慢的原因。

关于你的第二个问题

此命令不会将值分配给 DT:

DT[, .SD[V1 - V1[1] > 100][, plouf2 := Answer], by = id]

原因是 .SD operator 是一个单向运算符,也就是说,如果您更改 .SD 的子集中的某些内容给你,它不会将它应用到更大的 data.table 上,而只会将它应用到子集的 内存中 副本上。将其称为内存中的副本是不公平的,因为.SD实际上并不复制数据(它只是指向保存感兴趣子集的内存的相关部分),但要点是对它的赋值只会应用于此内存中指针并且不是原始的底层数据。

注意:那么您可能会争辩说它不应该支持任何赋值。我不知道 Matt Dowle 是怎么想的,但以我的拙见,分配实际上是一个有用的功能!例如:

DT.2 <- DT[, .SD[V1 - V1[1] > 100][, plouf2 := Answer], by = id]

这样我就有了一段非常简短、可读性强的代码,它生成了我想要的输出并将其存储在一个新的 data.table 中,而无需修改原始 data.table!我能想到的任何其他方式在不使用 .SD 的情况下生成这个精确的输出并且不触及原始数据。表涉及更长的代码。

关于您的最后一个问题

.SD当您想要处理 data.table 的许多或所有列而不仅仅是几列或只有一列时很有用。 (这就是为什么你在第一部分中使用的“with SD”方法不是做你想做的事情的合适方法)。 What does .SD stand for in data.table in R 中提供的示例对演示何时 .SD 非常有用可以很方便。在我看来,.SD 的主要优势不是代码运行的效率,而是将概念转化为 R 代码的效率,以及该段代码的可读性。

关于r - .SD 在 data.table 操作中的兴趣,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47539186/

相关文章:

r - 使用plot()绘制shapefile时如何设置线宽和颜色

r - 快速分组简单线性回归

python - 如何在 python 中将 csv 的列值转换为多行

python - 在 Python 中将具有不同标题的 csv 文件与 Pandas 合并

python - 我想计算每个不同组的行数

R 将聚合季度 data.table 合并到双边年度 data.table,同时扩展每个 i 的季度维度

r - 使用 data.table 聚合小计和总计

r - 计算r中shapefile中变量的表面积

r - 在 CMD 检查期间找不到函数 "%>%"

删除组中带有 NA 的行,假设该组包含至少一个非 NA 值