Using ggsave with a pipe - r

I can save a plot with ggsave after I stored it, but using it in a pipeline I get the following error. I wish to plot and save in the same (piped) command.
no applicable method for 'grid.draw' applied to an object of class "c('LayerInstance', 'Layer', 'ggproto', 'gg')"
I know ggsave's arguments are first the filename, and then the plot, but switching this in a wrapper does not work. Also, using 'filename=' and 'plot=' in the ggsave command does not work.
library(magrittr)
library(ggplot2)
data("diamonds")
# my custom save function
customSave <- function(plot){
ggsave('blaa.bmp', plot)
}
#This works:
p2 <- ggplot(diamonds, aes(x=cut)) + geom_bar()
p2 %>% customSave()
# This doesn't work:
ggplot(diamonds, aes(x=cut)) + geom_bar() %>% customSave()
# and obviously this doesn't work either
ggplot(diamonds, aes(x=cut)) + geom_bar() %>% ggsave('plot.bmp')

As akrun pointed out, you need to wrap all of your ggplot in parentheses. You can also use the dot notation to pass an object to a function parameter other than the first in a magrittr pipe stream:
library(magrittr)
library(ggplot2)
data("diamonds")
(
ggplot(diamonds, aes(x=cut)) +
geom_bar()
) %>%
ggsave("plot.png", . , dpi = 100, width = 4, height = 4)

