Knitr: one plot per .tabset in for-loop - r

Long story short, I'm trying to use knitr to dynamically create separate tabsets and render a plot within each tabset. Below is a description of my failed attempts. I would appreciate a solution, but I suspect I'm just not understanding the fundamental way in which knitr renders output. So if you could point me to resources or provide some conceptual guidance, that would also be appreciated.
Dynamically creating the tabsets themselves is easy. Here is my .R file
#' ---
#' title:
#' author:
#' date:
#' output:
#' html_document
#' ---
#' # {.tabset}
#+ results='asis', echo=FALSE
for(i in 1:10){
cat('##',i,' \n')
}
When I call rmarkdown::render() on this .R file, I get 10 empty tabsets (see below). I'm not really sure what cat does or why the \n is necessary (this is where some conceptual explanation would be appreciated), but it works.
Now I add in a boxplot() call to the for-loop and expect to get the same boxplot on each tabset.
#' ---
#' title:
#' author:
#' date:
#' output:
#' html_document
#' ---
#' # {.tabset}
#+ results='asis', echo=FALSE
for(i in 1:10){
cat('##',i,' \n')
boxplot(iris$Sepal.Length~iris$Species)
}
But instead, the first tab comes up empty and the second tab is this vomit of output.
If I add another cat() with two "\n" characters, then all the tabs appear, but only the second tab is populated with the chart. The other tabs are empty.
#' ---
#' title:
#' author:
#' date:
#' output:
#' html_document
#' ---
#' # {.tabset}
#+ results='asis', echo=FALSE
for(i in 1:10){
cat('##',i,' \n')
boxplot(iris$Sepal.Length~iris$Species)
cat(' \n \n')
}

So I found a solution, but still do not know why it works. Would appreciate any feedback. Adding plot.new() before each plot() works.
#' ---
#' title:
#' author:
#' date:
#' output:
#' html_document
#' ---
#' # {.tabset}
#+ results='asis', echo=FALSE
for(i in 1:10){
cat('##',i,' \n')
plot.new()
boxplot(iris$Sepal.Length~iris$Species)
cat(' \n \n')
}

