#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()
```
Я также думаю, что интересно, как сообщения выводятся перед результатами / печатаются с помощью 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. Это довольно хороший подход для тех, кто хочет быстро исправить ситуацию без особых проблем. Однако обновление блоков кода вызывает раздражение, так как вам нужно будет делать все дважды!