为什么 Data.Table ':=' 的 KNITR 缓存失败?

why does knitr caching fail for data.table `:=`?

提问人:Corvus 提问时间:3/9/2013 最后编辑:CommunityCorvus 更新时间:10/25/2017 访问量:1331

问:

这在精神上与这个问题有关,但在机制上必须有所不同。

如果尝试缓存包含分配的块,则它的行为就像该块尚未运行一样,并且后面的块看不到 .knitrdata.table:=:=

知道这是为什么吗?检测对象是如何更新的,是什么让它感到困惑?knitrdata.table

看来您可以通过执行 来解决此问题。DT = DT[, LHS:=RHS]

示例

```{r}
library(data.table)
```
Data.Table Markdown
========================================================
Suppose we make a `data.table` in **R Markdown**
```{r, cache=TRUE}
DT = data.table(a = rnorm(10))
```
Then add a column using `:=`
```{r, cache=TRUE}
DT[, c:=5] 
```
Then we display that in a non-cached block
```{r, cache=FALSE}
DT
```
The first time you run this, the above will show a `c` column, 
from the second time onwards it will not.

第二次运行时输出

knitr output

R 缓存 data.table knitr

评论

0赞 Matt Dowle 3/9/2013
+1 恐怕我对此一无所知。当你说“第二次以后”时,你的意思是重复,在块内重复,还是重新运行脚本?“第二次运行时输出”之后什么都没有 - 是那个点,即它完全是空白的,或者你忘记在那里粘贴一些东西。尝试在各个点检查对象。缓存是如何实现的?DTDTcache=FALSE.Internal(inspect(DT))knitr
0赞 Josh O'Brien 3/9/2013
@MatthewDowle -- 这有点推测性(b/c 我不想深入研究 knitr 的缓存机制),但我怀疑我下面的回答至少是正确的。
0赞 Matt Dowle 3/9/2013
@JoshO'Brien Cool,听起来很对,谢谢。目标是回到它并改变其中任何一个或一起玩得很好,但这个解决方案在此期间很好。knitrdata.table
1赞 Josh O'Brien 3/9/2013
@MatthewDowle -- 在我看来,最好固定在针织方面,Yihui 似乎同意。顺便说一句,非常感谢您进行必要的更改,使 data.table 在 R-3.0.0 下工作!摆脱所有非 API 调用是否是一项艰巨的工作?
0赞 Matt Dowle 3/9/2013
@JoshO'Brien:没问题。不是真的,只是几个小时。布莱恩·里普利(Brian Ripley)让我知道Cstack_info()的存在,帮了我很多忙。如果没有这个提示,我会被困很长时间。

答:

19赞 Josh O'Brien 3/9/2013 #1

投机:

这是似乎正在发生的事情。

KNITR 非常明智地在创建对象后立即缓存它们。然后,每当检测到它们已被更改时,它都会更新其缓存值。

但是,data.table 绕过了 R 的常规按值复制分配和替换机制,并使用运算符而不是 、 或 .因此,knitr 不会拾取被 更改的信号。:==<<-<-DTDT[, c:=5]

溶液:

只需将此块添加到代码中,只要您希望重新缓存当前值即可。它不会花费您任何内存或时间(因为除了引用之外,没有任何东西被复制),但它确实有效地向已更新的 knitr 发送(假)信号:DTDT <- DTDT

```{r, cache=TRUE, echo=FALSE}
DT <- DT 
```

示例文档的工作版本:

通过运行文档的编辑版本来检查它是否有效:

```{r}
library(data.table)
```
Data.Table Markdown
========================================================
Suppose we make a `data.table` in **R Markdown**
```{r, cache=TRUE}
DT = data.table(a = rnorm(10))
```

Then add a column using `:=`
```{r, cache=TRUE}
DT[, c:=5] 
```

```{r, cache=TRUE, echo=FALSE}
DT <- DT 
```

Then we display that in a non-cached block
```{r, cache=FALSE}
DT
```
The first time you run this, the above will show a `c` column. 
The second, third, and nth times, it will as well.

评论

0赞 Corvus 3/9/2013
谢谢你 - 任何猜测会涉及什么。是发出正确的信号,还是要注意?knitr:=data.tableknitr:=
3赞 Yihui Xie 3/9/2013
@Corone在这种情况下,手动将对象名称分配给缓存听起来是个好主意;如果您提交功能请求,我可以考虑:github.com/yihui/knitr/issuesknitr
0赞 Matt Dowle 3/10/2013
@Yihui 嗨。如果有什么我可以改变的,请告诉我。data.table
0赞 Yihui Xie 3/10/2013
@MatthewDowle谢谢,但这可能很难;我想要的是并且也应该出现在环境中,但这显然与 的哲学相矛盾。我心中有一个可能的解决方案,我会更多地考虑它。x=data.table(a=1:5); ev=new.env(); eval(quote({x[, b:=5]}), envir=ev)xevdata.table
2赞 Josh O'Brien 3/13/2013
@MatthewDowle -- 像这样的东西应该可以工作: .(在调用堆栈中搜索调用 to 可能比查找 更安全,因为在调用是“以编程方式”构造的情况下,它会更好地工作,如 或 等)"evaluate" %in% sapply(sys.calls(), function(X) deparse(X[[1]]))evaluateknitknitdo.call(knit, ...)sapply(..., knit)
11赞 Yihui Xie 4/8/2013 #2

正如乔什·奥布莱恩(Josh O'Brien)回答下的第四条评论所指出的,我添加了一个新的块选项来处理这种非常特殊的情况。在第二个缓存块中,我们可以指定 这样将保存 .cache.varscache.vars='DT'knitrDT

```{r}
library(data.table)
```
Data.Table Markdown
========================================================
Suppose we make a `data.table` in **R Markdown**
```{r, cache=TRUE}
DT = data.table(a = rnorm(10))
```
Then add a column using `:=`
```{r, cache=TRUE, cache.vars='DT'}
DT[, c:=5] 
```
Then we display that in a non-cached block
```{r, cache=FALSE}
DT
```

无论您编译文档多少次,输出都是这样的:

knitr works with data.table now