Create a loop that includes both a code chunk and text - r

I am trying to figure out how to create a loop that inserts some text into the rmarkdown file, and then produces the graph or table that corresponds to that header. The following is how I picture it working:
for(i in 1:max(month)){
### `r month.name[i]` Air quaility
```{r, echo=FALSE}
plot(airquality[airquality$Month == 5,])
```
}
This ofcourse just prints the for loop as text, if i surround the for loop with r`` I would just get an error.
I want the code to produce an rmd file that looks like this:
May Air Quality
Plot
June Air Quality
Plot
and so on and so forth.
Any ideas? I cannot use latex because I at my work they do not let us download exe files, and I do not know how to use latex anyways. I want to produce a word document.

You can embed the markdown inside the loop using cat().
Note: you will need to set results="asis" for the text to be rendered as markdown.
Note well: you will need two spaces in front of the \n new line character to get knitr to properly render the markdown in the presence of a plot out.
# Monthly Air Quality Graphs
```{r pressure,fig.width=6,echo=FALSE,message=FALSE,results="asis"}
attach(airquality)
for(i in unique(Month)) {
cat(" \n###", month.name[i], "Air Quaility \n")
#print(plot(airquality[airquality$Month == i,]))
plot(airquality[airquality$Month == i,])
cat(" \n")
}
```

As mentioned here, you could also make use of the pander package:
# Monthly Air Quality Graphs
```{r pressure2, fig.width=6, echo=FALSE, message=FALSE, results="asis"}
library(pander)
for (i in unique(airquality$Month)) {
# Inserts Month titles
pander::pandoc.header(month.name[i], level = 3)
# Section contents
plot(airquality[airquality$Month == i,])
# adding also empty lines, to be sure that this is valid Markdown
pander::pandoc.p('')
pander::pandoc.p('')
}
```

Under some conditions I find it helpful to write a loop that writes chunk code rather than write a chunk that runs a loop. Weird solution but it has worked for me beautifully in the past when a bare bones set of chunks is all I need. For your airquality case it would look like this:
## model chunk ##
# ## May Air Quality
# ```{r May}
#
# plot(airquality[airquality$Month == 5,])
#
# ```
# all months in airquality
aqmonths <- c("May",
"June",
"July",
"August",
"September")
for (m in aqmonths) {
cat(
paste0(
"## ", m, " Air Quality",
"\n\n",
"```{r ", m, "}",
"\n\n",
"plot(airquality[airquality$Month == ", match(m, months), ",])",
"\n\n",
"```",
"\n\n"
)
)
}
This will print code for all 5 chunks to the console, and then I can copy and paste into a .Rmd document. It is possible to include any chunk options such as captions or fig arguments in the chunk-writing loop as well. Depending on what else you try to bring in, using functions like match() as in the example is often helpful.
Pros: Preserves ability to use cross-references and set individual captions or options.
Cons: Making changes to all chunks usually requires re-copying the entire output of the chunk-writing loop, which can be tiresome and a bit unwieldy.

What about reusing the chunks inside a loop using <<label>> as described here: https://bookdown.org/yihui/rmarkdown-cookbook/reuse-chunks.html
Label your chunk, set eval=F
```{r my_chunk, echo=FALSE, eval=F}
plot(airquality[airquality$Month == 5,])
```
Then loop
for(i in 1:max(month)){
<<my_chunk>>
}

Related

Using an R script to create a highly repetitive Rmarkdown script

I analyze survey results regularly and like to use Rmarkdown so I can make nice HTML output of the results.
The surveys can be many questions (like 40), so creating 40 code chunks, with highly repetitive code and headers, can be annoying. And I can easily do this with a loop in R, I think. However, I'm just stuck on how to combine these 2 processes!
This was close --
how to create a loop that includes both a code chunk and text with knitr in R
But in the end, it was just a loop, and not very flexible. So, I couldn't add a figure to question 22 (or whatever).
### Question 1
#### `r key$Question_Text[key$Question=="Q1"][1]`
```{r chunk1}
quest <- "Q1"
# code for question 1
```
### Question 2
#### `r key$Question_Text[key$Question=="Q2"][1]`
```{r chunk2}
quest <- "Q2"
# Identical code for question 2
```
....and so on....
### Question 35
#### `r key$Question_Text[key$Question=="Q35"][1]`
```{r chunk35}
quest <- "Q35"
# Identical code for question 35
```
Because sometimes, a question has a special type of figure or tweak, I want the output to be something I can paste into RMD and make all the changes there. I just want to skip ahead as much as possible... by making all the boring, identical steps, fully automated.
make strings that I can refer to in loop
question<-paste(rep("Question",20), 1:20, sep=" ")
qnum<-paste0(rep("Q",20), 1:20, sep="")
quest_text_code <- paste0("#### ","`r key$Question_Text[key$Question==", "\"",qnum[i],'"' ,"][1]`")
chunk <- paste(rep("chunk",20), 1:20, sep="")
use sink() to send to a text file
sink("outfile.txt")
loop and paste and output into the sink
for(i in 1:20){ cat(paste("###", question[i], "\n", "\n",
quest_text_code,"\n", "\n",
"```", "{r ", chunk[i], "}", "\n","\n",
"function.dat(", qnum[i], ")","\n", "\n",
"function.dat.nr(", qnum[i], ")","\n", "\n",
"```", "\n", "\n"))
}
dev.off() # ends sink
After that, I was able to copy to an RMD file and use find and replace on a few glitches (extra leading spaces) I also had trouble adding "" marks with paste.

