44

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.

zx8754
  • 52,746
  • 12
  • 114
  • 209
Isaac Fratti
  • 475
  • 1
  • 4
  • 8
  • https://stackoverflow.com/questions/61330051 doesn't seem to have anything to do with spaces, moreso just putting everything into one, one-line `cat` statement – Brian D Oct 07 '22 at 21:27

4 Answers4

69

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")
}
```
jclouse
  • 2,289
  • 1
  • 20
  • 25
  • WoW thats super helpfull (please mark as correct answer) - it also answers this question: http://stackoverflow.com/questions/17178831/generate-markdown-comments-within-for-loop – Timo Kvamme Oct 20 '16 at 13:27
  • 17
    Where on earth is that "2 spaces in front of \n" documented? – Brandon Bertelsen Mar 30 '18 at 22:24
  • 2
    I try this solution in my R Notebook and get May Air Quality June Air Quality plot plot i.e., my text outputs are all jammed together in one block and then the graphs come along. Anyone know what's up with that? – David Kaufman May 29 '18 at 13:36
  • 3
    One drawback might be that we cannot do cross-reference now. – Liang Zhang Jun 15 '18 at 13:22
  • 1
    Great answer. It can probably be helpful to use multiline strings and the glue package if one is trying to get even closer to the „feel“ of actually looping around some longer markdown text: cat(glue("...")) – Magnus Sep 20 '20 at 23:11
  • I've also seen this documented similarly here. https://bookdown.org/yihui/rmarkdown-cookbook/results-asis.html – fullera Jul 06 '21 at 05:28
  • I have the same issue as David Kaufmann, the titles are all together, and then the plots all together. – Brigitte Aug 05 '22 at 15:29
  • This is really cool! Thanks for sharing. You'll need to wrap the plot in print() to make the same solution to work for ggplot2. – Mikko Sep 30 '22 at 09:22
10

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('')
}
```
andschar
  • 3,504
  • 2
  • 27
  • 35
  • 1
    For me, this should be the answer due to simplicity. Managing `cat(' \n')` everywhere proved cumbersome and was causing some errors in an entire document if it was not positioned correctly in one section. – fullera Jul 12 '21 at 01:19
  • @andschar Could you have a look [here](https://stackoverflow.com/q/56983105/5784831)? It seems, you suggestion gets close, but I still face problems with the leaflet map. (And I am not sure, whether {.tabset} works with pander.) Thanks a lot. – Christoph Jul 20 '21 at 13:39
3

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>>
}
MikeS
  • 103
  • 7
2

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.

  • 1
    I like this answer as it works with all types of plots and complicated documents. I suggest that instead of copy pasting, you run the code you created above in an R-file, and add sink("output_from_print_code.Rmd") which will write the entire code in there, from which you can just load it into your main R-markdown document by adding the chunk: ```{r, child=c('output_from_print_code.Rmd')} ``` – Brigitte Apr 17 '23 at 14:13
  • I'd love such a solution, but working strait in the Rmarkdown universe. Having a loop creating a chunk allows to change chunk options conditionally. E.g fig.width , ... – MsGISRocker Jun 12 '23 at 17:23