理解data.table中引用赋值的优化消息

时间:2013-04-22 16:34:19

标签: r data.table

这是在我从@sds here回答这个问题时的观察结果。

首先,让我打开 data.table的跟踪消息:

options(datatable.verbose = TRUE)
dt <- data.table(a = c(rep(3, 5), rep(4, 5)), b=1:10, c=11:20, d=21:30, key="a")

现在,假设有人希望获得按列a分组的所有列的总和,那么,我们可以这样做:

dt.out <- dt[, lapply(.SD, sum), by = a]

现在,假设我还要将属于每个组的条目数添加到dt.out,那么我通常按引用分配它,如下所示:

dt.out[, count := dt[, .N, by=a][, N]]
# or alternatively
dt.out[, count := dt[, .N, by=a][["N"]]]

在此引用分配中,data.table生成的消息之一是:

RHS for item 1 has been duplicated. Either NAMED vector or recycled list RHS.

这是来自data.table的源目录assign.C中的文件的消息。我不想在这里粘贴相关的片段,因为它大约有18行。如有必要,只需发表评论即可粘贴代码。 dt[, .N, by=a][["N"]]只提供[1] 5 5。所以,它是一个named vector。我不明白RHS中recycled list是什么......

但如果我这样做:

dt.out[, `:=`(count = dt[, .N, by=a][, N])]
# or equivalently
dt.out[, `:=`(count = dt[, .N, by=a][["N"]])]

然后,我收到消息:

Direct plonk of unnamed RHS, no copy.

据我所知,RHS在第一种情况下已经重复,这意味着它正在复制(浅/深,我不知道)。如果是这样,为什么会发生这种情况?

即使没有,为什么两个内部的参考分配的变化?有任何想法吗?

在撰写这篇文章时,想出了我脑海中的基础问题(似乎已经忘记了!):分配为dt.out[, count := dt[, .N, by=a][["N"]]]是否“效率低下” (与第二种方式相比)?

1 个答案:

答案 0 :(得分:6)

更新:表达式

DT[, c(..., lapply(.SD, .), ..., by=.]

已在v1.9.3(FR #2722)的提交#1242内部进行了优化。这是NEWS的条目:

  

o DT[, c(..., lapply(.SD, fun)), by=grp]形式的复杂j表达式现在已经过优化,只要.SD仅出现在lapply(.SD, fun)形式中。

     

对于前:DT[, c(.I, lapply(.SD, sum), mean(x), lapply(.SD, log)), by=grp]
       优化为:DT[, list(.I, x=sum(x), y=sum(y), ..., mean(x), log(x), log(y), ...), by=grp]

     

但是DT[, c(.SD, lapply(.SD, sum)), by=grp]例如尚未优化。    这部分解决了FR #2722。感谢Sam Steingold提交FR。


它表示NAMED vector表示在C级的内部R意义上;即一个对象是否已被赋予一个符号并被称为某个东西,而不是一个原子向量是否具有"names"属性。 SEXP结构中的NAMED值取值0,1或2.R使用它来知道是否需要复制子分段。见R-ints的第1.1.2节。

如果jdata.table的优化可以处理:

,那会更好
DT[, c(lapply(.SD,sum),.N), by=a]

虽然有效但可能很慢。目前只优化了更简单的形式:

DT[, lapply(.SD,sum), by=a]

回答主要问题,是以下内容:

Direct plonk of unnamed RHS, no copy.
与以下相比,

是可取的。

RHS for item 1 has been duplicated. Either NAMED vector or recycled list RHS.

实现这一目标的另一种方法是:

dt.out[, count := dt[, .N, by=a]$N]

我不太清楚为什么[["N"]]会返回NAM(2)而不是$N而不是{{1}}。