RMarkdown: Multiple ggplots in same chunk using loop - r

I am trying to generate multiple plots (in ggplot2) using a for loop within a single chunk in an RMarkdown document.
When I hardcode the code to generate the two plots, the plots are rendered as expected. See section in my code titled "Hardcoded Method".
But, when I load the parameters for the two plots in a list and loop through the list, the plots are not showing up. I don't see any errors either. Please see section of my code titled "Loop Method".
Can anyone please tell me what is going on and how I can fix it? Thanks.
Karthik.
Here is my code:
---
title: "Test for multiple plots"
author: "KC"
date: "4/3/2020"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
# Hardcoded Method
Sample Plot - One plot at a time
```{r Multiple Plots separately listed, echo=TRUE, fig.keep="all"}
library(ggplot2)
library(datasets)
combo = c("temperature", "pressure")
ggplot(pressure,
mapping=aes(x=base::get(combo[1]), y=base::get(combo[2]))) +
geom_point() +
labs(x=combo[1],
y=combo[2],
title=paste("Hardcoded Method:", paste(combo, collapse=" vs ")))
combo = c("pressure", "temperature")
ggplot(pressure,
mapping=aes(x=base::get(combo[1]), y=base::get(combo[2]))) +
geom_point() +
labs(x=combo[1],
y=combo[2],
title=paste("Hardcoded Method:", paste(combo, collapse=" vs ")))
```
# Loop Method
Now, I use a loop method to generate the same plots.
```{r Multiple Plots in a loop, echo=TRUE, fig.keep="all"}
library(ggplot2)
library(datasets)
combos = list(c("temperature", "pressure"), c("pressure", "temperature"))
for (combo in combos) {
# combo = combos[[1]]
print(paste("Plotting", paste(combo, collapse=" vs ")))
ggplot(pressure,
mapping=aes(x=base::get(combo[1]), y=base::get(combo[2]))) +
geom_point() +
labs(x=combo[1],
y=combo[2],
title=paste("Loop Method:", paste(combo, collapse=" vs ")))
}
```

When using a for loop in a code chunk with Markdown files, you need to explicitly print() the plot. So, the following code would not work:
for (i in length(x)) {
ggplot(...)
}
You need to convert to something like this:
for (i in length(x)) {
p <- ggplot(...)
print(p)
}

Related

R markdown: Use for loop to generate text and display figure/table

I think the R markdown can generate sections of text using for loop, see this post. However, I wonder if there is any possibility to generate figures and tables as well.
So I made a simple example. Assume in R markdown, I want to have the markdown language and display the table and plot below.
This will return a table and a plot.
df<- data.frame(
name = LETTERS[1:12],
data = runif(n = 12))
new_df<-some_function(df,1)
formattable(new_df)
plot(new_df$data)
where some_function is a simple function that does the following
some_function<-function(df,loc){
df$data<-df$data+loc
return(df)
}
So I hope to get this repeated 5 times, which means generating the below selection 5 times.
This will return a table and a plot.
(figure: pretend there displayed a figure)
(table: pretend there displayed a table)
How should I write the code using some template to display the tables and figures? The code for generating a list of new_df is below.
df_list=list()
for (i in 1:5){
new_df<-some_function(df,i)
df_list[[i]]<-new_df
}
The goal is to display the tables formattable(df_list[[i]]) and figures plot(df_list[[i]]$data) under the 5 separate sections. (Assume each section will have more meaningful text content than the example I made) Something like this screktch below.
template <- "## This will return a table and a figure.
Table is: formattable(df_list[[i]])
Figure is: plot(df_list[[i]]$data)
"
for (i in 1:5) {
current <- df_list[[i]]
cat(sprintf(template, current,current$data))
}
Is that possible to accomplish this? Any thoughts or ideas are very welcome.
You can use result = 'asis' inside the r chunk and use cat() to create the sections.
---
title: "R Notebook"
output:
html_document
---
## Example
```{r, results='asis'}
require(ggplot2)
for(i in 1:5){
cat("### Section ", i, "\n")
df <- mtcars[sample(5),]
tb <- knitr::kable(df, caption = paste0("Table",i))
g1 <- ggplot2::ggplot(df, aes(x = mpg, y = disp, fill = gear)) +
ggplot2::geom_point() +
ggplot2::labs(title = paste0("Figure ", i))
cat("\n")
print(g1)
print(tb)
cat("\n")
}
```

