6

这是我在@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")

现在,假设一个人想要得到按 column 分组的所有列的总和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. 而且我不明白recycled listRHS中的这是什么..

但如果我这样做:

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"]]]潜在问题(并且似乎已经忘记了!):分配为“效率较低” (与第二种方式相比)?

4

1 回答 1

7

更新:表达式,

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

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

o 现在优化了表单的复杂 j 表达式DT[, c(..., lapply(.SD, fun)), by=grp],只要.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"属性。NAMEDSEXP 结构中的值取值 0、1 或 2。R 使用它来知道它是否需要在子分配时复制。请参阅 R-ints 的第 1.1.2 节。

如果jin 的优化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"]]返回 aNAM(2)$N不是。

于 2013-04-22T17:03:31.720 回答