How to print all recorded plots in a list in R Markdown - r

this is my first question at StackOverflow. If I am doing something wrong let me know to improve the next questions.
I am starting with R Markdown and I have some troubles to print recorded plots with a for loop. Well, before I run Rmarkdown file, I run R function that generate a list of several data frames and plots.
To make it easier I put here a simplified reproducible example of this list just with plot objects.
x <- c(1,2,3,4,5)
y <- c(1,2,3,4,5)
plot(x,y)
abline(h=1)
p1.1 <- recordPlot()
plot(x,y)
abline(h=3)
p1.2 <- recordPlot()
plot(x,y)
abline(h=4)
p2.1 <- recordPlot()
plot(x,y)
abline(h=6)
p2.2 <- recordPlot()
lista<-NULL
lista["p1.1"] <- list(p1.1)
lista["p1.2"] <- list(p1.2)
lista["p2.1"] <- list(p2.1)
lista["p2.2"] <- list(p2.2)
save(new_list, file = "Data.RData")
Then I load this list in R Markdown file like this:
```{r setup}
knitr::opts_chunk$set(echo = TRUE,fig.keep = "all")
load("Data.RData")```
And I try to print this plots like this:
```{r,echo=FALSE, results='asis',fig.keep='all'}
for (i in c(1,2)){
for(j in c(1,2)){
print(lista[[paste(paste("p",i,sep=""),j,sep=".")]])
}
}```
When I run Knitr to obtain HTML file the result is that just the last plot of the for loop is shown.
I have tried use lapply instead of for loop, but it did not work for me. Also I tried to use replayPlot function print(replayPlot(lista[[paste(paste(paste("p",i,sep=""),".",sep=""),j,sep="")]])) with the same result.
Is there any way to solve this without modifying the previous R function that generate the plot list?
Thank you for the answers.

The syntax you use would probably be OK with a list of ggplot objects, but with base plot objects, you need to call plot.new() so that the next plot in the list doesn't overwrite the previous one:
---
title: "test"
output: html_document
---
## Define plots
```{r}
x <- c(1,2,3,4,5)
y <- c(1,2,3,4,5)
plot.new()
plot(x,y)
abline(h=1)
p1.1 <- recordPlot()
plot.new()
plot(x,y)
abline(h=3)
p1.2 <- recordPlot()
plot.new()
plot(x,y)
abline(h=4)
p2.1 <- recordPlot()
plot.new()
plot(x,y)
abline(h=6)
p2.2 <- recordPlot()
lista<-NULL
lista["p1.1"] <- list(p1.1)
lista["p1.2"] <- list(p1.2)
lista["p2.1"] <- list(p2.1)
lista["p2.2"] <- list(p2.2)
```
# Print list
```{r,echo=F}
for (i in c(1,2)){
for(j in c(1,2)){
# Needed to avoid overwrite
plot.new()
print(lista[[paste(paste("p",i,sep=""),j,sep=".")]])
}
}
```
Note that you can simplify the syntax of the loops:
for(p in lista) {
plot.new()
print(p)
}

You don't need a loop to plot each ggplot saved in a list. Much simpler:
```{r,echo=FALSE}
print(lista)
```
This automatically plots every element of the list (each plot) to the Markdown.

Related

Plot Layout of Base R Plots Saved by recordPlot() Function

