rmarkdown/purrr: remove list indices between plots - r

When I knit a document containing multiple plots obtained through purrr:map function, I get text slides in between each plot slide containing unwanted list index information (see image slides 2, 4, and 6). I'd like to remove these and just have plots.
I've tried results = "hide" and results = FALSE in the header.
These just return one plot instead of many, AND the text is still
there.
I've tried adding invisible() around my code as recommended
here. I don't see a difference.
How can I remove these and just have three slides with the three plots with no text?
---
title: "Reprex"
output: powerpoint_presentation
---
```{r include=FALSE}
library(tidyverse)
```
```{r echo=FALSE, results = FALSE}
ys <- c("mpg","cyl","disp")
ys %>% map(function(y)
invisible(ggplot(mtcars, aes(hp)) + geom_point(aes_string(y=y))))
```

Try this:
To suppress the console output use purrr::walk instead of map. See e.g. https://chrisbeeley.net/?p=1198
To get each plot printed on a separate slide use results='asis' and add two newlines via cat('\n\n') after each plot.
---
title: "Reprex"
output: powerpoint_presentation
---
```{r include=FALSE}
library(tidyverse)
```
```{r echo=FALSE, results='asis'}
ys <- c("mpg","cyl","disp")
walk(ys, function(y) {
p <- ggplot(mtcars, aes(hp)) + geom_point(aes_string(y=y))
print(p)
cat('\n\n')
})
```

Related

R Markdown Presentation: showing one plot in each slide

I have a list of 300 plots and want one PowerPoint slide to show one plot (300 slides). What's the best way to achieve this?
A toy example using the built-in iris dataset to create a list of plots:
purrr::map(names(iris[,-5]), function(col_name){
plot = iris %>%
ggplot(aes(x = !!as.name(col_name))) +
geom_histogram()
return(plot)
})
I hope to create PowerPoint slides with one plot on each slide.
I will be using the iris data set, which is a built-in data set often used to populate example code.
With the following code in a Rmd file:
---
title: "Test_PowerPoint"
author: "KoenV"
date: '2022-06-30'
output: powerpoint_presentation
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
```{r include=FALSE, warning=FALSE, message=FALSE}
library(tidyverse)
library(purrr)
```
```{r, iris, fig.cap="A scatterplot.", echo=FALSE, warning=FALSE, message=FALSE}
## your code
purrr::map(names(iris[,-5]), function(col_name){
plot = iris %>%
ggplot(aes(x = !!as.name(col_name))) +
geom_histogram()
return(plot)
})
```
And after hitting the "knit" button, you will see a PowerPoint presentation appearing, with the following lay-out:
Please let me know, whether this is what you wanted.

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

why does kable not print when used within a function in rmarkdown

