这是在我从@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"]]]
是否“效率低下” (与第二种方式相比)?
答案 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节。
如果j
中data.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}}。