I'm not sure, but based on these patterns, I'd say that boxplot, like most of R's native plotting functions, 'overplot' each other. So, rmarkdown::render just sees one figure being generated. By calling plot.new you're telling R to 'finalize' the current plot and then start writing plot output to a new plot. This results in ten different plots.
A tip that I find useful and you might find useful as well: when debugging/troubleshooting/figuring out the dynamics of rmarkdown::render and R Markdown, it can help to also inspect the 'raw' Markdown output. You can tell rmarkdown::render to keep this file around by including keep_md: TRUE in the YAML header (see the **Advance http://rmarkdown.rstudio.com/html_document_format.html for examples and more information).

Related

How to run a rmarkdown to create multiple html with different parameters?

I have a code in R, which is done in rMarkdown, it needs to run several times but with different parameter values.
How can I solve it? I have put an example of simplified problems.
Suppose I need to print different values in a loops, in order to make it simple I used simple loops.
For each combination of these two loops I want to make one html file, for example means 200 *400 different html report files.
---
title: "file1"
author: "user"
date: "25 1 2021"
output: html_document
---
# The first loop should be done, from 1:100, 101:200 ... to ... 20001:20100
```{r}
i=getfirst()
j=getsecond()
```
```{r}
for (i in i:j) print(i)
```
# The second loop should be done, from 1:50, 51:100 ... to ... 20001:20050
```{r}
for (i in i-50:j-50) print(i)
```
suppose each time we may have different i and j which should pass into markdown file.
I'd simply create a wrapper script (i.e. a separate e.g. .R) file, where you specify:
R file
for (i in 1:10){
j = i+50
if(!dir.exists(paste0("directory_",i))){dir.create(paste0("directory_",i))}
knitr::knit(input = "markdown.Rmd",output = paste0("directory_",i,"/output",i,"_",j,".html"))
}
And then your Rmd file, which you have saved as markdown.Rmd in this example looks like this:
RMarkdown file
---
title: "file1"
author: "user"
date: "25 1 2021"
output: html_document
---
# The first loop should be done, from 1:100, 101:200 ... to ... 20001:20100
```{r}
for (i in i:j) print(i)
```
# The second loop should be done, from 1:50, 51:100 ... to ... 20001:20050
```{r}
for (i in i-50:j-50) print(i)
```
There is a dedicated chapter in the official documentation, "Knitting with parameters", that outlines how to proceed in your use-case.

RMarkdown render to Notebook with child chunks included

I'm after a way to render an Rmd document (that contains references to various "child" files) to a self-contained R Notebook without these dependencies.
At the moment, the .Rmd code chunks are located throughout a number of .R, .py and .sql files and are referenced in the report using
```{r extraction, include=FALSE, cache=FALSE}
knitr::read_chunk("myscript.R")
```
followed by
```{r chunk_from_myscript}
```
as documented here.
I've done this to avoid code duplication and to allow for running the source files separately however these code chunks are only executable in the report via a call to knit or render (when read_chunk is run and the code chunk is available).
Is there a way to spin-off an Rmd (prior to knitting) with
just these chunks populated?
This function
rmarkdown::render("report.Rmd", clean = FALSE)
almost gets there as it leaves the markdown files behind whilst removing extraction and populating chunk_from_myscript however as these files are straight markdown, the chunks are no longer executable and the chunk options are missing. It obviously also doesn't include chunks where eval=TRUE, echo=FALSE which would be needed to run the resulting notebook.
I've also looked at knitr::spin however this would mean disseminating the contents of the report to every source file and isn't terribly ideal.
Reprex
report.Rmd
---
title: 'Report'
---
```{r read_chunks, include=FALSE, cache=FALSE}
knitr::read_chunk("myscript.R")
```
Some documentation
```{r chunk_from_myscript}
```
Some more documentation
```{r chunk_two_from_myscript, eval=TRUE, echo=FALSE}
```
myscript.R
#' # MyScript
#'
#' This is a valid R source file which is formatted
#' using the `knitr::spin` style comments and code
#' chunks.
#' The file's code can be used in large .Rmd reports by
#' extracting the various chunks using `knitr::read_chunk` or
#' it can be spun into its own small commented .Rmd report
#' using `knitr::spin`
# ---- chunk_from_myscript
sessionInfo()
#' This is the second chunk
# ---- chunk_two_from_myscript
1 + 1
Desired Output
notebook.Rmd
---
title: 'Report'
---
Some documentation
```{r chunk_from_myscript}
sessionInfo()
```
Some more documentation
```{r chunk_two_from_myscript, eval=TRUE, echo=FALSE}
1 + 1
```
Working through your reprex I now better understand the issue you are trying to solve. You can knit into an output.Rmd to merge your report and scripts into a single markdown file.
Instead of using knitr::read_chunk, I've read in with knitr::spin to cat the asis output into another .Rmd file. Also note the params$final flag to allow rendering the final document when set as TRUE or allowing the knit to an intermediate .Rmd as FALSE by default.
report.Rmd
---
title: "Report"
params:
final: false
---
```{r load_chunk, include=FALSE}
chunk <- knitr::spin(text = readLines("myscript.R"), report = FALSE, knit = params$final)
```
Some documentation
```{r print_chunk, results='asis', echo=FALSE}
cat(chunk, sep = "\n")
```
to produce the intermediate file:
rmarkdown::render("report.Rmd", "output.Rmd")
output.Rmd
---
title: "Report"
---
Some documentation
```{r chunk_from_myscript, echo=TRUE}
sessionInfo()
```
With the secondary output.Rmd, you could continue with my original response below to render to html_notebook so that the document may be shared without needing to regenerate but still containing the source R markdown file.
To render the final document from report.Rmd you can use:
rmarkdown::render("report.Rmd", params = list(final = TRUE))
Original response
You need to include additional arguments to your render statement.
rmarkdown::render(
input = "output.Rmd",
output_format = "html_notebook",
output_file = "output.nb.html"
)
When you open the .nb.html file in RStudio the embedded .Rmd will be viewable in the editing pane.
Since neither knitr::knit nor rmarkdown::render seem suited to rendering to R markdown, I've managed to somewhat work around this by dynamically inserting the chunk text into each empty chunk and writing that to a new file:
library(magrittr)
library(stringr)
# Find the line numbers of every empty code chunk
get_empty_chunk_line_nums <- function(file_text){
# Create an Nx2 matrix where the rows correspond
# to code chunks and the columns are start/end line nums
mat <- file_text %>%
grep(pattern = "^```") %>%
matrix(ncol = 2, byrow = TRUE)
# Return the chunk line numbers where the end line number
# immediately follows the starting line (ie. chunk is empty)
empty_chunks <- mat[,1] + 1 == mat[,2]
mat[empty_chunks, 1]
}
# Substitute each empty code chunk with the code from `read_chunk`
replace_chunk_code <- function(this_chunk_num) {
this_chunk <- file_text[this_chunk_num]
# Extract the chunk alias
chunk_name <- stringr::str_match(this_chunk, "^```\\{\\w+ (\\w+)")[2]
# Replace the closing "```" with "<chunk code>\n```"
chunk_code <- paste0(knitr:::knit_code$get(chunk_name), collapse = "\n")
file_text[this_chunk_num + 1] %<>% {paste(chunk_code, ., sep = "\n")}
file_text
}
render_to_rmd <- function(input_file, output_file, source_files) {
lapply(source_files, knitr::read_chunk)
file_text <- readLines(input_file)
empty_chunks <- get_empty_chunk_line_nums(file_text)
for (chunk_num in empty_chunks){
file_text <- replace_chunk_code(file_text, chunk_num)
}
writeLines(file_text, output_file)
}
source_files <- c("myscript.R")
render_to_rmd("report.Rmd", "output.Rmd", source_files)
This has the added benefits of preserving chunk options and working
with Python and SQL chunks too since there is no requirement to evaluate
any chunks in this step.

Knitr output differs between Rmd and Rnw: data.table output example

Revised title to clarify focus.
We notice an anomaly that R markdown and data.table interact in a surprising way. Same does not happen when knitting LaTeX. Commands which not have a return within the R session do cause a return within the knitted markdown output. I trace the problem back to commands like the following, which do not produce an output in R,
````{r}
poolballs[ , weight2:=2 * weight]
```
but inside Rmarkdown, the output includes the full print of the poolballs DT. Same does not happen if we knit an equivalent chunk in LaTeX.
This produced some funny HTML output because I wrote chunks like this, intending to display only the first 5 lines
```{r}
poolballs[ , weight2:=2 * weight]
head(poolballs)
```
Markdown parses that as the equivalent of two chunks,
> poolballs[ , weight2:=2 * weight]
> poolballs
> head(poolballs)
Here's the markdown file to demonstrate
---
title: "Data Table Guide"
author:
- name: Paul Johnson
affiliation: Center for Research Methods and Data Analysis, University of Kansas
email: pauljohn#ku.edu
date: "`r format(Sys.time(), '%Y %B %d')`"
output:
html_document:
theme: united
highlight: haddock
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo=TRUE, comment=NA)
options(width = 70)
```
```{r make_pb_dt}
set.seed(234234)
library(data.table)
poolballs <- data.table(
number = 1:15,
weight = rnorm(15, 45.7, 0.8),
diameter = c(3, 2.9, 3.1) #shows recyling
)
poolballs
```
I want the following to show only head in line 2
```{r}
poolballs[ , weight2:=2 * weight]
head(poolballs)
```
Compare the HTML output:
http://pj.freefaculty.org/scraps/mre-dt.html
I'm sorry if this is a known feature of markdown. I've coded around this wrinkle by hiding chunks, but it seems somewhat inconvenient. Today i'm curious enough to ask you about it. I wrote the same chunks in a LaTeX file and the funny DT output problem does not happen. I put link to PDF from LaTeX in http:/pj.freefaculty.org/scraps/mre-dt-3.pdf
In your final chunk, knitr sees that you have two objects that its attempting to print and you're getting the output for both. This isn't a feature, and has been addressed in a previous question.
If you want to only print the head of the first object in that chunk, your code should be head(poolballs[, weight2:=2 * weight])

R inline markdown

I’m using R Markdown in RStudio to create a report that mixes Markdown and R output. I know how to use inline R expressions in the Markdown, but I’m wondering how to do the converse, i.e., use Markdown within the R code. I want to loop through a series of calculations and get Markdown headings for each one. I want headings for formatting purposes (e.g., bold titles etc) and also to be able to specify (sub)sections in the resulting PDF (which is a nice feature of the way RMarkdown handles # and ## etc).
I know I can do the following:
---
title: "test"
output: pdf_document
---
#Section 1
```{r, echo=FALSE}
print(1+1)
```
#Section 2
```{r, echo=FALSE}
print(2+2)
```
#Section 3
```{r, echo=FALSE}
print(3+3)
```
Which gives something looking (roughly) like this:
Section 1
## [1] 2
Section 2
## [1] 4
Section 3
## [1] 6
Is it possible to achieve the same output using something along the lines of this:
---
title: "test2"
output: pdf_document
---
```{r, echo=FALSE}
for (i in 1:3)
{
print(paste("#Section",i))
print(i+i)
}
```
As #scoa pointed out, you have to set the chunk option results='asis'. You should also place two \n both before and after your header.
---
title: "test"
output: pdf_document
---
```{r, echo=FALSE, results='asis'}
for (i in 1:3) {
cat(paste0("\n\n# Section", i, "\n\n"))
print(i+i)
cat("\n\n\\newpage")
}
```
As a more general answer, it could be useful to a look at the markdownreports package that parses markdown code (and output) from your R variables.

Generate Dynamic R Markdown Blocks

In my dataset, I have 60 groups that I want to analyze in put into an HTML report using R Markdown. Because I want to apply the same analysis to each group, I am hoping that there is a way I can dynamically generate the code blocks/analysis.
Simply, I want to avoid replicating the block 60 times.
I came across this this question which uses children in knitr. I have attempted to replicate this with the iris dataset. In my example below, all I wanted to do was generate three H4 titles, one for each species.
It's worth noting that I am not married to this approach, it just appears to be related to what I am looking to do.
Here are the files I used:
parent.RMD file. This would be my "master" report.
Automate Chunks of Analysis in R Markdown
========================================================
```{r setup, echo=FALSE}
library(knitr)
```
```{r run-numeric-md, include=FALSE}
out = NULL
for (i in as.character(unique(iris$Species))) {
out = c(out, knit_child('child.Rmd'))
}
```
And here is child.Rmd.
#### Species = `r [i]`
Try knit_expand():
Automate Chunks of Analysis in R Markdown
========================================================
```{r setup, echo=FALSE}
library(knitr)
```
```{r run-numeric-md, include=FALSE}
out = NULL
for (i in as.character(unique(iris$Species))) {
out = c(out, knit_expand(text='#### Species = {{i}}'))
}
```
`r paste(knit(text = out), collapse = '\n')`
You can also create a template file like 'child.rmd' and put this in your for loop so you don't have to put a complicated analysis in quotes:
out = c(out, knit_expand('template.rmd'))
Then have your 'template.rmd' be:
#### Species = {{i}}
Taking #sam's solution, I made the following generic function. Say you have a data frame called grfDf with DiagrammeR graph objects in the column graph. The following is all you need to plot all the graphs in Rmd : r require(DiagrammeR); renderHtmlWidgetList(grfDf$graph, render_graph). See the code for caveats.
```
require(knitr)
#' Render a list of htmlWidgets using various tricks
#'
#' #param widgetList A list of htmlWidget objects to be rendered
#' #param renderFunction The function to render individual widgets. It can be either a name
#' of the rendering function, e.g., "render_graph" in DiagrammeR, or the actual function to
#' be passed to this call.
#' #return The knitted string. This is to be included in the output by using `r renderHtmlWidgetList(...)`;
#' #details This is a collection of various tricks. See the URL citations in the code.
#' Note that this code does alliterate global variables starting with "renderHtmlWidgetList_".
#' You may want to delete them using rm(list = ls(pattern="renderHtmlWidgetList_*")).
#' #examples Inlcude the following in the Rmd directly
#' `r require(DiagrammeR); renderHtmlWidgetList(grfDf$graph, render_graph)`
#'
#' #export
renderHtmlWidgetList <- function(widgetList, renderFunction){
# error checking
stopifnot(is.list(widgetList))
# handles if the renderFunction is actually a function
# http://stackoverflow.com/questions/10520772/in-r-how-to-get-an-objects-name-after-it-is-sent-to-a-function
if(is.function(renderFunction)) {
# convert back to string, because we need to knit it later
renderFunction <- deparse(substitute(renderFunction))
}
stopifnot(is.character(renderFunction) & length(renderFunction)==1)
stopifnot(exists(renderFunction, mode = "function"))
# inject global vars; make sure we have a unique global var name
gVarName<- paste0("renderHtmlWidgetList_", sample(1:10000, 1))
while (exists(gVarName)) {
gVarName<- paste0("renderHtmlWidgetList_", sample(1:10000, 1))
}
# assigning widgetList to a global temp var
# http://stackoverflow.com/questions/5510966/create-a-variable-name-with-paste-in-r
assign(gVarName, widgetList, envir = .GlobalEnv)
# solution from https://gist.github.com/ReportMort/9ccb544a337fd1778179
out <- NULL
knitPrefix <- "\n```{r results='asis', cache=FALSE, echo=FALSE}\n\n"
knitSuffix <- "\n\n```"
for (i in 1:length(widgetList)) {
knit_expanded <- paste0(knitPrefix, renderFunction, "(", gVarName, "[[", i, "]])")
out = c(out, knit_expanded)
}
#invisible(out)
paste(knitr::knit(text = out), collapse = '\n')
}
```

Resources