How to show formatted R output with results='asis' in rmarkdown

Is there a way of showing formatter R output in rmarkdown/knitr when using results = 'asis'?
An example would be the following function:
myfun <- function() {
cat("hello!\n")
cat(c("one" = 1, "two" = 2))
}
Then, this chunk will print the second cat on a new row:
```{r}
myfun()
```
But this will ignore the formatting from myfun:
```{r, results = "asis"}
myfun()
```
Is there a way of keeping results='asis' but at the same time keep the output of myfun formatted as intended?
You can use the knitr chunk option results = "asis" if you are happy to add two or more spaces at the end of the line. That is, instead of "hello\n", you need to write "hello \n" to trigger the line break.
Example R Markdown code:
---
output: html_document
---
```{r}
myfun <- function() {
cat("hello! \n")
cat(c("one" = 1, "two" = 2))
}
```
```{r results = "asis"}
myfun()
```
Gives
Why the blank spaces? It's because two spaces at the end of a line are used to indicate a hard line break in markdown. For example, this quote is taken from Pandoc's Markdown (which is the default markdown flavour R Markdown uses):
Paragraphs
A paragraph is one or more lines of text followed by one or more blank lines. Newlines are treated as spaces, so you can reflow your paragraphs as you like. If you need a hard line break, put two or more spaces at the end of a line.

Suppressing lengthy kable tables in Rmd inline output while retaining in final PDF

I'm trying to get R to suppress lengthy tables created with kable & kableExtra in the inline Rmd output while keeping them in the final knitted PDF. I only want to do this with a few chunks, so I'd rather not go the route of setting the global option of turning off all of the inline output.
I've gone through several iterations of the chunk options listed here: https://yihui.name/knitr/demo/output/ and here: https://yihui.name/knitr/options/#plots but haven't landed on the right one, so I'm not sure if I'm even looking in the right place or if I've just skipped over the correct setting.
YAML:
---
output:
pdf_document:
latex_engine: xelatex
---
Code:
```{r}
# Setup
library(knitr)
library(kableExtra)
# Create some data
dat <- data.frame ("A" = c(1:5),
"B" = c("Imagine a really long table",
"With at least 50 rows or so",
"Which get in the way in the inline output",
"But I want in the final PDF",
"Without influencing the other chunks")
)
# Produce the table
kable(dat, booktabs=TRUE, format="latex", longtable=TRUE) %>%
kable_styling(latex_options="HOLD_position")
```
Inline output that I don't want to have pop up every time I run this thing:
\begin{table}[H]
\centering
\begin{tabular}{rl}
\toprule
A & B\\
\midrule
1 & Imagine a really long table\\
2 & With at least 50 rows or so\\
3 & Which get in the way in the inline output\\
4 & But I want in the final PDF\\
5 & Without influencing the other chunks\\
\bottomrule
\end{tabular}
\end{table}
If you can imagine having to scroll through 50-100 lines of this stuff while trying to write code, you can see how annoying and time consuming it gets to be.
This function detects that an RMarkdown document is being processed inline in RStudio, rather than through knitting:
is_inline <- function() {
is.null(knitr::opts_knit$get('rmarkdown.pandoc.to'))
}
So you could wrap your problematic code in something like
if (!is_inline()) {
kable(dat, booktabs=TRUE, format="latex", longtable=TRUE) %>%
kable_styling(latex_options="HOLD_position")
}
or make another function
hide_inline <- function(x) {
if (is_inline())
cat("[output hidden]")
else
x
}
and add it to your pipe:
kable(dat, booktabs=TRUE, format="latex", longtable=TRUE) %>%
kable_styling(latex_options="HOLD_position") %>%
hide_inline()
Both of these have the disadvantage of requiring a modification to your code that will be shown if echo=TRUE. I don't think there is any chunk option that is equivalent to hide_inline, but I could be wrong.
If you were really desperate, you could use echo=2:3 or similar to hide the if (!is_inline()) { and } lines.

Generate both markdown comments and html wigets in a loop rmarkdown

