How to save graphs in files

There are two ways to save and/or print graphs. Before you create your graphs, you can decide on a format for output. The format is called a device. The term device refers either to an on-screen display window or a file storage format. A graph on the screen is using the default device, either x11 on Linux/Unix or windows on MS Windows. You are allowed to open several graph devices at once in order to visually compare graphs, for example.

Saving output is a complicated topic, and let me start by just telling you some examples that work “pretty well.”

If currently have a graph on the screen device and you want to save it as an encapsulated post script file for inclusion in a document, do this.

dev.copy(postscript, file="myfile.eps", height=6, width=6, horizontal=F, onefile=F)

This “device copy” works because the on-screen graph is almost always considered the “current device.” If you have typed a few dev commands, you may have accidentally changed that. To fix it, either re-run the commands that created the plot or read further to learn more about selecting the current device.

Height and width of eps output are in inches, and the onefile=F option is absolutely vital if you want the result to be truly useful in your output document. The onefile=F makes sure the EPS bounding box is set, so programs that use the figure can find the boundaries in it. If you forget onefile=F, then you don’t really get encapsulated postscript. You get postscript. (The onefile option is useful if you want to send a series of figures into the same file, but I hardly ever want to do that.)

The EPS output is not a picture, but rather it is code that various programs can use to re-construct an image. That is good if you are using some text processing programs, because they can do some re-sizing of graph elements. However, you may instead want an actual picture of the output. As you are probably aware from browsing the internet, there are many picture formats, such as “jpg” and “png”. If you want to save the same on-screen graph as a picture image, say a png file with white background, do

dev.copy(png,filename="myfile.png",height=600, width=800,bg="white")

png format required height and width in pixels, not inches like postscript.

Note you can also set the pointsize for text. Peter Dalgaard told me that on postscript devices, a point is 1/72nd of an inch. If you expect to shrink an image for final presentation, set a big pointsize, like 20 or so, in order to avoid having really small type.

The dev.copy approach works well ALMOST ALL OF THE TIME. Sometimes, however, you get in trouble because the size on the computer screen does not translate into the size you specify in your output. If, for example, your onscreen graph fits inside a window that is 8 inches by 6 inches, and then you (stupidly) save it to an EPS file that is 1 inch by 5 inches, then it will look horrible! So you can either re-do your screen output or revise your copy command.

The R experts recommend that instead of copying a result from screen into a device file, we should instead create the device and then run the plot commands. Then R knows it is supposed to create the graph for the indicated device and everything is supposed to be internally consistent.

Again, the most confusing thing for new users is that R uses the term “device” to refer to the various graphical output formats. The command dev.off() is vital because it lets the device know you are finished drawing in there.

There are many devices representing kinds of file output. You can choose among device types, postscript, png, jpeg, bitmap, etc. (windows users can choose windows metafile). Type


to see a list of devices R finds on your system.

For each device, you can find out more about it.

or, in MS Windows 
(and so forth)

You start a device with a configuration command, like

png(filename="myfile.png",width=800, height=600,bg="blue")

for the default png (a gif-like) format.

Suppose we want png output. We create an instance of a png device and adjust features for that device, as in:

 > png(filename="mypicture.png", width=480, height=640, pointsize=12)

You can then run your graph, and when it is finished you have to close the device:


Some print devices can accept multiple plots, and dev.off() is needed to tell them when to stop recording. On a single-plot device, if you try to run another graph before closing this one, you’ll get an error.

The postscript device has options to output multiple graphs, one after the other. If you want to set options to the postscript device, but don’t actually want to open it at the moment, use ps.options.

Here’s an example of how to make a jpeg:

plot(sin, 2*pi) 

After you create a device, check what you have on:


And, if you have more than one device active, you tell which one to use with dev.set(n), for the n device. Suppose we want the third device:


For me, the problem with this approach is that I don’t usually know what I want to print until I see it. If I am using the jpeg device, there is no screen output, no picture to look at. So I have to make the plot, see what I want, turn on an output device and run the plot all over in order to save a file. It seems complicated, anyway.

So what is the alternative?

If you already have a graph on the screen, and did not prepare a device, use dev.copy() or dev.print(). This may not work great if the size of your display does not match the target device.

dev.copy() opens the device you specify. It is as if (but not exactly like) you ran the png() command before you made the graph:

dev.copy(device=png, file="foo", width=500, height=300) 

The dev.copy() function takes a device type as the first argument, and then it can take any arguments that would ordinarily be intended for that device, and then it can take other options as well. Be careful, some devices want measurements in inches, while others want them in pixels. Note that, if you do not specify a device, dev.copy() expects a which= option to be specified telling it which pre-existing device to use.

