Rmarkdown child documents do not detect their `params` - r

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}
```

Related

Insert Pagebreak for Output after Code Chunk

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')
```

RMarkdown Using comments to make sections optional

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:

R Markdown - Hyperlink outside Rmd file

I am wondering how we can define a hyperlink for R markdown outside Rmd file. Defining a hyperlink inside Rmd file is as easy as typing [hyperlink lable](actual link); but, if on the Rmd file, I am calling some other r-file (e.g., a function) that generates an address to a file, is there a way to transfer this information back to Rmd file to generate a hyperlink there?
Please see example below for more clarification:
Inside Rmd file:
myFun( with some input)
Inside myFun:
myFun <- function( with some input)
some calculations...
return("[click here](C:/myFile.docx)")
The output on the generated html page is:
[1] "[click here] (C:/myFile.docx)"
Which is not a hyperlink!
To define the links outside the rmd file you can use a parameterized report. This allows you to pass values into the rmarkdown document when you compile it. To do this, first create an rmarkdown document that includes the desired parameters, which are declared in the yaml header and then used later in the report. Then, in a separate R script, run the render command (from the rmarkdown package) and pass the desired parameter values.
Here's an example that uses cat or paste to generate the links. For comparison, I've also added a second set of parameters that add different links using the methods in #JohnCoene's answer. I've saved the rmarkdown document as "test.rmd", which is how the document is identified in the render command.
rmarkdown document
---
output: html_document
params:
text1: "add text"
link1: "add URL"
text2: "add text"
link2: "add URL"
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
```{r}
# Function to add link
myFun <- function(text, link, inline=TRUE) {
if(inline) {
paste("[", text, "](", link, ")", sep="")
} else {
cat("[", text, "](", link, ")", sep="")
}
}
```
Blah, blah, blah, more text. And here's the link:
```{r, results="asis"}
myFun(params$text1, params$link1, inline=FALSE)
```
Blah, blah, blah, more text. And here's a way to `r myFun(params$text1, params$link1)`.
Or, using the examples from `#JohnCoene's` answer:
With an HTML tag:
```{r, results="asis"}
tg <- function (link, text){
paste0("<a href='", link, "'>", text, "</a>")
}
tg(params$link2, params$text2)
```
With `htmltools`:
```{r}
# install.packages("htmltools")
library(htmltools)
tags$a(
href = params$link2,
params$text2
)
```
Separate R script to render the rmarkdown document
library(rmarkdown)
render(input="test.rmd",
params=list(text1="link to Stackoverflow",
link1="https://stackoverflow.com/questions/52745926/r-markdown-hyperlink-outside-rmd-file",
text2="link to google",
link2="https://google.com"))
Here's what the output html document looks like:
There are multiple solutions, return an html tag or use the htmltools package.
HTML tag
```{r, results="asis"}
tg <- function (link, text){
paste0("<a href='", link"'>", text, "</a>")
}
tg("http://google.com", "link to Google")
```
htmltools
Definitely recommend this way over the other.
```{r}
# install.packages("htmltools")
library(htmltools)
tags$a(
href = "https://google.com",
"Link to Google"
)
```
As mentioned in the question, assuming the output of 'myFun' function is the hyperlink string, here is what worked best for me:
Inside myFun:
myFun <- function()
...some calculations to generate a csv file...
return("C:/myFile.csv")
Inside Rmd file:
```{r, echo=FALSE}
myHyperlink <- myFun()
hyperlink_exists = !is.null(myHyperlink)
```
```{r, eval=hyperlink_exists, results="asis", echo=FALSE}
cat(paste0("The file is saved ","[", "Here", "](", myHyperlink, ")","\n"))
```

Toggle chunk output using "details" HTML tag

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"}
```

set fig.cap to options$label

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()
```

Resources