【问题标题】:knitr gets tricked by data.table `:=` assignmentknitr 被 data.table `:=` 赋值欺骗
【发布时间】:2013-02-22 09:43:20
【问题描述】:

似乎knitr 不理解DT[, a:=1] 不应导致向文档输出DT。有没有办法阻止这种行为?

示例knitr文档:

Data.Table Markdown
========================================================
Suppose we make a `data.table` in **R Markdown**
```{r}
DT = data.table(a = rnorm(10))
```
Notice that it doesn't display the contents until we do a
```{r}
DT
```
style command.  However, if we want to use `:=` to create another column
```{r}
DT[, c:=5]
```
It would appear that the absence of a equals sign tricks `knitr` into thinking this 
is to be printed.

针织输出:

这是knitr 错误还是data.table 错误?

编辑

我刚刚注意到,knitrecho 处理代码时很奇怪。看看上面的输出。在我的源代码中,我有 DT[, c:=5]knitr 呈现的是

DT[, `:=`(c, 5)]

奇怪...

编辑 2:缓存

:= 的缓存似乎也有问题,但这肯定是不同的原因,所以这里有一个单独的问题:why does knitr caching fail for data.table `:=`?

【问题讨论】:

  • 我很难理解这是一个data.table 错误。如果将其分配回 DT 会发生什么? (虽然我不确定 MatthewDowle 是否愿意)。
  • 好主意! DT=DT[, :=] 是一个可行的解决方法。我怀疑它一定是knitr,但我不知道knitr 是如何决定它是否应该输出的——这纯粹是分配的存在吗? data.table 显然做了一些事情来停止控制台中的输出 - 也许这还不够全面?
  • 如果将表达式包装在 invisible() 中,则 knitr 不会打印它,这让我认为 knitr 比在有作业时不打印更聪明。但后来我认为 data.table 在这种情况下可能应该返回一个不可见的对象,因此 data.table 中的错误可能是可能的。为什么还是打印不出来?那一定是 data.table 的事……
  • @Spacedman,对于:=(通过引用分配),是的,它没有。您必须使用print(DT[, LHS := RHS])DT[, LHS := RHS][]
  • 如果我从 R-forge 获得最新的 data.table 并使用 devtools 加载它,我确实会看到带有 DT[,c:=1] 的打印输出。但只有在加载了 devtools 时。这个包确实有一些看起来很粗鲁的东西,到处都是invisible() 和一个.global 对象。嗯。

标签: r data.table knitr


【解决方案1】:

2014 年 10 月更新。现在在 data.table v1.9.5 中:

:= 不再打印在 knitr 中以与提示符 #505 处的行为保持一致。测试knit("knitr.Rmd") 的输出现在位于 data.table 的单元测试中。

及相关:

