【问题标题】:why does knitr caching fail for data.table `:=`?为什么 knitr 缓存对 data.table `:=` 失败?
【发布时间】:2013-02-24 06:07:19
【问题描述】:

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

如果您尝试缓存包含data.table := 分配的knitr 块,那么它就好像该块尚未运行,并且以后的块看不到:= 的影响。

知道这是为什么吗? knitr 是如何检测对象更新的,data.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.

第二次运行的输出

【问题讨论】:

  • +1 恐怕我对此一无所知。当您说“第二次以后”时,您的意思是重复DT,在cache=FALSE 块内重复DT,还是重新运行脚本? “第二次运行时输出”之后没有任何内容 - 即它是完全空白的,或者你忘记在那里粘贴一些东西。尝试在各个点使用.Internal(inspect(DT)) 检查对象。 knitr缓存是如何实现的?
  • @MatthewDowle -- 这有点投机(b/c 我不想深入研究 knitr 的缓存机制)但我怀疑我下面的答案至少得到了大局正确。
  • @JoshO'Brien Cool,听起来对我来说是正确的,谢谢。打算回到它并更改 knitrdata.table 以一起玩得很好,但同时这个解决方案很好。
  • @MatthewDowle -- 在我看来,将其固定在 knitr 方面会更好,而 Yihui 似乎也同意这一点。顺便说一句,非常感谢您进行了使 data.table 在 R-3.0.0 下工作所需的更改!摆脱所有非 API 调用需要做很多工作吗?
  • @JoshO'Brien 没问题。不是真的,就几个小时。 Brian Ripley 让我知道 Cstack_info() 的存在帮了我很多忙。如果没有那个提示,我会被困很长时间。

标签: r caching data.table knitr


【解决方案1】:

正如 Josh O'Brien 回答下的第四条评论所示,我添加了一个新的块选项 cache.vars 来处理这种非常特殊的情况。在第二个缓存块中,我们可以指定cache.vars='DT',以便knitr 将保存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, cache.vars='DT'}
DT[, c:=5] 
```
Then we display that in a non-cached block
```{r, cache=FALSE}
DT
```

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

【讨论】:

    【解决方案2】:

    推测:

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

    knitr 非常明智地在对象创建后立即对其进行缓存。然后,只要检测到它们已被更改,它就会更新它们的缓存值。

    但是,

    data.table 绕过了 R 的常规按值复制分配和替换机制,并使用 := 运算符而不是 =<<- 或 @987654325 @。因此,knitr 没有接收到 DT 已被 DT[, c:=5] 更改的信号。

    解决方案:

    只需将此块添加到您希望重新缓存DT 的当前值的任何位置。它不会花费你任何内存或时间(因为除了引用之外什么都不会被DT <- DT 复制),但它确实向 knitr 发送了一个(假)信号 DT 已经更新:

    ```{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.
    

    【讨论】:

    • 谢谢你 - 任何猜测都会涉及到让knitr 接听:=。是让data.table 发出正确的信号,还是让knitr 提防:=
    • @Corone 在这种情况下,手动将对象名称分配给knitr 以进行缓存听起来是个好主意;如果您提出功能请求,我可以考虑:github.com/yihui/knitr/issues
    • @易慧你好。如果有什么我可以在 data.table 方面更改的内容,请告诉我。
    • @MatthewDowle 谢谢,但这可能很难;我想要的是x=data.table(a=1:5); ev=new.env(); eval(quote({x[, b:=5]}), envir=ev)x 也应该出现在ev 环境中,但这显然与data.table 的理念相矛盾。我想到了一种可能的解决方案,我会考虑更多。
    • @MatthewDowle -- 这样的东西应该可以工作:"evaluate" %in% sapply(sys.calls(), function(X) deparse(X[[1]]))。 (在调用堆栈中搜索对evaluate 的调用可能比查找knit 更安全,因为在knit 的调用是“以编程方式”构造的情况下,它会更好地工作,例如do.call(knit, ...) 或@ 987654349@等)
    猜你喜欢
    • 2023-04-06
    • 1970-01-01
    • 2019-04-30
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 2014-07-07
    相关资源
    最近更新 更多