Multiple Plot outputs from a function in rmarkdown

I have a function that produces 2 chart objects and I'd like to use this function in an rmarkdown file and knit it so that I get an HTML report. The problem I'm facing is that when I use this function, I get an output that looks something like this:
Here "Plot1" and "Plot2" are the actual plots that get rendered. What changes to I need to make so that I only get the following?
Plot1
Plot2
Here is the code chunk from my R-markdown file.
```{r OverallGRP106_trended_fav, echo=FALSE, fig.align="center", message=FALSE, warning=FALSE, paged.print=TRUE}
trended_fav(psc_surv,"Overall","GRP106")
```
The return statement of function "trended_fav" is as follows:
return(list(plot(p4), plot(p1)))
I think you'll be ok if you assign the function's output to a variable and then call up the element of the variable separately, like so:
---
output: html_document
---
```{r setup, include=FALSE}
library(ggplot2)
trended_fav <- function() {
p4 <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p1 <- ggplot(txhousing, aes(year, median)) + geom_point()
plot_list <- list(p4,p1)
return(plot_list)
}
a <- trended_fav()
```
## First report
Here it is:
```{r}
a[1]
```
## Second report
This one is even better:
```{r}
a[2]
```

Looping through a list of ggplots and giving each a figure caption using Knitr

I am creating a series of plots using ggplot2. Each of these are programmatically named and I want use the names to give each their own figure caption. I want to pull the names from the list and pass them to fig.cap dynamically.
Is there a way to do this? Here is a MCVE, and you can switch between the list and the individual plots to see the figures disappear or show up:
---
output: pdf_document
---
```{r, include = FALSE}
library(ggplot2)
library(knitr)
opts_chunk$set(echo=FALSE)
```
```{r}
## Plot 1
listOfPlots <- list(
# Plot 1
ggplot(data = diamonds) +
geom_point(aes(carat, price)),
## Plot 2
ggplot(data = diamonds) +
geom_point(aes(carat, depth))
)
names(listOfPlots) <- c("This is caption 1", "This is caption 2")
```
```{r, fig.cap = c("This is caption 1", "This is caption 2"), echo=TRUE}
listOfPlots
# listOfPlots$`This is caption 1`
# listOfPlots$`This is caption 2`
```
Notes:
Yihui and others have said (https://groups.google.com/forum/#!topic/knitr/MJVLiVyGCro), that to have multiple figures in a chunk and give them a figure caption, you need to have echo=TRUE because inline figures and pandoc stuff.
I don't want to show code and the number of figures may be variable so I don't want to hard code things. Interestingly, using a list of ggplots also does not work even if echo=TRUE. I have to individually call each ggplot.
There needs to be spaces between plots in R Markdown if they are to be given their own caption. As stated by Yihui on your link, the trick here is to add some line breaks between the two images.
```{r, fig.cap=c("Caption 1", "Caption 2")}
listOfPlots[[1]]
cat('\n\n')
listOfPlots[[2]]
```
Note, the double square brackets are used to only return the plot itself.
Using a Loop
Assuming you are looking for a more generalizable method which could work for a list of any length, we can automate the creation of the line breaks between the plots using a loop. Note that results="asis" is required in the chunk header:
```{r, fig.cap=c("Caption 1", "Caption 2"), echo=FALSE, results="asis"}
for(plots in listOfPlots){
print(plots)
cat('\n\n')
}
```
As a final tip, you may wish to use the name of the list directly within the caption. The syntax {r, fig.cap = names(listOfPlots)} would be able to achieve this.

How to embed plots into a tab in RMarkdown in a procedural fashion?

I can embed plots using just RMarkdown's {.tabset}
#### Heading {.tabset}
##### Subheading 1
```{r, echo=F}
df[[1]]
```
This produces individual tabs with the specified graphs (df is a list of graphs, calling df[[i]] produces a graph) in the preview pane (renders all the graphs inline in RStudio).
And I can generate just the tabs using a for loop.
```{r, results='asis', echo = FALSE}
for (i in 1:length(gg0)) {
cat("##### ",q$Subheading[i],"\n")
}
```
And this produces the desired output - the tabs with the names in the Subheading column.
However, I am stuck in trying to generate the graphs themselves using the for loop similar to how I did when I coded it manually.
Extending the above, I tried to generate the markdown that produced the initial output but the plot fails to generate (both in the inline markdown and preview).
```{r, results='asis', echo = FALSE}
for (i in 1:length(gg0)) {
cat("##### ",q$Subheading[i],"\n")
cat('```{r, echo=F} \n')
cat("gg0[[",i,"]]\n")
cat('``` \n')
}
```
Maybe I am missing a finer point regarding markdown? I have tried various patterns using cat (and even without)
I would prefer a RMarkdown solution but other solutions are just as welcome.
I played around a little and found a solution. You have to use print within the asis code chunk...
```{r}
library(ggplot2)
gg0 <- list()
gg0[[1]] <- ggplot(mtcars, aes(mpg, hp)) + geom_point()
gg0[[2]] <- ggplot(mtcars, aes(mpg, disp)) + geom_point()
gg0[[3]] <- ggplot(mtcars, aes(mpg, drat)) + geom_point()
headings <- c('hp','disp','drat')
```
#### Heading {.tabset}
```{r, results='asis', echo = FALSE}
for (i in 1:length(gg0)) {
cat("##### ",headings[i],"\n")
print(gg0[[i]])
cat('\n\n')
}
```
As an explanation, the cat command together with results='asis' produces the markdown code for a lower level headline and prints the ggplot graph afterwards. Since we used `{.tabset} in the parent headline, it creates the plots in separate tabs.
Adding both plot.new(), dev.off() inside the for loop solves the problem of adding all the figures in the last tab. See the complete solution here.

