Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sequentially output partial (base r) plots in rmarkdown/knitr?

Suppose I have the following rmarkdown code:

---
title: "Untitled"
author: "Author"
date: "04/12/2019"
output: ioslides_presentation
---

## Slide title

```{r echo=FALSE}
plot(1:10, axes = FALSE, ty = "n")
axis(1)
## Next
axis(2)
## Next
points(1:10, 1:10)
```

At each of the ## Next points I would like to output the current state of the plot, so that I can sequentially reveal parts of the plot. My ultimate goal is to create subsequent slides in an ioslides presentation with the sequential plots in them, all from the code above (with the caveat that ideally I would not want later lines of code to be able affect earlier ones, as could occur above).

I would like it to have the same effect as how I'm currently solving it:

---
title: "Untitled"
author: "Author"
date: "04/12/2019"
output: ioslides_presentation
---

```{r setup, include=FALSE}

## Set up environment for running the code
env <- new.env()

## Source code to run, as a list of quotes

full_src <- list(
  quote({
    plot(1:10, axes = FALSE, ty = "n")
    axis(1)
    }),
  quote({
    axis(2)
    }),
  quote({
    points(1:10, 1:10)
    })
)

```

## Slide title

```{r echo=FALSE}
# Evaluate first set of lines
eval(full_src[[1]], envir = env)
# Reset environment so later code can't affect earlier code
rm(list = ls(envir = env), envir = env)
```

## Slide title

```{r echo=FALSE}
# Evaluate first and second set of lines
invisible(sapply(1:2, function(i) eval(full_src[[i]], envir = env)))
# Reset environment so later code can't affect earlier code
rm(list = ls(envir = env), envir = env)
```

## Slide title

```{r echo=FALSE}
# Evaluate all lines
invisible(sapply(1:3, function(i) eval(full_src[[i]], envir = env)))
```

The slides that are output look like this:

Four slides

But as you can see, that is clunky and not very general. Important features of a solution would be that the lines of code for the plot is as close together as possible, and only written once; ideally, I could just do this with a chunk option/hook.

The best kind of answer would retain the ability to echo the new lines of code added to the graph (e.g., the ones between the ## Next markers) yet yield the whole plot up to that point.

What's the best way to achieve this?

Edit: Also, I want to be able to group changes to a plot, and not just output every to a new plot, so fig.keep won't work.

like image 958
richarddmorey Avatar asked Dec 04 '19 16:12

richarddmorey


1 Answers

The chunk option fig.keep can take a numeric vector to index the plots, so you can choose the last three plots via fig.keep = 2:4 (or remove the first plot via fig.keep = -1).

To group plots on different slides, you can dynamically add slide titles. You can either use cat('\n\n## Title\n\n') with the chunk option results = 'asis', or with knitr::asis_output().

Below is a full example:

---
title: "Untitled"
author: "Author"
date: "04/12/2019"
output: ioslides_presentation
---

```{r, include=FALSE}
new_slide = function(title = '\n\n## Next\n\n') {
  knitr::asis_output(title)
}
```

## Slide title

```{r echo=FALSE, fig.keep=2:4}
plot(1:10, axes = FALSE, ty = "n")
axis(1)

new_slide()
axis(2)

new_slide()
points(1:10, 1:10)
```

Note that there is a bug in the current version of knitr, which I just fixed on Github. For now you need to use the Github version of knitr:

if (packageVersion('knitr') <= '1.26')
  remotes::install_github('yihui/knitr')
like image 137
Yihui Xie Avatar answered Nov 13 '22 21:11

Yihui Xie