Результаты R markdown = ‘удерживать’, но для сообщений?

#r #r-markdown

#r #r-markdown

Вопрос:

У меня есть функции, выводящие несколько сообщений в моем коде, и я не могу поместить эти сообщения в один и тот же блок вывода, как с results='hold' .

На данный момент единственным вариантом, который эмулирует то, что я хочу, является collapse=T option , но это объединяет выходные данные и код, чего я не хочу.

Этот код иллюстрирует ситуацию :

 ---
title: "Example"
author: "Me"
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```



```{r}
a <- function(){
  print("hello")
  print("world")
  message("hello again")
  message("world")
}
```

### default
```{r}
a()
```


### with `results='hold'`
```{r results='hold'}
a()
```


### with `collapse=T`
```{r collapse=T}
a()
```
  

Вывод :
rmd

Я также думаю, что интересно, как сообщения выводятся перед результатами / печатаются с помощью results='hold' .

Ответ №1:

Возможность хранения сообщений, аналогичная способу хранения результатов, может быть реализована путем переопределения выходных крючков knitr по умолчанию message и chunk .

Этот подход по-прежнему позволяет сохранять результаты, если кто-то желает сопоставить результаты, которые в противном случае были бы разделены появлением сообщения.

Содержимое Rmd

 ---
title: "Knitr Message Hold Hook Implementation"
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)

# global store for current chunk messages
current_messages <- NULL

# override default message hook
# if message option is hold, copy messages to the global store and print nothing
# otherwise let knitr do its thing
knitr::knit_hooks$set(message = function(x, options) {
  if (options$message == "hold") {
    current_messages <<- c(current_messages, x)
    return(NULL)
  } else {
    return(knitr::hooks_html()$message(x, options))
  }
})

# override chunk hook
# if message option is hold, clear global store, and append prepared messages
# otherwise let knitr do its thing
knitr::knit_hooks$set(chunk = function(x, options) {
  if (options$message == "hold") {
    collapsed_messages <- paste(current_messages, collapse = "")
    options$message <- TRUE
    messages <- knitr::hooks_html()$message(collapsed_messages, options)
    current_messages <<- NULL
    paste(x, messages, collapse = "")
  } else {
    knitr::hooks_html()$chunk(x, options)
  }
})

# test function with results and messages
a <- function() {
  print("a out 1")
  message("a msg 1")
  print("a out 2")
  message("a msg 2")
}
```

# Default Behavior

```{r}
a()
```

# Holding Messages

```{r, message='hold'}
a()
```

# Holding Results

```{r, results='hold'}
a()
```

# Holding Messages and Results

```{r, message='hold', results='hold'}
a()
```
  

Вывод HTML

введите описание изображения здесь

Комментарии:

1. Спасибо, это, кажется, решает проблему довольно элегантно с помощью переназначения сообщений. Вы случайно не знаете место в исходном коде knitr, где записана эта разметка сообщения по умолчанию? Мне любопытна эта class = "hljs" часть.

2. Я не знаю, поскольку я получил эту разметку, просмотрев связанный HTML-документ с настройками по умолчанию. Я подозреваю, что это связано с highlight.js .

3. Прошло некоторое время с тех пор, как я задавал вопрос, но это довольно приятное решение. Есть ли способ получить все выходные данные в одном блоке? Что произойдет, если появится печать, затем сообщение, затем еще одна печать? Изначально я хотел вывод, аналогичный тому, что я получил бы из вывода консоли ( т. в порядке выполнения сообщения / печати)

4. Я обновил ответ на более элегантное решение, которое позволяет полагаться на существующую опцию результатов удержания.

Ответ №2:

Вот другая версия, которая может лучше соответствовать тому, что хотел OP.

Эта версия будет смешивать сообщения и выводить вместе, как они наблюдались бы, если бы они были отправлены на консоль.

Это делается путем переопределения трех перехватчиков: message , output и chunk .

Содержимое Rmd

 ---
title: "Knitr As Console Output Hook Implementation"
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)

# global store for output and messages
asconsole_output <- NULL

# override message hook
# if output option is 'asconsole', append message to global
# otherwise let knitr do its thing
knitr::knit_hooks$set(message = function(x, options) {
  if(!is.null(options$output)) {
    if(options$output == 'asconsole') {
      asconsole_output <<- c(asconsole_output, x)
      return(NULL)
    }
  }
  
  knitr::hooks_html()$message(x, options)
})
  
# override output hook
# if output option is 'asconsole', append output to global
# otherwise let knitr do its thing
knitr::knit_hooks$set(output = function(x, options) {
  if(!is.null(options$output)) {
    if(options$output == 'asconsole') {
      asconsole_output <<- c(asconsole_output, x)
      return(NULL)
    }
  }
  
  knitr::hooks_html()$output(x, options)
})

# override chunk hook
# if output option is 'asconsole', clear global and append formatted contents
# otherwise let knitr do its thing
knitr::knit_hooks$set(chunk = function(x, options) {
  if(!is.null(options$output)) {
    if(options$output == 'asconsole') {
      y <- paste(asconsole_output, collapse = "")
      y <- knitr::hooks_html()$output(y, options)
      asconsole_output <<- NULL
      y <- paste(x, y, collapse = "")
      return(y)
    }
  }
  
  knitr::hooks_html()$chunk(x, options)
})

# test function with results and messages
a <- function() {
  print("a out 1")
  message("a msg 1")
  print("a out 2")
  message("a msg 2")
}
```

# Default Behavior

```{r}
a()
```

# Output As Console

```{r, output='asconsole'}
a()
```
  

Вывод HTML

введите описание изображения здесь

Комментарии:

1. Я приму этот ответ, поскольку он касается моего первоначального вопроса, даже если людей может больше заинтересовать ваш другой ответ (который получил награду)

Ответ №3:

Я не могу найти какое-либо решение, используя опцию chunks docs1 docs2

Единственное, что приходит мне в голову, это иметь два блока — один для отображения кода без вычисления, а второй для сворачивания выходных данных, но без кода:

 ### Two blocs
```{r eval=FALSE}
a()
```
```{r collapse=TRUE, echo=FALSE}
a()
```
  

введите описание изображения здесь

Комментарии:

1. Это довольно хороший подход для тех, кто хочет быстро исправить ситуацию без особых проблем. Однако обновление блоков кода вызывает раздражение, так как вам нужно будет делать все дважды!