获取代码块中的每行代码,以显示在' R Markdown'窗口,因为它被执行

时间:2015-07-17 07:35:21

标签: r rstudio knitr

编织R markdown文件时,我常常发现自己盯着Markdown输出等待一个块完成。不幸的是,我不知道有什么方法可以看到该块正在执行的命令。这对于了解进度或找到性能瓶颈非常方便。

我错过了一些针织选项吗?设置progress=TRUE, verbose=TRUE只显示执行该块之前的块代码。我希望每行代码都在执行之后/之前显示在控制台中,就像在交互式会话中一样。

2 个答案:

答案 0 :(得分:2)

在底部的控制台标签旁边,您会看到 R Markdown 标签,所有步骤都可以在那里看到,请参见下面的屏幕截图:

enter image description here

编辑1:我们需要设置全局选项opts_knit$set(progress = TRUE, verbose = TRUE)

示例:

---
title: "Untitled"
output: html_document
---

```{r global_options, include=FALSE}
library(knitr)
opts_knit$set(progress = TRUE, verbose = TRUE)
```

This is an R Markdown document. Markdown is a simple ...


```{r SummaryCars}
summary(cars)
```

R Markdown 窗口的输出:

processing file: temppp.Rmd
  |................                                                 |  25%
  ordinary text without R code

  |................................                                 |  50%
label: global_options (with options) 
List of 1
 $ include: logi FALSE

  |.................................................                |  75%
  ordinary text without R code

  |.................................................................| 100%
label: SummaryCars
   ~~~~~~~~~~~~~~~~~~~~~~~~~ R code chunk ~~~~~~~~~~~~~~~~~~~~~~~~~~ 
   summary(cars) 
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
##------ Fri Jul 17 09:00:42 2015 ------##


output file: temppp.knit.md

编辑2:这个答案不符合OP的要求,以此答案为例说明OP的代码。

答案 1 :(得分:0)

这是一个解决方案... :极其谨慎地使用它。在评估之前,它会对源代码感到困惑。此外,它使输出文档与诊断输出混乱。但是,出于调试目的,它可能非常有用。

简介

据我了解,RStudio中编织过程的进度窗口不能用于记录块的评估进度,因为任何块输出(如message s)仅在整个块之后显示评估。因此,唯一可行的解​​决方法是将进度写入文件。

右钩子

因此,解决方案是编写一些诊断消息,例如"现在评估[code]"到文件,表达式表达。不幸的是,在每个表达式"之前,没有默认的knitr hook用于"调用FUN。块中的所有代码(至少是要评估的所有代码)都传递到evaluate::evaluate中的block_exec(请参阅line 172 in block.R)。

由于knitr永远不会通过表达式迭代一个块的代码表达式,因此没有自然的方法来植入写入诊断消息的逻辑。 (我没有深入研究evaluate;也许这样的迭代发生在其中的某个地方。)然而,在传递之前,可以操作块的源代码马上到evaluate

请注意,运行块时,其源代码可用作chunk option code的值。结构是一个字符向量,每个元素代表一行。操作code允许修改源代码,以便在评估时自己编写诊断消息。

记录功能

我将使用以下小助手函数来编写日志文件:

writeLog <- function(message, file = paste0(current_input(), ".log"), sep = "\n") {
  cat(paste(message, sep), file = file, append = TRUE)
}

日志文件的文件名将为[documentname].log,例如mydocument.Rmd.log。由于writeLog始终附加到现有文件,因此以下行可用于清除(擦除和重新创建)每个编译的日志文件:

if(file.exists(logfile <- paste0(current_input(), ".log"))) { file.remove(logfile) }

writeLog的潜在有用扩展可以gc()使用monitor the memory usage

修改源

基本思想是添加类似

的内容
writeLog(sprintf("%s - Now evaluating the following expression: ", format(Sys.time(), "%Y-%m-%d %H:%M:%S")), sep = NULL)
writeLog('summary(cars)')