if (TRUE) DT[,LHS:=RHS] 现在不打印(感谢 Jureiss,#869)。添加了测试。为了让它工作,我们不得不忍受一个缺点:如果在函数结束之前在没有DT[]的函数中使用:=,那么下次在提示符下键入DT,什么都不会打印。将打印重复的DT。为避免这种情况:在函数中的最后一个 := 之后包含一个 DT[]。如果这是不可能的(例如,它不是您可以更改的功能),那么保证在提示符处打印print(DT)DT[]。和以前一样,在:= 查询的末尾添加一个额外的[] 是一个推荐的习惯用法来更新然后打印;例如> DT[,foo:=3L][]



为子孙后代保留以前的答案(global$depthtrigger 业务不再像 data.table v1.9.5 那样完成,所以这不再是真的)...

为了清楚,我明白了:knitr 正在打印,而您不希望它打印。

尝试在脚本开头稍微增加data.table:::.global$depthtrigger

目前这将是 3 个:

data.table:::.global$depthtrigger
[1] 3

我不知道knitr 增加了多少 eval 深度。但是先尝试将触发器更改为 4;即

assign("depthtrigger", 4, data.table:::.global)

knitr 脚本的末尾确保将其设置回 3。如果 4 不起作用,请尝试 5,然后尝试 6。如果达到 10,请放弃,我会再考虑。 ;-P

为什么会这样?

查看 v1.8.4 的新闻:

DT[,LHS:=RHS,...] 不再打印 DT。这实现了 #2128 “尝试 再次让DT[i,j:=value]隐形返回”。感谢这里的讨论:
how to suppress output when using `:=` in R {data.table}, prior to v1.8.3?
http://r.789695.n4.nabble.com/Avoiding-print-when-using-tp4643076.html
常见问题解答 2.21 和 2.22 已更新。

FAQ 2.21 为什么 DT[i,col:=value] 返回整个 DT?我期望没有可见值(与
v1.8.3 已更改 以满足您的期望。请升级。返回整个 DT (现在不可见)以便复合语法可以工作;例如。, DT[i,done:=TRUE][,sum(done)]。返回更新的行数 当详细程度打开时,基于每个查询或全局使用 选项(datatable.verbose=TRUE)。

FAQ 2.22 好的,谢谢。 DT[i,col:=value] 不可见返回的结果有什么困难?
R 内部强制 对于 [. FunTab 的 eval 列的值(见 src/main/names.c) for [ 为 0 表示强制 R_Visible on(参见 R-Internals 第 1.6 节)。因此,当我们尝试 invisible() 或 自己直接设置R_Visible为0,在src/main/eval.c中eval 会再次强迫它。要解决这个问题,关键是停止 试图在 := 之后停止运行打印方法。相反,在 := 我们现在(从 v1.8.3 开始)设置一个全局标志,打印方法用于 知道是否实际打印。

那个全局标志是data.table:::.global$print。在data.table:::print.data.table 的顶部,您会看到它正在查看它。这是因为没有已知的方法来禁止从[ 打印(如常见问题解答 2.22 所述)。

所以,在:= 内部[.data.table 内部,它看起来会看到这个调用有多“深度”:

if (Cstack_info()[["eval_depth"]] <= .global$depthtrigger) {
    suppPrint = function(x) { .global$print=FALSE; x }
    # Suppress print when returns ok not on error, bug #2376.
    # Thanks to: https://stackoverflow.com/a/13606880/403310
    # All appropriate returns following this point are
    # wrapped i.e. return(suppPrint(x)).
}

基本就是说:如果DT[,x:=y] 在提示符下运行,那么我知道REPL 将在我的结果上调用print 方法,这是我无法控制的。好的,所以考虑到print 方法将要运行,我将通过设置一个标志在print 方法中抑制它(因为运行的print 方法(即print.data.table)是我可以控制的)。

knitr 的情况下,它以一种巧妙的方式模拟了 REPL。这不是一个真正的脚本,iiuc,否则DT[,x:=y] 无论如何都不会因为这个原因打印出来。但是因为它通过eval 模拟REPL,所以对于从knitr 运行的代码,eval 的深度有一个额外的级别。或者类似的东西(我不知道knitr)。

这就是为什么我认为增加 depthtrigger 可能会奏效。

Hacky/crufty,我同意。但如果它有效,并且您让我知道哪个值有效,我可以将data.table 更改为knitr 感知并自动更改depthtrigger。或者欢迎任何更好的解决方案。

【讨论】:

  • 啊!我只花了半个小时从源代码中挖掘出来!为什么针对 C 堆栈深度进行测试可以很好地测试是否使结果不可见?
  • @Spacedman 这本身可能不是。但到目前为止,这是我发现的唯一方法,可以知道这个对[.data.table 的调用返回的结果是否即将由 REPL 打印。 invisible() 的内部也设置了一个全局标志,顺便说一句。基本上,R 会忽略 [.class 的结果的不可见性并打印它(FAQ 2.22)。
  • 因为在开发data.table时(本质上我只是将source() .R代码转换为.GlobalEnv)我需要depthtrigger为9。但是当安装包时,depthtrigger可以低得多 3. 我认为这与包含 ByteCompile: TRUE 的描述有关。
  • @Spacedman 有时你必须打碎一些鸡蛋来制作煎蛋卷,即使有几块贝壳掉了进去。
  • v1.9.5 中引入的更新,似乎不再适用于 v1.9.6(或截至今天的 1.9.7)。 knitr 在 := assignment 之后仍然打印 data.table。
【解决方案2】:

为什么不直接使用:

```{r, results='hide'}
DT[, c:=5]
```

【讨论】:

  • 因为它可能不是块中唯一的东西。随着工作的进行,自我分配可能会更好。最后,如果您的作业调用一个打印的函数(代替“5”)怎么办。
【解决方案3】:

对于 2017 年使用 RMarkdown 1.3 和 data.table 1.10 或类似版本返回此项目的任何人,有一个 resurgence of this bug, as identified and documented here

这随后在 RMarkdown 1.4 中得到修复

【讨论】:

    【解决方案4】:

    只需用 invisible() 将表达式括起来。这对我有用。

    【讨论】:

    • 这并不理想,因为如果将echo = TRUE 和输出用作教程/参考,新学习者可能会认为在常规编码中使用invisible 是正常/必需的(它不是)。不必要的混乱。
    【解决方案5】:

    我遇到过同样的问题,我通过重新分配变量相当容易地解决了这个问题。在你的情况下:

    DT <- DT[, ':=' (c, 5)]
    

    虽然有点冗长,尤其是在变量名很大的情况下。

    【讨论】:

    • 这是我找到的最简单的方法。特别是如果您不想更新到开发版本(因为当前的 cran 似乎是 v1.9.4)
    猜你喜欢
    • 1970-01-01
    • 2018-07-27
    • 1970-01-01
    • 1970-01-01
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 2017-01-19
    • 1970-01-01
    相关资源
    最近更新 更多