【问题标题】:interest of .SD within data.table operations.SD 在 data.table 操作中的兴趣
【发布时间】:2018-05-12 08:37:36
【问题描述】:

我有一个关于 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 吗?

提前感谢您的帮助。

【问题讨论】:

  • 第一个问题,Subset by group with data.table 的可能重复项:“OP 运行缓慢的主要原因不仅在于其中包含 .SD,而且在于它以特定方式使用它 -通过调用[.data.table,目前它的开销很大,因此在循环中运行它(当一个人执行by时)会累积非常大的惩罚”。另请参阅Optimize .SD query to keep the elegance but make it faster 和其中的链接。
  • 感谢您的链接。
  • 当你想对多列进行操作(就像你已经展示过的那样)或在某个操作之后返回少数(或所有列)时,你通常会想要使用.SD 987654335@ 或DT[, .SD[1L], by = x]。您也可以将它用于条件连接,例如DT[x &gt; 2, .SD[DT, x, on = .(y)]]。除此之外,我真的没有理由使用它,您可能会使用实际的向量。
  • 这个问题太笼统了,如果可以的话,我会投票关闭它;我在有用的时候使用.SD,这很常见——这不是一个好的答案,但这主要是因为这不是一个好问题
  • What does .SD stand for in data.table in R 的可能重复项。特别是考虑到@MichaelChirico 最近非常彻底的answer

标签: r dataframe data.table


【解决方案1】:

关于您的第一个问题

只有当所有三种方法都产生相同的输出时,基准才是公平的。 “没有 SD 替代”的方法会产生不同的结果,所以让我们把它放在一边。

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

关于你的第二个问题

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

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

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

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

DT.2 &lt;- DT[, .SD[V1 - V1[1] &gt; 100][, plouf2 := Answer], by = id]

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

关于您的最后一个问题

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

【讨论】:

  • 感谢您的回答。 stackoverflow.com/questions/8508482/… 中提供的示例确实非常好。谢谢你的解释
  • 顺便说一下,不同的方法会产生相同的结果。没有 SD 的替代方案生成一个字符向量,而其他两个生成数据表,但结果是相同的。我编辑了帖子,所以结果是 100% 相同的。
  • 嗯,它们语义上相同(对你和我而言)但技术上不同(制作 data.table 的处理和内存占用与向量不同)。一个好的基准是所有方法的结果完全相同相同。
猜你喜欢
  • 1970-01-01
  • 2017-04-29
  • 2015-10-04
  • 1970-01-01
  • 2016-04-17
  • 2015-11-12
  • 2011-08-10
  • 1970-01-01
相关资源
最近更新 更多