I'm trying to generate a flexdashboard, creating each page from within a loop and with each of the generated pages containing a dygraph (although any HTML widget ought to behave the same).
I have looked extensively and it seems that rmarkdown comments can be generated using cat("title") (as per the solution here: generate markdown comments within for loop).
The HTML widgets on the other hand only behave nicely if you use htmltools::tagList() (as per the solution here:For loop over dygraph does not work in R).
I dont have working code to share, but this broadly gives the picture of what I am hoping to achieve:
for (i in 1:ncol(downloadedData)){
fund_NAVS <- downloadedData[,i] #this is an xts object
fund_NAVS <- fund_NAVS[!is.na(fund_NAVS)]
cat("pageTitle")
cat("===================================== \n")
cat("Row\n")
cat("------------------------------------- \n")
cat("### Page title")
dygraph(fund_NAVS)
}
I've been able to get this to partially work using pander::pandoc.header. However, getting content (plots and HTML objects) to actually show up in the final HTML file is a different story.
---
output:
flexdashboard::flex_dashboard:
orientation: columns
source: embed
vertical_layout: fill
---
```{r results='asis'}
library(pander)
pages <- 5
cols <- 2
sections <- 3
for (p in 1:pages) {
pandoc.header(paste("Page", p), level = 1)
for (c in 1:cols) {
pandoc.header(paste("Column", c), level = 2)
for (s in 1:sections) {
pandoc.header(paste("Section", s), level = 3)
cat("hi")
pandoc.p("")
}
}
}
```
I was able to autogenerate content by building r chunks explicitly then kniting them inline with r paste(knitr::knit(text = out)). This amazing line of code was found in an SO post.
In my case, I wanted to produce a series of graphs, each with a separate tab, with different content. Each graph was similar but there were numerous (about 15) and I didn't want to copy/paste all of the separate chunks.
Here is a gist you can download of a more simple example. (The code is also below but note that I add \ before each chunk so that it rendered as a single block of code so remove the \ before running.) I built a much more complicated function to build graphs, but the idea of the R chunks can be carried forward to any list object containing htmlwidgets as elements.
---
title: "Loop to Auto Build Tabs Containing htmlwidgets"
output: flexdashboard::flex_dashboard
---
\```{r setup, echo =FALSE, eval = TRUE}
library(tidyverse)
library(flexdashboard)
library(highcharter)
labels <- mtcars %>% names # these will serve as labels for each tab
# create a bunch of random, nonsensical line graphs
hcs <- purrr::map(.x = mtcars, ~highcharter::hchart(mtcars, y = .x, type = 'line')) %>%
setNames(labels) # assign names to each element to use later as tab titles
\```
Page
====================
Column {.tabset .tabset-fade}
-----------------------------
<!-- loop to build each tabs (in flexdashboard syntax) -->
<!-- each element of the list object `out` is a single tab written in rmarkdown -->
<!-- you can see this running the next chunk and typing `cat(out[[1]])` -->
\```{r, echo = FALSE, eval = TRUE}
out <- lapply(seq_along(hcs), function(i) {
a1 <- knitr::knit_expand(text = sprintf("### %s\n", names(hcs)[i])) # tab header, auto extracts names of `hcs`
a2 <- knitr::knit_expand(text = "\n```{r}") # start r chunk
a3 <- knitr::knit_expand(text = sprintf("\nhcs[[%d]]", i)) # extract graphs by "writing" out `hcs[[1]]`, `hcs[[2]]` etc. to be rendered later
a4 <- knitr::knit_expand(text = "\n```\n") # end r chunk
paste(a1, a2, a3, a4, collapse = '\n') # collapse together all lines with newline separator
})
\```
<!-- As I mentioned in the SO post, I don't quite understand why it has to be -->
<!-- 'r paste(knitr::knit(...)' vs just 'r knitr::knit(...)' but hey, it works -->
`r paste(knitr::knit(text = paste(out, collapse = '\n')))`

R markdown rerun the same section of report with different parameter

I'm familiar with R markdown "parameters".
However, say I want to generate the same report (same chart, same table) but for 5 different regions.
Is there a way to do this elegantly in a loop or lapply or do I need to make several sections. So in pseudo code I want to do something like:
for(i in 1:5):
Bunch of text
table[i]
plot[i]
Instead of
bunch of text
table[1]
plot[1]
bunch of text
table[2]
plot[2]
...
Put another way, I want to functionalize a "section" of the report, and then I can call
for(i in 1:5):
makeReport(i)
And it will go in, put in the text, figures, etc associated with index i.
You have to call print explicitly if inside for loop:
```{r}
for(i in 1:2) {
print(summary(cars[,-i]))
plot(cars[,-i])
}
```
or
```{r}
makeReport <- function(i) {
print(summary(cars[,-i]))
plot(cars[,-i])
}
for(i in 1:2) {
makeReport(i)
}
```
Update
As Stéphane Laurent already demonstrated in Dynamic number of calls to a chunk with knitr
you can define a child .rmd:
test_section.rmd
Header: `r i`-th cars
```{r}
print(summary(cars[,-i]))
plot(cars[,-i])
```
and in the main rmd file concatenate the results:
```{r runall, include=FALSE}
out <- NULL
for (i in 1:2) {
out <- c(out, knitr::knit_child('test_section.rmd'))
}
```
`r paste(out, collapse = '\n')`

Resources