I can use the recordPlot() function to save Base R plots in data objects:
plot(1:5, 1:5)
my_plot1 <- recordPlot()
plot(1:10, 1:10)
my_plot2 <- recordPlot()
plot(1:20, 1:20)
my_plot3 <- recordPlot()
I would like to draw these three plots in a grid of plots. Usually, I could use the layout function for this. However, this does not work when I want to draw plots created by recordPlot.
This does not work:
layout(matrix(c(1, 0, 2, 3), ncol = 2))
plot.new()
my_plot1
my_plot2
my_plot3
How can I draw a grid of plots saved by the recordPlot() function?
I recently had to solve a similar problem, below are two solutions that may work for you.
(1) use par() to specify numbers of rows/ columns:
# create objects with base plots
plot(rnorm(50),rnorm(50))
my_plot1 <- recordPlot()
plot(rnorm(50),rnorm(50))
my_plot2 <- recordPlot()
plot(rnorm(50),rnorm(50))
my_plot3 <- recordPlot()
# clear plots in workspace
plot.new()
# plot side by side
par(mfrow= c(1,3)) # specify rows (1) and columns (3) for plotting
my_plot1
my_plot2
my_plot3
(2) Save objects to a list and then use plot_grid (cowplot library) - this is good if you need to export the figure:
library(cowplot)
# create empty list
p <- list()
# save objects to list
plot(rnorm(50),rnorm(50))
p[[1]] <- recordPlot()
plot(rnorm(50),rnorm(50))
p[[2]] <- recordPlot()
plot(rnorm(50),rnorm(50))
p[[3]] <- recordPlot()
# clear plots
plot.new()
# plot in 3 columns using plot_grid
plot_grid(plotlist = p, nrow=1, ncol=3)

arrange R plots: how can I arrange plots of the VIM package?

I would like to generate multiple plots using marginplot() (VIM package) and then arrange them into one big figure. I tried to use grid.arrange (grid/gridExtra package) and it did not work. The error was, that a grob was expected as input. So I tried to first convert the marginplot into a ggplot (as.ggplot) or grob (as.grob) but this did not work. Has anyone an idea how to arrange the plots?
library(VIM)
library(ggplotify)
library(grid)
library(gridExtra)
x <- cars[, c("speed", "dist")]
marginplot(x)
y <- cars[, c("speed", "dist")]
marginplot(y)
p <- qplot(1,1)
#p2 <- as.ggplot(marginplot(x))
r <- rectGrob(gp=gpar(fill="grey90"))
grid.arrange( r, p,p, r, ncol=2)
I created a small code with cars, where I managed to arrange grey squares and qplots. put I cannot add the marginplot.
With base plots this error occurs. Learned here: grid.arrange from gridExtras exiting with "only 'grobs' allowed in 'gList'" after update
grid.arrange is intended to be used with "grid graphical objects" (grobs), such as ggplot2.
One could find an equivalent grid-plot or use a base-graphics approach to stacking plots.
Try this:
library(VIM)
x <- cars[, c("speed", "dist")]
y <- cars[, c("speed", "dist")]
par(mfrow = c(2,2))
marginplot(x)
marginplot(y)
plot(rnorm(100))
hist(rnorm(100))
par(mfrow = c(1, 1)) #reset this parameter

How do I use qqplot() and assign the output to an object?

I'd like to make an object that has a QQ chart
This is my code
qqnorm(titanic$age)
qqline(titanic$age)
In ggplot, I can layer geoms on top of each other, so they can be in one object
What's the equivalent for this case?
Here's some code as an example. I had to use a different dataset, as the "Titanic" dataset's age column is non-numeric:
data("AirPassengers")
qqnorm(AirPassengers)
qqline(AirPassengers)
lines(x = 1:length(AirPassengers), rep(300, 144))
p <- recordPlot()
p
edit: to disable the plot:
dev.control('inhibit')
plot(rnorm(10))
p <- recordPlot()
dev.off()
in a loop:
for(i in 1:10){
# dev.control('inhibit')
plot(rnorm(10))
p <- recordPlot()
# dev.off()
l_plots[[i]] <- p
}
Somehow it seems difficult to combine the approaches. How about you just delete the plots in the plotting window after creating them?

Why does ggplotly does not work in rmarkdown the same way ggplot does

