106

In ggplot2, one can easily save a graphic into a R object.

p = ggplot(...) + geom_point()      # does not display the graph
p                                   # displays the graph

The standard function plot produces the graphic as a void function and returns NULL.

p = plot(1:10)     # displays the graph
p                  # NULL

Is it possible to save a graphic created by plot in an object?

Remi.b
  • 17,389
  • 28
  • 87
  • 168
  • `plot` is a generic, and different `plot` methods do return such objects as far as I know. `plot.default`, however, indeed returns `NULL`. – Konrad Rudolph Apr 11 '15 at 22:41
  • 1
    Is your goal to be able to replot your graph only by typing `p` after saving it as an object? Or would you like to save it as an object which you would then be able to alter its values for example? – LyzandeR Apr 11 '15 at 22:51
  • I might want to overlay other graphics on top of the saved graphic but I don't aim to modify the plot that has been created and saved. Did I answer your question? Thks – Remi.b Apr 11 '15 at 22:53
  • @Remi.b It does thanks. I posted a solution that might help. – LyzandeR Apr 11 '15 at 22:59

4 Answers4

116

base graphics draw directly on a device.

You could use

1- recordPlot

2- the recently introduced gridGraphics package, to convert base graphics to their grid equivalent

Here's a minimal example,

plot(1:10) 

p <- recordPlot()
plot.new() ## clean up device
p # redraw

## grab the scene as a grid object
library(gridGraphics)
library(grid)
grid.echo()
a <- grid.grab()

## draw it, changes optional
grid.newpage()
a <- editGrob(a, vp=viewport(width=unit(2,"in")), gp=gpar(fontsize=10))
grid.draw(a)
baptiste
  • 75,767
  • 19
  • 198
  • 294
  • 1
    It's unclear to me: are (1) and (2) separate methods to achieve the same goal, or two steps both necessary to answer the OP's question? – NLi10Me May 25 '17 at 15:04
  • @NLi10Me 2 different methods. – zx8754 Jan 26 '18 at 12:30
  • If I try `saveRDS(object = p, file = "p.Rds")`, then load a new R session, run `p <- readRDS(file = "p.Rds")` followed by `p`, I get an error saying `Error in replayPlot(x) : loading snapshot from a different session`. Am I saving the `p` object incorrectly? – user5359531 May 04 '18 at 17:48
  • It seems that the error I was getting was resolved in [R 3.3.0](https://www.stat.auckland.ac.nz/~paul/Reports/DisplayList/dl-record.html), using that version it works. When I used the `gridGraphics` method shown here, the colors on the redrawn plot kept getting messed up, even with `grid.grab(wrap=TRUE)` – user5359531 May 04 '18 at 18:49
50

I solved this by using a function instead of an object. For example, suppose we want to compare two beta distributions with different parameters. We can run:

z1<-rbeta(10000,5,5)
z2<-rbeta(10000,20,20)

plotit<-function(vector,alpha,beta){
    plot(density(vector),xlim=c(0,1))
    abline(v=alpha/(alpha+beta),lty="longdash")
}

And save the plots as functions rather than objects.

z.plot1<-function(){plotit(z1,5,5)}
z.plot2<-function(){plotit(z2,20,20)}

Next, we can call each plot as we want by simply calling the two plots as functions rather than objects.

z.plot1()

plots the first plot and

z.plot2()

plots the second.

user438383
  • 5,716
  • 8
  • 28
  • 43
cartwheel
  • 601
  • 5
  • 2
20

You can use the active binding feature of the pryr package if you don't want to directly change the values of the object created.

library(pryr)
a %<a-% plot(1:10,1:10)

Each time you type a on the console the graph will be reprinted on the screen. The %<a-% operator will rerun the script every time (in case of one graph this is not a problem I think). So essentially every time you use a the code will be rerun resulting in your graph which of course you can manipulate (overlay another plot on top) or save using png for example. No value itself will be stored in a however. The value will still be NULL.

I don't know if the above is what you are looking for but it might be an acceptable solution.

LyzandeR
  • 37,047
  • 12
  • 77
  • 87
  • Thank you. That is a very handy solution. Do you know if this works if the plot is build through several lines (such as `plot(1:10);abline(v=4)` for example)? +1 – Remi.b Apr 11 '15 at 23:08
  • @Remi.b Yeah of course it does. You need to type it like this though" `a % – LyzandeR Apr 11 '15 at 23:13
  • This is really cool! I'll have to read a bit about this package because it sounds impossible to me to do what this weird function `% – Remi.b Apr 11 '15 at 23:17
  • Yeah it is another one of those really cool packages Hadley made. It is a set of functions that lets you understand the R language in depth. Have a look at `% – LyzandeR Apr 11 '15 at 23:23
  • 1
    @LyzandeR is it possible to combine the saved plots into a multiplot? – user2300940 May 12 '16 at 10:10
  • @user2300940 yes, it is. http://stackoverflow.com/questions/42723141/saving-a-base-r-plot-as-an-object-that-can-be-plotted-in-a-multiplot/42723635#42723635 – B. Davis Mar 10 '17 at 17:11
  • If you have a function that does some computation and produces a plot as a side effect and you don't want to postpone the computation until you evaluate `a` later, then you cannot use this method. – chriad Dec 29 '17 at 13:31
-10
library(ggplot2)
# if mygraph is a plot object
ggsave("myplot1.png",mygraph)

# if the plot is in a list (e.g. created by the Bibliometrics package)
ggsave("myplot1.png",mygraphs[[1]])
Dr. T.
  • 11
  • 5
  • Although your answer looks 'correct' (but I am no R programmer), it is normal practice on Stack Overflow to add some explanatory text, rather than just posting a 'terse' code-only block. This makes the answer more valuable in the long-term, and to a broader range of users. (But have an upvote, anyway!) – Adrian Mole Sep 30 '19 at 12:34
  • 6
    The OP was asking about how to do this *without* using ggplot2. – Jason Dec 19 '19 at 12:02
  • 2
    Also, this saves the plot as an image to a file. The OP wants to save the plot to an R object (a variable), which can be accessed in the code. The ggplot way to do this is trivial: `plot <- ggplot(...)`, but they're specifically asking to do it in base graphics, which works differently – divibisan Jan 20 '23 at 22:25