在每个表达式之前(在上面的例子中:summary(cars)之前)。问题是,到位writeLog的确切位置应该在哪里?对于每行只包含一个(完整)表达式的代码,这不是问题,但在以下情况下 有问题:

summary( # Here some descriptives:
  cars
)

对于具有该代码的块,code将是具有三个元素的向量:

print(code)
[1] "summary( # Here some descriptives:" "  cars"                             ")"

在每个元素之后简单地注入writeLog个调用是个好主意...因此,这就是解决方案变得有些讨厌的地方,code需要{{1}首先。使用参数parse,所有注释,格式等都将被删除,我们得到&#34; clean&#34;表达式。

在每个表达式之前,可以插入keep.source = FALSE个调用。此新代码使用我调用writeLog的{​​{3}}替换原始代码:

log

结果

从以下块开始

opts_hooks$set(log = function(options) {
    parsed <- parse(text = options$code, keep.source = FALSE)
    newCode <- sapply(as.character(parsed), function(x) {
      return(c(
        'writeLog(sprintf("%s - Now evaluating the following expression: ", format(Sys.time(), "%Y-%m-%d %H:%M:%S")), sep = NULL)',
        sprintf("writeLog('%s')", x),
        x))
    })

    options$code = as.vector(newCode)
  return(options)
})

源代码变为

```{r, log = TRUE}  
summary( # Here some descriptives
  cars
)
Sys.sleep(5)   
print("foo")
```

生成以下日志文​​件:

writeLog(sprintf("%s - Now evaluating the following expression: ", format(Sys.time(), "%Y-%m-%d %H:%M:%S")), sep = NULL)
writeLog('summary(cars)')
summary(cars)
writeLog(sprintf("%s - Now evaluating the following expression: ", format(Sys.time(), "%Y-%m-%d %H:%M:%S")), sep = NULL)
writeLog('Sys.sleep(5)')
Sys.sleep(5)
writeLog(sprintf("%s - Now evaluating the following expression: ", format(Sys.time(), "%Y-%m-%d %H:%M:%S")), sep = NULL)
writeLog('print("foo")')
print("foo")

使用日志文件

由于日志文件的目的是在编织过程中反映进度,因此在不阻止对其进行进一步写入操作的情况下打开它是很重要的。理想情况下,我们甚至希望看到文件的更新。

在Windows上,例如Notepad ++以非阻塞方式打开文件,并有option hook选项。在Linux上,2016-03-04 12:08:05 - Now evaluating the following expression: summary(cars) 2016-03-04 12:08:05 - Now evaluating the following expression: Sys.sleep(5) 2016-03-04 12:08:10 - Now evaluating the following expression: print("foo") autoreload modified files

限制和警告

主要限制与输出文档中可见的源代码有关tail -f [documentname].log:它被echo = TRUE调用污染。此外,所有评论和标识都丢失了,语法是标准化的#34; (例如,writeLog变为print('x'))。到目前为止,我还没有找到解决方案 - 只有部分解决方案:

  • print("x")添加为options$codeBackup <- options$code选项挂钩中的第一行。
  • 使用should do the trick log并将参数source(将打印的源代码)替换为x

    options$codeBackup
  • 问题:仅适用于hook_source <- knit_hooks$get("source") knit_hooks$set(source = function(x, options) { return(hook_source(options$codeBackup, options)) }) 或块,其中唯一的输出发生在最后。否则,只要打印代码的部分完整的代码就会重复。

此外,操纵源代码可能很危险。由于results = "keep"是一个基本R函数,我不希望解析本身存在问题。但是,我不确定之后转换为角色。

更令人担忧的是将函数调用插入到原始源中。本身,parse非常有创意,不应干扰以下代码。但你永远不知道。想象一段代码生成当前工作目录(第一个表达式)中所有文件的列表,并依赖此列表在第二个表达式中准确。突然之间,在中间创建日志文件已经不再那么无辜了......(即使这个例子中的真正问题又回到了原始代码中的output hook)。

底线是:不要在生产中使用此代码&#34;&#34;但仅用于调试目的。

完整代码/示例

writeLog