I have to repeat certain outputs in many rmarkdown reports and want to write a function to use for this.
Calling a function outputs plots ok when I knit the rmd file but not kable data frames.
For example
---
title: "Markdown example"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
# Markdown example
```{r mtcars}
make_outputs <- function(){
knitr::kable(head(mtcars))
plot(mtcars$mpg, mtcars$cyl)
hist(mtcars$cyl)
}
make_outputs()
```
Displays the plots but not the kable table.
You can do this by using print to print the kable output, setting the results="asis" of the code chunk and then using kable_styling from package kableExtra.
This works for me:
```{r mtcars, results='asis'}
library(kableExtra)
library(knitr)
make_outputs <- function(){
print(kable_styling(kable(head(mtcars))))
plot(mtcars$mpg, mtcars$cyl)
hist(mtcars$cyl)
}
make_outputs()
```
Using a return statement with all objects in a list can help here, You can try recordPlot or plot from base R to solve your problem, By putting each of these plots in list, I managed to get the plots along with your table. Changed your code a bit in return statement to plot each of the plots along with tables like this.
Option1:
Using list in return with all the objects binded together without using lapply in function call
---
title: "Markdown example"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
# Markdown example
```{r mtcars}
make_outputs <- function(){
return(list(hist(mtcars$cyl),
knitr::kable(head(mtcars)),
plot(mtcars$mpg, mtcars$cyl)))
}
make_outputs()
```
Another version (In case you don't want the code to print hist output to your html then you can use below function to suppress it.
make_outputs <- function(){
h1 <- plot(hist(mtcars$cyl, plot=FALSE))
h2 <- knitr::kable(head(mtcars))
h3 <- plot(mtcars$mpg, mtcars$cyl)
return(list(h1, h2, h3))
}
Option2:
Another (better version by using invisible function on lapply to suppress NULL printing, then using results='asis' option in the markdown settings as below gives a clean output than earlier.
---
title: "Markdown example"
output: html_document
---
knitr::opts_chunk$set(echo = FALSE)
knitr::opts_knit$set(root.dir= normalizePath('..'))
knitr::opts_chunk$set(error = FALSE)
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
# Markdown example
```{r mtcars, results='asis'}
make_outputs <- function(){
return(list(plot(hist(mtcars$cyl, plot =FALSE)),
knitr::kable(head(mtcars)),
plot(mtcars$mpg, mtcars$cyl)))
}
invisible(lapply(make_outputs(), print))
```
This has given me a histogram, a scatter plot and a table in the knitted html document. Hope this helps, Not sure though if you wanted this way. Please let me know in case you wanted in any other way.
The problem seems to be related to knitr::kable incorrectly detecting the environment for the printing when it is embedded inside a function. This interferes with its ability to correctly figure out how to format. We can hack around this by placing the object to print in the top level environment before we print it.
---
title: "Markdown example"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
print_kable = function(x) {
print(kable_print_output <<- x)
cat('\n')
}
```
# Markdown example
```{r mtcars, results='asis'}
make_outputs <- function() {
print_kable(knitr::kable(head(mtcars)))
plot(mtcars$mpg, mtcars$cyl)
print_kable(knitr::kable(tail(mtcars)))
}
make_outputs()
```
I got something similar working by
moving the knitr::kable(head(mtcars)) inside a return() at the end of the function.
including results = 'asis'
e.g.
---
title: "Markdown example"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
# Markdown example
```{r results = 'asis'}
make_outputs <- function(){
print(plot(mtcars$mpg, mtcars$cyl))
print(hist(mtcars$cyl))
return(knitr::kable(head(mtcars)))
}
make_outputs()
```
If you use ggplot for plots, you will need to wrap your plot inside print()

Figure captions with multiple plots in one chunk

I label my figures like this.
---
title: "xxx"
output:
pdf_document:
fig_caption: true
---
And then in each chunk
```{r, fig.cap="some caption"}
qplot(1:5)
```
This works quite nicely. However in chunks where I plot multiple figures within a loop I can't specify a caption. This produces no caption at all:
```{r, fig.cap="another caption"}
qplot(1:5)
qplot(6:10)
```
How can I specify a figure that counts from the same number as the first chunk for each plot?
You can use a fig.cap argument of length 2 (or the size of your loop):
```{r, fig.cap=c("another caption", "and yet an other")}
qplot(1:5)
qplot(6:10)
```
Found an easy way to dynamically produce plots and add them to the pdf with individual captions, using knitr::fig_chunk as described here. This is also a workaround for OPs comment that message=false (or echo=False or results='asis' for that matter) supresses the fig.cap argument.
```{r my-plots, dev='png', fig.show='hide', echo=FALSE}
# generate plots first
qplot(1:5)
qplot(6:10)
```
```{r, echo=FALSE, results='asis'}
# then put them in the document with the captions
cat(paste0("![some caption](", fig_chunk(label = "my-plots", ext = "png", number = 1), ")\n\n"))
cat(paste0("![another caption](", fig_chunk(label = "my-plots", ext = "png", number = 2), ")\n\n"))
```
Hopefully this helps someone who stumbles upon this question in the future.

How to avoid figure filenames in child calls

I would like to reuse a child file from my parent Rmd after modifying my data. The code seems to work fine, but the first figures are stepped over and all figures are replaced by the last one.
Is there a way to force new filenames with each new call?
This is my Parent.Rmd
XParent
========
```{r Opts, echo=FALSE}
opts_chunk$set(fig.show='asis', fig.keep='all', fig.width=3, fig.height=4, options(digits = 2), dev='jpeg')
```
```{r XLoad}
read_chunk(lines = readLines('XCode.R'))
```
```{r ParentChunk}
```
First child call
---------------
#### NOTICE the data is OK but the figure corresponds to the second child call (Y axis = 1200)
```{r CallChild, child='XChild.Rmd'}
```
#### I now modify the dataframe
```{r}
df$dist <- df$dist * 10
```
Second child call
-----------------
As this is the last case, the figure agrees with the data:
```{r CallChild2, child='XChild.Rmd'}
```
This Child.Rmd
XChild
```{r CodeAndFigs}
```
and XCode.R
## #knitr ParentChunk
df <- cars
colMeans(df)
# Y axis' upper limit is 120
plot(cars)
## #knitr CodeAndFigs
colMeans(df)
plot(df)
The figure in the first child call has been replace by the second figure. I have tried playing with different fig.keep and fig.show options with no luck.
With the latest development version on Github (which will turn into knitr 1.3 very soon on CRAN), you can use the fig.path option to specify different figure paths for the child document in two parent chunks CallChild and CallChild2, e.g.
XParent
========
```{r Opts, echo=FALSE}
opts_chunk$set(fig.show='asis', fig.keep='all', fig.width=3, fig.height=4, options(digits = 2), dev='jpeg')
```
```{r XLoad}
read_chunk(lines = readLines('XCode.R'))
```
```{r ParentChunk}
```
First child call
---------------
#### NOTICE the data is OK but the figure corresponds to the second child call (Y axis = 1200)
```{r CallChild, child='XChild.Rmd', fig.path='figure/child-'}
```
#### I now modify the dataframe
```{r}
df$dist <- df$dist * 10
```
Second child call
-----------------
As this is the last case, the figure agrees with the data:
```{r CallChild2, child='XChild.Rmd', fig.path='figure/child2-'}
```
The child document will inherit options from its parent chunk, so the figure paths will no clash if the parent options are different.

Resources