If you forget the last line, it won’t work. It’s like a write command.

If you use dev.copy or dev.print, you may run into the problem that your graph elements have to be resized and they don’t fit together the way you expect. The default x11 size is 7in x 7in, but postscript size is 1/2 inch smaller than the usable papersize. That mismatch means that either you should set the size of the graphics window on the monitor display device to match the eventual output device, or you fiddle the dev.copy() or dev.print() command to make sure the sizes are correct. Recently I was puzzled over this and Peter Dalgaard said you can force the sizes to match, as in

  1. Force the pdf output to match the size of the monitor:
      dev.copy(pdf, file="foo", height=7, width=7)


  1. Change the display size before creating the graphs so that its size matches the intended device you might use for output:
x11(height=6, width=6)

There are some specialized functions that can do this in a single step, as in dev.copy2eps() or dev2bitmap(). dev.copy2eps creates an eps file, and I’m not sure how it is different from the eps-compatible output created by the postscript() device. dev.copy2eps *is* an indirect way of calling dev.copy(device=postscript,...). It takes the dimensions from the current plot, and sets a couple of options. The same is true of dev.print().

The dev.print() function, as far as I can see, is basically the same as dev.copy(), except it has two appealing features. First, the graph is rescaled according to paper dimensions, and the fonts are rescaled accordingly. Due to the problem mentioned above, not everything gets rescaled perfectly, however, so take care. Second, it automatically turns off the device after it has printed/saved its result.


If you just need to test your computer setup, Bill Simpson offered this. Here is a plot printing example

dev.print(width=5,height=5, horizontal=FALSE)

A default jpeg is 480×480 (in pixels), but you can change that:

jpeg(filename="plot.jpg" width = 460, height = 480, pointsize = 12, quality = 85)

The format of png use is the same.

As of R1.1, the dev.print() and dev.copy2eps() will work when called from a function, for example:

 > ps <- function(file="Rplot.eps", width=7, height=7, ...) { 
        dev.copy2eps(file=file, width=width, height=height, ...) 
 > data(cars) 
 > plot(cars) 
 > ps()

The mismatch of “size” between devices even comes up when you want to print out an plot. This command will print to a printer:

 > dev.print(height=6, width=6, horizontal=FALSE)

You might want to include pointsize=20 or whatever so the text is in proper proportion to the rest of your plot.

One user observed, “Unfortunately this will also make the hash marks too big and put a big gap between the axis labels and the axis title...”, and in response Brian Ripley observed: “The problem is your use of dev.print here: the ticks change but not the text size. dev.copy does not use the new pointsize: try

x11(width=3, height=3, pointsize=8) 
x11(width=6, height=6, pointsize=16) 

Re-scaling works as expected for new plots but not re-played plots. Plotting directly on a bigger device is that answer: plots then scale exactly, except for perhaps default line widths and other things where rasterization effects come into play. In short, if you want postscript, use postscript() directly.

The rescaling of a windows() device works differently, and does rescale the fonts. (I don’t have anything more to say on that now)

How to save graphs in files from scripts and/or batch

Here is an example...

def.par <- par(no.readonly = TRUE) # save default, for resetting... 

x <- pmin(3, pmax(-3, rnorm(300))) 
y <- pmin(3, pmax(-3, rnorm(300))) 
xhist <- hist(x, breaks=seq(-3,3,0.1), plot=FALSE) 
yhist <- hist(y, breaks=seq(-3,3,0.1), plot=FALSE) 
top <- max(c(xhist$counts, yhist$counts)) 
xrange <- c(-3,3)
yrange <- c(-3,3)

postscript(file="myfile.eps", height=6, width=6, horizontal=F, onefile=F)
nf <- layout(matrix(c(2,0,1,3),2,2,byrow=TRUE), c(3,1), c(1,3), FALSE) 

plot(x, y, xlim=xrange, ylim=yrange, xlab="real", ylab="imaginary") 
barplot(xhist$counts, axes=FALSE, ylim=c(0, top), space=0) 
barplot(yhist$counts, axes=FALSE, xlim=c(0, top), space=0, horiz=TRUE) 



The key is to make sure that the lines with layout are ‘after’ the device definition. Notice that the graph thus produced does not have any labels even thought labels were specified in the plot command. If anyone knows who to fix this bug, please change the code above.

tips/graphics-base/0savegraphs.txt · Last modified: 2008/05/08
Recent changes RSS feed R Wiki powered by Driven by DokuWiki and optimized for Firefox Creative Commons License