Update: The following code no longer works. See the tidyverse GitHub issue and another StackOverflow question for the official solution.
Original answer that NO LONGER WORKS:
If you want to plot and save in one line, try this
ggsave('plot.bmp') ```
If you don't want to show the plot, simply put `p <- ` at the
beginning.
If you have a custom save function, you can also do this
``` mysave <- function(filename) { ggsave(file.path("plots",
paste0(filename, ".png")),
width = 8, height = 6, dpi = 300) } ```
and simply replace `ggsave('plot.bmp')` with `mysave('plot')` in the
snippet above.
I found this usage by accident but haven't found any documentation.

Related

R patchwork package trouble displaying many plots

I am enjoying the patchwork package quite a bit and I am generally happy with even its default formatting.
However, I cannot figure out how to get a reasonable result when trying to display many plots. The output is unreadable (plots shrunk down and dominated by text) and the saved output is completely blank.
Input:
require(ggplot2)
require(patchwork)
plots <- list()
testfunction <- function(x) {
plot_placeholder <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
ggtitle(paste("p", x, sep = ""))
plots[[length(plots) + 1]] <<- plot_placeholder
}
mapply(testfunction, c(1:66), SIMPLIFY = FALSE)
patchwork::wrap_plots(plots)
ggsave("test.pdf")
Output:
Is there a way within patchwork to make the page larger or wrap plots across multiple pages of a PDF? Or is there a different package that may help with this?
Update:
Maurits Evers pointed out that this is an issue with my arguments in ggsave(), not patchwork. With these changes the plots look much better in the .pdf output, but were still being truncated at the bottom of page 1. From this post, found the last bit of change needed to generate desired results
Input:
# ...
pdf()
patchwork::wrap_plots(plots)
ggsave("test.pdf", width = 20, height = 20)
dev.off()
Output:

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.

Save and Load a ggplot plot

I am working on a large shinydashboard and was keeping my code for modeling in a separate file from the main app.R. The problem is that I need to plot my data. This requires that I save my ggplots from one file and load them in my main app.R file. How can I save my ggplots and load them.
As a simple example lets say I have the following
#make plot
> p <- mtcars %>%
ggplot(aes(x = mpg, y = cyl))+geom_point()
#save plot
> save(file=here::here("plots/a_plot.Rdata"),p)
#load plot
> p <- load(file=here::here("plots/trans_arima.Rdata"))
> p
[1] "p"
How can I load my ggplot?
You can save your plot as a png file and then load it back into youyr file
you have several option for saving your plot. you could use ggplot2s' ggsave()
function or you could use the save_plot() from the cowplot package. save_plot() is said to
give you more flexibility when it comes file adjusting hence my pick.
You can explore both.
refer to https://rdrr.io/cran/cowplot/man/save_plot.html to read more about save_plot.
tmp = data.frame(first = c('a','b','c','d','e','f','g','h','i','j','k','l','m','n'),
second = c(2,3,4,5,2,3,4,5,6,3,4,4,6, 7))
plot_tmp = ggplot(tmp, aes(first, second)) + geom_bar(stat = 'identity')
dev.new()
if("png" %in% installed.packages()){
library(png)
}else{
install.packages("png")
library(png)
}
save_plot("~/plot_tmp.png", plot_tmp, base_height = NULL, base_aspect_ratio = 1.618,
base_width = 6)
Use the following steps to load files into your shiny using by using the
#read plot
library(OpenImageR)
img<-OpenImageR::readImage("~/plot_tmp.png")
imageShow(img)
Hopefully this helps. To read more about OpenImageR and how you can use it in shiny please go to https://cran.r-project.org/web/packages/OpenImageR/vignettes/The_OpenImageR_package.html
have fun!!!

gridsvg does not work when used inside a function

I want to define a plot saving function that uses the gridsvg device from the package gridSVG.
library(ggplot2)
library(gridExtra)
mtcars$gear <- factor(mtcars$gear,levels=c(3,4,5),
labels=c("3gears","4gears","5gears"))
mtcars$am <- factor(mtcars$am,levels=c(0,1),
labels=c("Automatic","Manual"))
mtcars$cyl <- factor(mtcars$cyl,levels=c(4,6,8),
labels=c("4cyl","6cyl","8cyl"))
myPlot <- qplot(mpg, data=mtcars, geom="density", fill=gear, alpha=I(.5),
main="Distribution of Gas Milage", xlab="Miles Per Gallon",
ylab="Density")
savePlot <- function(filename, plot, plotWidth = 15, plotHeight = 10){
gridSVG:::gridsvg(name = filename, width = plotWidth, height = plotHeight)
print(plot)
dev.off(which = dev.cur())
}
However if I then try to use the function it does not work. An error results:
savePlot("~/Desktop/myplot.svg", myPlot)
Show Traceback
Rerun with Debug
Error in eval(expr, envir, enclos) : object 'filename' not found
However if I do those steps from the console it works:
gridSVG::gridsvg(name = "~/Desktop/myPlot.svg", width = 15, height = 10)
myPlot
dev.off()
Is there a way I might be able to use the gridsvg function from within another function?
I wonder if I might be able to do it with eval from some environment.
Thanks,
Ben.
Here's a roundabout, but slightly more principled, way without sticking everything in the global environment (inspired by the scoping discussion in R Inferno):
library(gridSVG)
savePlot <- function(filename, plot, plotWidth = 15, plotHeight = 10){
gridsvg(sys.frame(1))
print(plot)
grid.export(filename)
grDevices::dev.off(which = dev.cur())
}
sys.frame(1) gives us the evaluation frame of the parent context (there's an ok explanation here for all variations on functions that access the call stack).
I separated out the call to grid.export() from the call to dev.off(), because essentially all the dev.off from gridSVG does is call grid.export, then call the grDevices::dev.off. This also lets us explicitly feed the file name to grid.export.
How curious. gridsvg seems to have an eval(fncall[[i]]) step where it walks through all the arguments and assigns them, and it must be looking in the wrong environment or something?? I am not sure if this is a problem with the gridSVG package; eval-semantics always confuse me.
Here's a workaround: if you make sure the argument -values- get passed to gridsvg (rather than the argument names) it works, though I agree this isn't particularly elegant. And you have to explicitly library(gridSVG).
library(gridSVG)
savePlot <- function(filename, plot, plotWidth = 15, plotHeight = 10){
eval(call('gridsvg', name=filename, width=plotWidth, height=plotHeight))
print(plot)
dev.off(which = dev.cur())
}
All it does is essentially call gridsvg with width=15 rather than width=plotWidth and so on.

How to furnish a ggplot2 figure with a hyperlink?

I am trying to furnish a ggplot2 plot with a hyperlink:
This works:
library(gridSVG)
library(lattice)
xyplot(mpg~wt, data=mtcars, main = "Link to R-project home")
mainGrobName <- grep("main", grid.ls()[[1]], value=TRUE)
grid.hyperlink(mainGrobName, "http://www.r-project.org")
gridToSVG("HyperlinkExample.svg")
This not:
p = ggplot(mtcars, aes(wt, mpg)) + geom_point()+ labs(title="link")
print(p)
mainGrobName <- grep("title", grid.ls()[[1]], value=TRUE)
grid.hyperlink(mainGrobName, "http://www.r-project.org")
gridToSVG("HyperlinkExample.svg")
Any hints on this?
I have asked Simon Potter, one of the authors of the gridSVG package:
Here is his (working) answer:
I suggest you try the development version here:
http://r-forge.r-project.org/R/?group_id=1025
It contains a workaround specifically to deal with gTables (and therefore ggplot2 graphics).
So to try and get your example to work, start up a new R session and run the following code:
install.packages("gridSVG", repos="http://R-Forge.R-project.org")
library(gridSVG)
library(ggplot2)
(p <- ggplot(mtcars, aes(wt, mpg)) + geom_point() + labs(title="link"))
titleGrobName <- grep("title", grid.ls(print=FALSE)$name, value=TRUE)
grid.hyperlink(titleGrobName, "http://www.r-project.org/")
gridToSVG("HyperlinkExample.svg", "none", "none")
The only real difference here are the additional parameters given to gridToSVG(). This is mainly to reduce the output to just the SVG file and an HTML wrapper (otherwise you also get some JSON data, which is not useful for your example).
As far as getting the correct mainGrobName this code would succeed (and not create the distracting side-effects by setting print=FALSE):
grep("title", grid.ls(print=FALSE)$name, value=TRUE)
#[1] "title.2-4-2-4"
The structure of the plot object is clearly more complex than in the lattice situation and the gridToSVG does not capture it by default:
grep("title", grid.ls()$name, value=TRUE)
#--------------------
GRID.gTableParent.125
background.1-5-6-1
spacer.4-3-4-3
panel.3-4-3-4
grill.gTree.103
panel.background.rect.94
panel.grid.minor.y.polyline.96
panel.grid.minor.x.polyline.98
panel.grid.major.y.polyline.100
panel.grid.major.x.polyline.102
geom_point.points.90
panel.border.zeroGrob.91
axis-l.3-3-3-3
axis.line.y.zeroGrob.113
axis
axis-b.4-4-4-4
axis.line.x.zeroGrob.107
axis
xlab.5-4-5-4
ylab.3-2-3-2
title.2-4-2-4
This is also interesting output but I fail to see how I can get gridToSVG to convert it into a useful HTML object:
grid.ls( print=pathListing )$name

Resources