S Graphics Hints and Tricks
R Graphics Setup Templates
Besides the Hmisc package's
setpdf
and
setps
functions, you can use commands like the following to make publication-ready graphics.
pdf('/tmp/z.pdf', pointsize=16)
par(mar=c(3.5, 3.5,.5,.5), lwd=2, mgp = c(2.2, 0.45, 0), tcl = -0.4)
for(i in 1:2)plot(runif(100),xlab='Label for X',ylab='Label for Y')
dev.off()
postscript('/tmp/z.ps', pointsize=24)
par(mar=c(2.825, 3, .125, 1.5), lwd=2, mgp = c(2, 0.45, 0), tcl = -0.4)
for(i in 1:2)plot(runif(100),xlab='Label for X',ylab='Label for Y')
dev.off()
postscript('/tmp/z.eps', pointsize=14,
onefile=FALSE, paper='special',
horizontal=FALSE, height=4, width=5)
par(mar=c(3, 3.25, .25, .5), lwd=2, mgp = c(2, 0.45, 0), tcl = -0.4)
plot(runif(100),xlab='Label for X',ylab='Label for Y')
dev.off()
The following function sets up for single low-level plots and matrices of plots in a way that is useful with Sweave. Note that
par
parameters have only been optimized for single and 2x2 plots.
spar <- function(mar=if(axes)
c(2.25+bot-.45*multi,1.5+left,.5+top+.25*multi,.5+rt) else
c(3.25+bot-.45*multi,3.5+left,.5+top+.25*multi,.5+rt),
lwd = if(multi)1 else 1.75,
mgp = if(axes) mgp=c(.75, .1, 0) else
if(multi) c(1.5, .365, 0) else c(2.4-.4, 0.475, 0),
tcl = if(multi)-0.25 else -0.4,
bot=0, left=0, top=0, rt=0, ps=if(multi) 14 else 10,
mfrow=NULL, axes=TRUE, ...)
{
multi <- length(mfrow) > 0
par(mar=mar, lwd=lwd, mgp=mgp, tcl=tcl, ps=ps, ...)
if(multi) par(mfrow=mfrow)
}
Example calls:
spar() # single plot
spar(mfrow=c(2,2)) # 2x2 matrix of plots, minimal space between panels
spar(top=1) # single plot, leave room for title
Under Sweave you might invoke
spar
right after <<fig=T>>=. If not using Sweave,
spar
might be called right after
postscript
or
pdf
.
Customizing Lattice Axis Spacing
Like the
mgp
par
element in base graphics, you can set the spacing between tick marks and the axis numbers using the following model:
xyplot(y ~ x, data=d,
par.settings=list(axis.components=list(left=list(pad1=.3), bottom=list(pad1=.4))))
Adding panel numbers in margins of multi-panel graphs
For submitting multi-panel graphs made with low-level S graphics for publication, journals often request that panel numbers be placed on each panel. The following setup provides good margin settings for this and shows how to add the panel descriptors outside the top left corner of each graph.
Panel <- function(panel)
{
par(xpd=NA)
u <- par('usr')
text(u[1]-(u[2]-u[1])*.125, u[4]+(u[4]-u[3])*.1, panel)
par(xpd=FALSE)
}
par(mfrow=c(2,2), mar=c(3.5, 3.75, 1.75, .75))
plot( )
Panel('1A')
hist( )
Panel('1B')
plot( )
Panel('1C')
hist( )
Panel('1D')
Embedding fonts
Certain journals require all fonts (including Helvetica) to be embedded when depositing
pdf
or
eps
files with them.
According to Brian Ripley, the problem is "having a legitmately licensed version of those copyright fonts that allows you to send them (or subsetted versions)" to the journal. If the journal really feels that way, the journal should supply the fonts. "One possible way out is to use Nimbus Sans aka URW Helvetica, which
embedFonts
can embed." Here is an example. If using
postscript()
instead of
pdf()
be sure to specify a
paper
argument equal to
'special'
.
pdf('my.pdf', onefile=FALSE, pointsize=18,
family="NimbusSan",height=6,width=8,paper="special")
...
dev.off()
embedFonts('my.pdf')
See
http://colinm.org/tips/latex for some useful information. If the journal requires you to use Microsoft
.ttf
fonts, see
http://corefonts.sourceforge.net (note that Microsoft forgot to copyright these fonts) or do
sudo apt-get install msttcorefonts
. This will provide the arial font to linux.
According to PLoS Guidelines one must generate
.afm
files before using them in R, along the lines of:
ttf2afm /usr/share/fonts/msttcorefonts/arial.ttf > /foo/arial.afm
ttf2afm /usr/share/fonts/msttcorefonts/ariali.ttf > /foo/ariali.afm
ttf2afm /usr/share/fonts/msttcorefonts/arialbd.ttf > /foo/arialbd.afm
ttf2afm /usr/share/fonts/msttcorefonts/arialbi.ttf > /foo/arialbi.afm
and then one can use them in R's postscript() with the
family
argument on the lines of
postscript(file="try.ps", horizontal=F, onefile=F, width=4, height=4,
family=c("/foo/arial.afm", "/foo/arialbd.afm",
"/foo/ariali.afm", "/foo/arialbi.afm"),
pointsize=12)
You will need the
texlive-font-utils
package on your system.
ttf
and
arial
information is thanks to Ted Harding, Deepayan Sarkar, Dirk Eddelbeutel. Sarkar also pointed out the following potentially simpler solution:
cairo_ps()
par(family = "Arial") # or par(family = "Andale mono") to be really sure this works
plot(1:10)
dev.off()
He also said to see the 2006 article in Rnews referenced in the help file for
postscript()
.
Making plots with Two Y-axes
Someone posted this to S-news, with the following note. I would like to thank Naomi Robbins, Luciano Molinari, Samantha Choy, Melanie Cox, Z. Todd Taylor, Anne York and Ronald Fehd for their replies. I ended up using the function that Anne York sent me. I have modified it somewhat and will post the modifed version latter. Here is the original form:
Twoplots <-
function(x1, y1, x2, y2, type = "l", xlim = NULL, ptitle = "Twoplots", xlab
= "x", ylab1 = "y1", ylab2 = "y2", ...)
{
# Written by AE York Sept. 1996
# Plots y1 vs x1 and y2 vs x2 on the same plot
# Labels for y1 are on the left and labels for y2 on the right
oldpar <- par()
par(mar = rep(6, 4)) #
if(missing(xlim))
xlim <- range(pretty(range(c(x1, x2))))
plot(x1, y1, lty = 1, pch = 1, type = type, xlim = xlim, xlab =
xlab, ylab = ylab1, ...)
par(new = T)
plot(x2, y2, lty = 2, pch = 16, type = type, axes = F, xlim = xlim,
xlab = "", ylab = "")
axis(side = 4)
mtext(ylab2, side = 4, line = 2, outer = F)
par(oldpar)
title(ptitle)
}
Compressing PDF Files
PDF files produced by R are uncompressed. In may cases you can get an almost 10-fold reduction in file size by compressing the PDF graphics files. To to this, first make sure the
pdftk
package is installed in your system, then define the following
bash
script in your
~/bin
directory and name it
cmpdf
:
#! /bin/bash
for f in $@
do
pdftk $f output /tmp/$f compress
mv -f /tmp/$f .
done
To use it, here are some examples:
cmpdf mygraph.pdf # compress one file
cmpdf a.pdf b.pdf # compress two files
cmpdf *.pdf # compress all .pdf files
cmpdf a*.pdf # compress all .pdf files beginning with the letter a
Making Small Postscript Files
Tim Hesterberg of Insightful Corp. in 2000 provided the following examples:
postscript(file="CLT2.ps", height=2.2, width=6.5, horizontal=F, onefile=F)
par(mfrow=c(1,3), mar=c(2.1,2.1,4.1,2.1))
postscript(file="CLT3.ps", height=2.2*2, width=3, horizontal=F, onefile=F)
par(mfrow=c(2,1), mar=c(2.1,2.1,1.1,0.1))
par(cex=.7, mex=.7)
postscript(file="misc-three1.ps", height=2.2, width=6.5/3, horizontal=F,
onefile=F)
par(mar=c(2.1,2.1,1.1,0.1))
par(cex=.6, mex=.6)
Note that sometimes it is necessary to use two calls to
par()
,
that combining the calls into a single call to
par()
fails.
I believe this is because changing some graphical options changes
others, so to get what you want for the latter they must be set in a
later call to
par()
.
Modifying Postscript Files for Customizing Trees
Samuel Buttrey (
sebuttre@monterey.nps.navy.mil) wrote on 04May99 the following.
Here is a simple Splus function that takes in a Postscript file
written by post.tree, plus the (regression) tree object itself, and writes a
new Postscript file in which the number under each leaf is the number of
observations, not the deviance.
If anyone is interested I have a couple of these manipulation-type functions
for classification trees, too. Warning: they rely very heavily on the
particular Postscript layout that I've observed Splus to use. If post.tree
ever changes, I presume these functions will have to, too.
I don't have a function that will add the standard error, but this technique
can be adapted.
fix.ps.reg <- function(file.in, tree.in, file.out)
{
#
# Fix.ps.reg: function to change the labels below the nodes of
# a regression tree.
#
# Arguments: file.in: file with Postscript (output from post.tree)
# tree.in: Splus tree
# file.out: new Postscript file
#
# Read in the file.in file. Find the lines with "centershow" in
# them; these come in pairs. Replace the first line of each pair
# with a line of the form "(n) centershow" where n is the number
# of items in the node. We get n from tree.in.
#
strs <- scan(file.in, what = "", sep = "\n", strip.white = T)
rights <- regexpr(")", strs)
lefts <- regexpr("(", strs)
shows <- regexpr("centershow", strs)
goods <- (1:length(strs))[shows > 0 & rights > 0 & lefts > 0]
goods <- goods[-1] # Skip over title
goods <- goods[seq(1, by = 2, length = length(goods)/2)] #
#
# Okay. Goods now holds the numbers of the relevant lines.
#
devs <- strs[goods] #
first.occurrence <- function(str, char)
{
#
# first.occurrence: function to find the number of the first occurrence
# of character "char" in string "str"
#
# Arguments: str: string in which to find character
# char: character to be found
# Return value: number giving the position of the first occurrence
# of "char" in "str." If char isn't found, return 0.
#
all.chars <- substring(str, 1:nchar(str), 1:nchar(str))
first <- (1:nchar(str))[all.chars == char][1]
return(ifelse(length(first) == 0, 0, first))
}
for(i in 1:length(devs)) {
left.paren <- first.occurrence(devs[i], "(")
right.paren <- first.occurrence(devs[i], ")")
devs[i] <- paste("(", tree.in$frame[i, "n"], ") centershow")
}
strs[goods] <- devs
if(missing(file.out))
sink(file.out)
cat(paste(strs, "\n"))
if(missing(file.out))
sink()
invisible()
}
Changing Trellis/Lattice Strip Characteristics
You can turn off the shading in the strip labels as well as tell
lattice
to use black and white with
ltheme <- canonical.theme(color = FALSE)
ltheme$strip.background$col <- "transparent"
lattice.options(default.theme = ltheme) ## set as default
Here is another approach:
trellis.par.set(strip.background = list(col = 'transparent'))
or
strip <- function(...) strip.default(..., style=1)
Another function posted to S-news is:
"trellis.strip"<-
function(b = 0, s = 0)
{
# Sets trellis parameters for strip colors
# Default will eliminate shading from trellis strips
# Use trellis.strip(5,12) for trellis defaults
if(is.null(attr(get(".Device", where = 0), "trellis.settings"))) stop(
"Current device is not a trellis device")
s.b <- trellis.par.get("strip.background")
s.b$col[1] <- b
trellis.par.set("strip.background", s.b)
s.s <- trellis.par.get("strip.shingle")
s.s$col[1] <- s
trellis.par.set("strip.shingle", s.s)
}
Here's some other postings:
strip.back<- trellis.par.get("strip.background")
strip.back$col<-rep(0,7)
trellis.par.set("strip.background",strip.back)
remove.shading <- function()
{
# Removes shading on shingles above Trellis plots
strip.background <- trellis.par.get("strip.background")
strip.background$col <- 0
trellis.par.set("strip.background", strip.background)
strip.shingle <- trellis.par.get("strip.shingle")
strip.shingle$col <- 0
trellis.par.set("strip.shingle", strip.shingle)
}
stripplot(Sut ~ lpelong | Knottype, jitter=T,
strip=remove.shading(),
par.strip.text = list(cex=1.2), scale = list(x=list(cex=1.2),
y=list(cex=1.4)), xlab=list("Loop elongation (mm)", cex=1.6) )
More Hints About Exporting R Graphics to Office
To make a high resolution graph in R that can be pasted into word, you can use the png function. If you make it a large size with high resolution, if it is resized, it will still have good quality.
png("name.png", pointsize = 10, width = 6, height = 6, units = "in", res = 1600)
...
dev.off()
Sometimes it's simplest just to create a high enough resolution
png
file once you've created a
pdf
file in R. To easily control the resolution of the resulting
png
file and to scale text accordingly, use the
ImageMagick
convert
command, e.g.
convert -density 300x300 myplot.pdf myplot.png
.
Another
ImageMagick
command that has worked well is
convert -verbose -density 150 -trim my.pdf -quality 100 /tmp/my.png
. Note that the output file
my.png
is put in a different directory than the one holding
my.pdf
to keep LaTeX from being confused with two graphics files with the same base name.