Print RMarkdown captions from a loop

I am creating a series of plots from within a loop in an RMarkdown document, then knitting this to a PDF. I can do this without any problem, but I would like the caption to reflect the change between each plot. A MWE is shown below:
---
title: "Caption loop"
output: pdf_document
---
```{r, echo=FALSE}
library(tidyverse)
p <-
map(names(mtcars), ~ggplot(mtcars) +
geom_point(aes_string(x = 'mpg', y = .))) %>%
set_names(names(mtcars))
```
```{r loops, fig.cap=paste(for(i in seq_along(p)) print(names(p)[[i]])), echo=FALSE}
for(i in seq_along(p)) p[[i]] %>% print
```
I have made a first attempt at capturing the plots and storing in a variable p, and trying to use that to generate the captions, but this isn't working. I haven't found too much about this on SO, despite this surely being something many people would need to do. I did find this question, but it looks so complicated that I was wondering if there is a clear and simple solution that I am missing.
I wondered if it has something to do with eval.after, as with this question, but that does not involve plots generated within a loop.
many thanks for your help!
It seems that knitr is smart enough to do the task automatically. By adding names(mtcars) to the figure caption, knitr iterates through these in turn to produce the correct caption. The only problem now is how to stop all of the list indexes from printing in the document...
---
title: "Caption loop"
output: pdf_document
---
```{r loops, fig.cap=paste("Graph of mpg vs.", names(mtcars)), message=FALSE, echo=FALSE, warning=FALSE}
library(tidyverse)
map(
names(mtcars),
~ ggplot(mtcars) +
geom_point(aes_string(x = 'mpg', y = .))
)
```
In case this might be useful to somebody. Here is an adaptation of Jonny's solution for captions without printing list indices. This can be achieved by using purrr::walk instead of purrr::map. Also included is a latex fig label and text that references each plot.
---
title: "Loop figures with captions"
output:
pdf_document
---
```{r loops, fig.cap=paste(sprintf("\\label{%s}", names(mtcars)), "Graph of mpg vs.", names(mtcars)),results='asis', message=FALSE, echo=FALSE, warning=FALSE}
library(tidyverse)
library(stringi)
walk(names(mtcars),
~{
p <- ggplot(mtcars) +
geom_point(aes_string(x = 'mpg', y = .))
#print plot
cat('\n\n')
print(p)
#print text with refernce to plot
cat('\n\n')
cat(sprintf("Figure \\ref{%s} is a Graph of mpg vs. %s. %s \n\n", ., .,
stri_rand_lipsum(1)))
cat("\\clearpage")
})
```

Resources