I would like to use ggplotly for it's side effect the same way ggplot does or even graphics does. By this I mean when I knitr::knit or rmarkdown::render a Rmd document I expect print(obj) where obj is a ggplotly objcet to be in the report and that's not the case.
Can anyone explain what is going on?
Can anyone tell me how I can achieve want I want to do. I want to be able to plot a ggplotly graph into a function without returning the object (I want to return the underlying data of the graph) and I'd like the code to work for both ggplot and ggplotly (i.e. use the same code for a ggplot or ggplotly)
question.R file
#+ libs, echo = FALSE
suppressMessages({
library(ggplot2)
library(plotly)
library(rmarkdown)
})
#+ functions decl, echo = FALSE
df <- data.frame(x = 1:5, y = 1:5)
f_0 <- function(df) {
p <- ggplot(df, aes(x, y)) + geom_line()
# p or plot(p) or print(p) works
print(p)
return(df)
}
f_1 <- function(df) {
p <- ggplot(df, aes(x, y)) + geom_line()
p <- ggplotly(p)
# plot(p) crashes
# print p does not print in report
print(p)
# p standalone does not work either
p
return(df)
}
#' # plots
#' plot 0
#+ plot_0
res_0 <- f_0(df)
#' plot 1
#+ plot_1
res_1 <- f_1(df)
Render this file
rmarkdown::render("question.R")
The output
Editorial comment: As a point of style, it's usually a good idea to separate computation and plotting into distinct functions, because it increases modularity, makes code easier to maintain, and allows for finer control without parameter creep. The output of individual functions can then be easily mapped to separate knitr chunks.
Best solution: I know that you are specifically asking about not returning the plot object, but I just want to point out that returning it alongside the results provides the cleanest and most elegant solution:
---
output: html_document
---
```{r include=FALSE}
library( tidyverse )
df <- data_frame( x=1:5, y=1:5 )
```
```{r}
f <- function(df) {
gg <- ggplot(df, aes(x,y)) + geom_point()
list( result=df, plot=plotly::ggplotly(gg) )
}
res <- f(df)
res$plot
```
However, if you absolutely cannot return a plotly object from a function, you have some alternatives.
Option 1: Store the plotly object to the parent frame, providing access to it from the knitr chunk.
```{r}
f1 <- function(df) {
gg <- ggplot(df, aes(x,y)) + geom_point()
assign("ggp", plotly::ggplotly(gg), envir=parent.frame())
df # NOT returning a plot
}
res1 <- f1(df)
ggp # Let knitr handle the rendering naturally
```
Option 2: Render the plot to a temporary .html and then import it as an iframe
```{r, results='asis'} # <-- note the "asis" chunk option
f2 <- function(df)
{
gg <- ggplot(df, aes(x,y)) + geom_point()
htmlwidgets::saveWidget( plotly::ggplotly(gg), "temp.html")
print( htmltools::tags$iframe(src="temp.html", width=640, height=480) )
df # NOT returning a plot
}
res2 <- f2(df)
```
Explanation: Yihui can correct me if I'm wrong, but knitr basically does "Option 2" behind the scenes. It renders htmlwidgets, such as plotly objects, into temporary .html files and then combines those temporaries to produce the final document. The reason it fails inside a function is because the temporary file gets deleted when execution leaves the function scope. (You can replicate this yourself by using tempfile() instead of the persistent "temp.html"; the iframe object will display a "file not found" error in the final document.) There's probably a way to modify knitr hooks to retain the temporary files, but it's beyond my knowledge. Lastly, the reason you don't have this issue with basic ggplot objects is because their output goes to the plotting device, which persists across calling frames.

Multiple plotting device in R

To save multiple plots in a pdf, I do this:
pdf("plot1.pdf")
for(i in 1:10){
p <- plot(rnorm(10))
p
}
dev.off()
Is there any way I can open two pdf and print different plots in them. Something like
pdf("plot1.pdf")
pdf("plot2.pdf")
for(i in 1:10){
p1 <- plot(rnorm(10))
p1 # print this in plot1.pdf
p2 <- plot(rnorm(100))
p2 # print this in plot2.pdf
}
dev.off()
You can only have one graphics device active at a time, but you can switch between them. R tracks a list of open devices (dev.list()) in the order in which you create them. For example you can do
pdf("plot1.pdf")
pdf("plot2.pdf")
for(i in 1:3){
dev.set(dev.prev()) #go back to plot1.pdf
plot(rnorm(10))
dev.set(dev.next()) # jump ahead to plot2.pdf
plot(rnorm(100))
}
dev.off()
dev.off()
(Note it doesn't make sense to store the result of plot(rnorm(10)) to a variable because it doesn't return anything. Base plotting typically just have the side effect of drawing to the screen.)

Resources