I have a .Rmd file which I am converting to PDF. For layout reasons, I want to display the generated output plot of my code chunk on the next page even though it would just fit in underneath.
Normally with text etc. one would use
\pagebreak
But how can I signalize the code chunk that it should display its output on the next page?
Thanks for helping!
You can write a knitr hook to set up a chunk option to do this.
So here I have modified the source chunk hook and created a chunk option next_page which, if TRUE, the output of that chunk will be on the next page.
---
title: "Chunk Output in Next page"
output: pdf_document
date: "2022-11-25"
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
## R Markdown
```{r, include=FALSE}
library(knitr)
default_source_hook <- knit_hooks$get('source')
knit_hooks$set(
source = function(x, options) {
if(isTRUE(options$next_page)) {
paste0(default_source_hook(x, options),
"\n\n\\newpage\n\n")
} else {
default_source_hook(x, options)
}
}
)
```
```{r, next_page=TRUE}
plot(mpg ~ disp, data = mtcars)
```
I would recommend you to save the output in the file (png/jpeg/pdf) and then load it with the markdown or knitr.
Such solution gives you a full control.
Automatic
I found alternative solution in rmarkdown-cookbook.
We generate a plot in this code chunk but do not show it:
```{r cars-plot, dev='png', fig.show='hide'}
plot(cars)
```
\newpage
![A nice plot.](\`r knitr::fig_chunk('cars-plot', 'png')\`)
Manual
```{r}
png("NAME.png")
# your code to generate plot
dev.off()
```
then load image with
![LABEL](PATH/NAME.png) or with
```{r echo=FALSE, out.width='100%', ...}
knitr::include_graphics('PATH/NAME.png')
```
Related
Is it possible to have entire sections programmatically commented out in RMarkdown. For example, say I have a section of my report :
### 2.1. Heading
Blah blah blah my description, with `r variables` and other stuff.
#### Table 2.1.1. Sub heading
```{r table_2_1_1}
kable_2_1_1
```
Now based on a boolean in the code, I want to completely comment out that whole section. I've tried putting r variables into the report like "< !--" & "-->" but that does not work.
You can pass a boolean value to eval in your rmd chunk to conditionally knit different blocks of rmarkdown code:
---
title: "Untitled"
author: "Matt"
date: "12/21/2021"
output: html_document
---
```{r setup, include=FALSE}
library(knitr)
knitr::opts_chunk$set(echo = TRUE)
should_i_evaluate <- F
```
```{r header, eval = should_i_evaluate}
asis_output("# My Header")
```
```{asis, echo = should_i_evaluate}
Blah blah blah my description, with `r names(mtcars[1:3])` and other stuff.
```
```{r cars, eval = should_i_evaluate}
summary(mtcars)
```
When should_i_evaluate is TRUE:
When should_i_evaluate is FALSE:
I'm writing some tutorials using blogdown. For a pedagogical reason, I want my students to think before seeing the solution. Here's my current code.
Original
---
title: "Toggle Chuck Output Using details Tag"
output: html_document
---
```{r calc, prompt=TRUE, eval=FALSE}
90 + 30
```
<details>
<summary>Toggle output</summary>
```{r, ref.label='calc', echo=FALSE, prompt=TRUE}
```
</details>
Here's my attempt:
To avoid repeatedly writing the HTML tags, I think I need to define a function similar to ...
togglable <- function(label, summary = "Toggle output"){
cat('<details>')
cat(' <summary>', summary, '</summary>', sep = '')
# Code to print output using 'ref.label' should go here.
# The following doesn't work.
knitr::knit_print(knitr:::knit_code$get(label))
cat('</details>')
}
.... then replace the <detals>...</details> block with a R code chunk similar to the following:
Use case 1 (better)
```{r usecase1, echo=FALSE, results='asis'}
togglable(label = "calc")
```
I tried to make it work, but in vain.
One more thing. If possible, I'd like this togglable() function to override the chunk options so that I don't even need to write echo=FALSE, results='asis', because the following chunk would look nicer.
Use case 2 (best)
```{r usecase2}
togglable(label = "calc")
```
In summary, I would like to ask the following questions.
How can I define this togglable() function so that it behaves in the same way as the original <detals>...</details> block?
Is it possible that this function overrides the options (echo and results in particular) for the chunk where this function is called? If yes, how?
Alternatively, is there any other idea how to produce the result of the original code without repeatedly writing the HTML tags?
Thank you very much!
This can be done by a combination of the chunk option ref.label (to reuse chunks), a chunk hook (to print the <details> tag) and a option hook (to change the chunk options when displaying the results.
---
title: "Toggle Chuck Output Using details Tag"
output: html_document
---
```{r setup, include=FALSE}
library(knitr)
knit_hooks$set(showDetails = function(before, options, envir) {
if (before) {
return("<details>\n")
} else {
return("\n</details>")
}
})
opts_hooks$set(showDetails = function(options) {
if(options$showDetails) {
options$echo = FALSE
options$results = "asis"
}
return(options)
})
```
```{r calc, prompt=TRUE, eval=FALSE}
90 + 30
```
```{r, ref.label="calc", showDetails = TRUE}
```
How it works:
The chunk hook is executed before and after each chunk where the option showDetails is not NULL. It prints (returns) the respective HTML.
The option hook adjusts the other options (echo and results) for each chunk there showDetails is TRUE.
The code could be further improved by globally setting the options of the calc chunk, such that you do not have to repeat them for all other "show code only" chunks: add opts_chunk$set(prompt = TRUE, eval = FALSE) to the setup chunk and options$eval = TRUE to the option hook.
Besides, if you want <detail> tags by default whenever using ref.label, you could use ref.label as option hook:
```{r setup, include=FALSE}
library(knitr)
opts_chunk$set(prompt = TRUE, eval = FALSE)
knit_hooks$set(showDetails = function(before, options, envir) {
if (before) {
return("<details>\n")
} else {
return("\n</details>")
}
})
opts_hooks$set(ref.label = function(options) {
options$echo = FALSE
options$results = "asis"
options$eval = TRUE
options$showDetails = TRUE
return(options)
})
```
```{r calc}
90 + 30
```
```{r, ref.label="calc"}
```
I have a master Rmarkdown document into which I am including my individual chapters using knitr's child option. Each chapter makes use of rmarkdown parameters in its own YAML.
Each chapter compiles well individually, but when put in this master document, I get the error
object: 'params' not found
I believe it is because when the child is knitted, knitr does not read the parameters in the YAML (which is an rmarkdown feature, not a knitr feature).
Is there some way I can make these available to knitr? Is there an "rmarkdown"-way of putting in child documents?
---
title: My thesis
---
blah blah.
# Introduction
```{r child='01-introduction.rmd'}
```
# Mathematical background
```{r child='02-mathsy-maths.rmd'}
```
Example 01-introduction.rmd
---
title: Introduction
params:
dataset: ABC
---
As I understand knitr, when you knit a child document, this document is evaluated in the context (ie environment) of the parent document.
So, I see 4 solutions.
Set the params in the main document
With this solution, params are controlled inside the YAML front-matter of the main document. I think it is the natural solution.
---
title: My thesis
params:
dataset: ABC
---
blah blah.
# Introduction
```{r child='01-introduction.rmd'}
```
# Mathematical background
```{r child='02-mathsy-maths.rmd'}
```
Assign the params in the global environment
With this solution, params are controlled with R code inside the main document.
---
title: My thesis
---
blah blah.
# Introduction
```{r set-params, include=FALSE}
params <- list(dataset = "ABC")
```
```{r child='01-introduction.rmd'}
```
# Mathematical background
```{r child='02-mathsy-maths.rmd'}
```
Retrieve params of the child document
With this solution, params are controlled inside each child document. It is a variant of the previous solution.
In the main document, child document's params are read using knitr::knit_params() and then assigned in the global environment.
---
title: My thesis
---
blah blah.
```{r def-assign-params, include=FALSE}
assign_params <- function(file) {
text <- readLines(file)
knit_params <- knitr::knit_params(text)
params <<- purrr::map(knit_params, "value")
}
```
# Introduction
```{r, include=FALSE}
assign_params('01-introduction.rmd')
```
```{r child='01-introduction.rmd'}
```
# Mathematical background
```{r child='02-mathsy-maths.rmd'}
```
Use a (hacky) hook to assign params temporarily
Here, I define a hook for a new use.params chunk option: this solution extends the previous one. When use.params=TRUE is used, this hook is run for each chunk of the child document.
Note that with this solution, you cannot use params in inline code.
---
title: "Main document"
---
```{r hook-def, include=FALSE}
params_cache <- new.env(parent = emptyenv())
knitr::knit_hooks$set(use.params = function(before, options, envir) {
if (before && options$use.params) {
if (exists("params", envir = envir)) {
params_cache$params <- envir$params
}
text <- readLines(knitr::current_input(dir = TRUE))
knit_params <- knitr::knit_params(text)
envir$params <- purrr::map(knit_params, "value")
}
if (!before && options$use.params) {
if (exists("params", envir = params_cache)) {
envir$params <- params_cache$params
rm("params", envir = params_cache)
} else {
rm("params", envir = envir)
}
}
})
```
blah blah.
# Introduction
```{r child='01-introduction.rmd', use.params=TRUE}
```
# Mathematical background
```{r child='02-mathsy-maths.rmd', use.params=TRUE}
```
I have 2 questions about knitr chunk options:
1) Is it possible to override knitr chunk options from inside a chunk of code so that the options were applied in the same chunk? e.g. write something like the following lines and get result as-is:
```{r, results= "markup"}
knitr::opts_chunk$set(results= "asis")
for (i in 1:5)
print("# This text should be printed 'as-is'")
```
p.s. I'm familiar with knitr::asis_output.
2) Is it possible to get chunk options from inside a chunk? E.g., to use code like:
```{r}
knitr::opts_chunk$get("results")
```
And get string markup.
```{r, results='asis'}
knitr::opts_chunk$get("results")
```
And get string asis.
Unfortunately, knitr::opts_chunk$get("results") gets global options, and not the ones of a current chunk.
1) Printings can be customized using functions knitr::normal_print and knitr::asis_output (as you mentionned). For instance :
```{r, results='markup'}
knitr::asis_output(replicate(5, "# This text should be printed 'as-is'\n"))
print("# This text should be printed 'normal'")
```
and alternatively
```{r, results='asis'}
for (i in 1:5)
cat("# This text should be printed 'as-is'\n")
knitr::normal_print("# This text should be printed 'normal'\n")
```
2) Current chunk options can be retrieved using knitr::opts_current$get(). Use :
```{r, results='asis'}
knitr::opts_current$get("results")
```
and get string asis
How can I programmatically set a figure caption in a knitr hook?
I'd like to set the figure caption, if not explicitly defined, to the chunk label. I've read the knitr docs on options, options, and hooks, and though I think I understand the mechanisms at play, I can't get it to work.
My use-case that perhaps justifies this behavior: my work-flow recently adapted to start my data and visualization exploration in Rmd files. I'll use chunks for cleaning, subsetting, etc, and then a sample chunk for each visualization. This is quick and dirty, meaning minimal markdown. When I look over the report (typically rendered into PDF), I'll look at a figure and want to go straight to the source for it. Though text before/after the figure can provide insight, due to LaTeX figure rules it is not a sure thing. Counting figure numbers is feasible, but not "easy" (and becomes problematic with many figures). Captions are always with the figure, so it'd be great if I can default to filling the caption with the chunk label. (Yes, it's a little lazy of me.)
The MWE is below.
The hook code ran just fine; the returned strings in the hook appeared correctly. However, the figure caption did not change. Exception: when there is a chunk with an undefined fig.cap, all subsequent chunks have their caption set to the first un-captioned chunk name; this doesn't surprise me due to the global nature of opts_chunk, so that's out.
I suspect it might be related to "output hooks" vice "chunk hooks," but this really is a per-chunk thing and I do not want to modify the plot, just set the caption.
MWE:
---
title: "Document Title"
author: "My Name"
output:
pdf_document:
fig_caption: yes
---
# Header
```{r setup}
knit_hooks$set(autocap = function(before, options, envir) {
if (before) {
if (is.null(options$fig.cap)) {
options$fig.cap <- options$label
knitr::opts_current$set(fig.cap = options$label)
knitr::opts_chunk$set(fig.cap = options$label) # wrong!
paste('Set: `', options$label, '`, `',
knitr::opts_current$get('fig.cap'), '`', sep = '')
} else {
paste('Kept: `', options$fig.cap, '`', sep = '')
}
}
})
opts_chunk$set(autocap = TRUE)
```
## No Plot
```{r textOnly}
1+1
```
## Caption Already Set
```{r someplot, fig.cap='someplot caption'}
plot(0)
```
## Caption Not Set
```{r anotherPlot}
plot(1)
```
Is it ok like this ? I simply modify the knitr internal function .img.cap function which can be found here.
```{r}
.img.cap = function(options) {
if(is.null(options$fig.cap)) options$label else options$fig.cap
}
assignInNamespace(".img.cap", .img.cap, ns="knitr")
```
Does it help ?
```{r}
library(knitr)
knit_hooks$set(htmlcap = function(before, options, envir) {
if(!before) {
caption <- ifelse(is.character(options$htmlcap), options$htmlcap, options$label)
paste('<p class="caption">', caption, "</p>", sep="")
}
})
```
```{r Hello, htmlcap=TRUE}
library(ggplot2)
ggplot(diamonds,aes(price,carat)) + geom_point()
```
```{r, htmlcap="Hello again"}
ggplot(diamonds,aes(price,carat)) + geom_point()
```