My template for controlling publication quality figures

[This article was first published on me nugget, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.



The following is a template that I usually start with when producing figures for publication. It allows me to control:
  1. The overall size of the figure (in inches) (WIDTH, HEIGHT)
  2. The layout of figure subplots (using the layout() function) (LO)
  3. The resolution of the figure (for a .png file) (RESO)
I define the overall dimensions of the figure in units of measurement (e.g. inches or centimeters) in order to control how the figure will look on the printed page. For example, a typical journal page might have ~8 inches of space for a 2 column figure and ~4 inches for a 1 column figure.

I define margins (mar, oma) in terms of point size (ps), since this relates to the height of text, which allows of control of axis labeling. By defining the outer margins (OMA) and point size (PS) before calling layout, you will have these margins incorporated. Then, by running the x11() device (after the #), you can check your figure layout with layout.show(n):



I learned recently that the layout() function will adjust the character expansion size (par()$cex) depending on how your device is split up. For that reason, I usually include another line of code resetting par(cex=1) before proceeding with individual plots.

Finally, the three different device types included in the template are:

  1. x11(), for initial tweaking of the layout and general functionality of the plotting code
  2. png(), for producing a compact figure useful in pasting into Word documents, and for cases where the figure contains a lot of information and would be slow to loading as a .pdf
  3. pdf(), for a vector-based figure that is fully scalable / zoomable. When not too big, these figures look the best, and can also be embedded in LaTeX documents
I have been able to use this template to successfully control my figures to the formatting requirements of specific journals or other publications (e.g. overall size, point size, resolution, etc.).


Figure template:
#Layout of plots
#1 1 3
#1 2 3
LO <- matrix(c(2,1,1,4,1,1,3,4), nrow=2, ncol=4, byrow=TRUE)
LO #double check layout
 
#Resolution, pointsize
RESO <- 400
PS <- 10
 
#Overall units in inches
WIDTHS <- c(2,2,2,2) #widths of each figure in layout (i.e. column widths)
HEIGHTS <- c(2,2)  #heights of each figure in layout (i.e. row heights)
 
#Outer margins and calculation of full dimensions
OMA <- c(0,0,0,0)  #Outer margins c(bottom, left, top, right)
HEIGHT <- sum(HEIGHTS) + OMA[1]*PS*1/72 + OMA[3]*PS*1/72
WIDTH <- sum(WIDTHS) + OMA[2]*PS*1/72 + OMA[4]*PS*1/72
 
#Double check full dimensions
WIDTH; HEIGHT 
 
 
#Start plot; open device - run from x11() down to observe behavior; run from pdf() down to produce .pdf
png("plot.png", width=WIDTH, height=HEIGHT, units="in", res=RESO)
#pdf("plot.pdf", width=WIDTH, height=HEIGHT)
#x11(width=WIDTH, height=HEIGHT)
 
par(oma=OMA, ps=PS) #settings before layout
layout(LO, heights=HEIGHTS, widths=WIDTHS)
#layout.show(max(LO)) # run to see layout; comment out to prevent plotting during .pdf
par(cex=1) # layout has the tendency change par()$cex, so this step is important for control
 
#par(mar=c(4,4,1,1)) # I usually set my margins before each plot
#######################
###INSERT PLOTS HERE###
#######################
 
dev.off() # closes device



To reproduce example:
#required packages
require(maps)
require(mapdata)
require(mapproj)
 
deg <- 4
grd <- expand.grid(x=seq(-180+deg/2,180-deg/2,deg), y=seq(-90+deg/2,90-deg/2,deg))
poly <- vector(mode="list", nrow(grd))
for(i in seq(nrow(grd))){
	xs <- c(grd$x[i]-deg/2, grd$x[i]-deg/2, grd$x[i]+deg/2, grd$x[i]+deg/2)
	ys <- c(grd$y[i]-deg/2, grd$y[i]+deg/2, grd$y[i]+deg/2, grd$y[i]-deg/2)
	poly[[i]] <- data.frame(x=xs, y=ys)
}
 
PROJ="orthographic"
ORIENT <- c(-38,178, 0)
PAR=NULL
 
 
#Layout of plots
#1 1 3
#1 2 3
LO <- matrix(c(2,1,1,4,1,1,3,4), nrow=2, ncol=4, byrow=TRUE)
LO #double check layout
 
#Resolution, pointsize
RESO <- 400
PS <- 10
 
#Overall units in inches
WIDTHS <- c(2,2,2,2)
HEIGHTS <- c(2,2)
OMA <- c(0.5,0.5,2,0.5)
HEIGHT <- sum(HEIGHTS) + OMA[1]*PS*1/72 + OMA[3]*PS*1/72
WIDTH <- sum(WIDTHS) + OMA[2]*PS*1/72 + OMA[4]*PS*1/72
 
#Double check full dimensions
WIDTH; HEIGHT 
 
#The plot - run from x11() down to observe behavior; run from pdf() down to produce .pdf
png("tmp2.png", width=WIDTH, height=HEIGHT, units="in", res=RESO)
#pdf("tmp.pdf", width=WIDTH, height=HEIGHT)
#x11(width=WIDTH, height=HEIGHT)
 
par(oma=OMA, ps=PS) #settings before layout
layout(LO, heights=HEIGHTS, widths=WIDTHS)
#layout.show(max(LO)) # commented out to prevent plotting during .pdf
par(cex=1) # layout has the tendency change par()$cex, so this step is important for control
 
pos <- list(x=174 + 45/60 + 51.39/3600, y=-36 - 52/60 - 38.54/3600) # position of Maunga Whau volcano
 
#plot1
par(mar=c(3,3,1,0.5))
plot(pos, t="n", col=2, xlab="", ylab="", xlim=c(168, 183), ylim=c(-43,-33))
mtext("Latitude", side=2, line=2)
mtext("Longitude", side=1, line=2)
map("worldHires", add=TRUE, fill=TRUE, col=8)
points(pos, pch=24, cex=2, col=3, bg="yellow", lwd=3)
grid()
box()
 
#plot2
par(mar=c(2,2,0,0))
require(plotrix)
map("world", proj=PROJ, orient=ORIENT, par=PAR, col=NA)
for(i in seq(poly)){
	polygon(mapproject(poly[[i]]), col="grey95", border="grey95", lwd=0.1)
}
map("world", proj=PROJ, orient=ORIENT, par=PAR, add=TRUE, resolution=0, fill=TRUE, col=8, border=8, lwd=0.001)
polygon(mapproject(c(168,168,183,183),c(-43,-33,-33,-43)), col=NA, border=1, lwd=0.5)
 
#plot3
par(mar=c(3,3,0.5,0.5))
image(x=seq(nrow(volcano)), y=seq(ncol(volcano)), z=volcano, xaxt="n", yaxt="n", col=terrain.colors(100))
abline(h=35, lty=2, col=4, lwd=2)
box()
 
#plot4
par(mar=c(3,0.5,1,4))
plot(seq(0, (nrow(volcano)-1)*10, by=10), volcano[,35], t="l", lty=2, col=4, lwd=2, xaxs="i", xaxt="n", yaxt="n")
axis(4)
mtext("[m]", side=4, line=2)
 
#outer margin text
mtext("Maunga Whau volcano transect", line=0, side=3, outer=TRUE, cex=2)
 
dev.off()





To leave a comment for the author, please follow the link and comment on their blog: me nugget.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)