|
R: basic graphs
|
Back to Local tips for R
http://www.psychol.cam.ac.uk/statistics/R/graphs1.html
|
R is a very powerful graphing package; for examples of what it can do, see the R Graph Gallery. What we'll be concerned about here is producing publication-quality simple graphs of the types frequently seen in the fields of experimental psychology and behavioural neuroscience, to get you going quickly. So to begin with, I'll replicate most of the major graph types I created with SigmaPlot for my PhD and MD theses.
Update 2011: these days it looks much easier to work with the ggplot2 library; see basic graphs 2.
These files are all comma-separated value (CSV) text files:
In all the code that follows, you'll have to change the path names to reflect where you actually put these files. Note also that the code is longer than it needs to be, because it is spaced and commented for clarity.
There are a number of ways to achieve this. We'll look at one here based on the plotCI function, part of the gplots package. You need to install the gplots packages from the R menus if you haven't done so before (Packages > Install Packages > choose a CRAN mirror > select "gplots"). You may also need to edit the code below so that the filename points to wherever you have stored the PhD_fig37B.csv file above. The code below imports the data, ensures the gplots library is available, and then uses a plotCI call to create the basic graph and the first line with its error bars, another plotCI call to add the second line and error bars, an abline call to add an absolute-referenced line, and a legend. The code is commented using # characters for explanation.
library(gplots) # for plotCI
fig37b <-read.table(
"D:/Documents/Techniques/R/PhD_fig37B.csv",
header=TRUE, sep=",", na.strings="NA", dec=".", strip.white=TRUE
)
attach(fig37b) # this is perhaps not the most elegant method: it puts the contents of "fig37b" on the search path
plotCI(
x=session,
y=sham,
uiw=SEM_sham, # error bar length (default is to put this much above and below point)
pch=24, # symbol (plotting character) type: see help(pch); 24 = filled triangle pointing up
pt.bg="white", # fill colour for symbol
cex=1.5, # symbol size multiplier
lty=1, # line type: see help(par)
type="o", # p=points, l=lines, b=both, o=overplotted points/lines, etc.; see help(plot.default)
gap=0, # distance from symbol to error bar
sfrac=0.005, # width of error bar as proportion of x plotting region (default 0.01)
xlim=c(0.5,12.5), # x axis limits
xaxp=c(1,12,11), # x-min tick mark, x-max tick mark, number of intervals between tick marks
xlab="Session", # x axis label
ylim=c(-4,8),
yaxp=c(-4,8,6),
ylab="Difference score",
main="Two-stimulus discriminated approach task",
las=1, # axis labels horizontal (default is 0 for always parallel to axis)
font.lab=2 # 1 plain, 2 bold, 3 italic, 4 bold italic, 5 symbol
)
plotCI(
x=session,
y=ACCX,
uiw=SEM_accx,
pch=21, # symbol type 21 = filled circle
pt.bg="black",
cex=1.8,
lty=1,
type="o",
gap=0,
sfrac=0.005,
add=TRUE # ADD this plot to the previous one
)
abline(h=0, lty=3) # horizontal line at y=0, linetype 3 = dotted
legend(
x=0.5, # x coordinate of the top left of the legend
y=8.5, # y coordinate of the top left of the legend
box.lty=0, # line type to surround the legend box (0 for none)
legend=c("sham","ACCX","chance"), # sequence of text for the legend
pch=c(24,21,-1), # sequence of point types for the legend; -1 is a nonexistent point
pt.bg=c("white","black",NULL), # sequence of fill colours for the points
pt.cex=c(1.5,1.8,1),
lty=c(1,1,3) # sequence of line types for the legend
)
detach(fig37b) # for good form, detach it again after use...
rm(fig37b) # ... and delete it.
That produces the following in the R graphics window:
One way to export the graph is to save it as a PDF, which also renders it rather more nicely.
dev.print(pdf, file="D:/Temp/RGraph.pdf", width=5.1, height=5.1, pointsize=8) # width/height are in inches
(If you make it too small, characters start falling off!) The PDF that's produced is here. A screenshot of the PDF is below; note the rather better rendering (although the hyphen has become an en dash; fine for minus signs, not so great for text).
Here we introduce a few more fancy things, including expressions and Greek symbols in the axis labels, a categorical X axis, and a free-floating error bar.
fig21a <- read.table(
"D:/Documents/Techniques/R/PhD_fig21A.csv",
header=TRUE, sep=",", na.strings="NA", dec=".", strip.white=TRUE
)
attach(fig21a)
# now we define a new function for adding error bars:
superpose.eb <- function (x, y, ebl, ebu = ebl, length = 0.08, ...)
arrows(x, y + ebu, x, y - ebl, angle = 90, code = 3,
length = length, ...) # from http://users.fmg.uva.nl/rgrasman/rpages/2005/09/error-bars-in-plots.html
# in what follows, we force the four doses (0, 3, 10, 20) to be displayed as equally
# spaced categories (at absolute x positions 1,2,3,4), and then force the labels on
plot(
x=1:4, y=shamCR,
pch=21, bg="white", cex=1.5, type="o",
ylim=c(0,14),
yaxp=c(0,14,7),
yaxs="i", # makes the axis just fit the range, rather than extending 4% beyond
ylab=expression(sqrt("lever presses")),
xlab=substitute(paste("Intra-Acb amphetamine (",mu,"g)")), # substitute() with an empty environment seems necessary
axes=FALSE,
main="Conditioned reinforcement",
asp=0.5 # aspect ratio: here, one x unit is as long as 0.5 y units
)
axis(side=1, at=1:4, labels=dose) # turn on X axis
axis(side=2, las=1) # turn on Y axis (with text horizontal)
box(bty="L") # bty=box type to surround plot (e.g. "O", "L", "7", "C", "U" to set box shape or "n" for none)
lines( # use "lines" to add a line to an existing plot
x=1:4, y=shamNCR,
pch=24, bg="white", cex=1.5, type="o"
)
lines(x=1:4, y=ACCXCR, pch=21, bg="black", cex=1.5, type="o")
lines(x=1:4, y=ACCXNCR, pch=24, bg="black", cex=1.5, type="o")
superpose.eb(x=1,y=12,ebl=halfSED[1]) # superimpose +/- 0.5 SED, centred at (0,12); the value is in fig21a$halfSED[1]
text(x=1.5,y=12,labels="SED")
legend(
x=2, y=3, box.lty=0,
legend=c("sham, CRf","sham, NCRf","ACCX, CRf","ACCX, NCRf"),
pch=c(21,24,21,24),
pt.bg=c("white","white","black","black"),
pt.cex=c(1.5,1.5,1.5,1.5),
lty=c(1,1,1,1),
y.intersp=0.9, # squish things up a bit vertically
ncol=1 # one-column legend (=default)
)
detach(fig21a) # clean up
rm(fig21a)
After resizing the graphics window to make it a long and thin figure, that gives us the following:
Let's have a look at another one.
fig22d <-read.table(
"D:/Documents/Techniques/R/PhD_fig22D.csv",
header=TRUE, sep=",", na.strings="NA", dec=".", strip.white=TRUE
)
attach(fig22d)
superpose.eb <- function (x, y, ebl, ebu = ebl, length = 0.08, ...)
arrows(x, y + ebu, x, y - ebl, angle = 90, code = 3,
length = length, ...) # from http://users.fmg.uva.nl/rgrasman/rpages/2005/09/error-bars-in-plots.html
# our data begin as:
# group CSpluslatency CSminuslatency CSplusSEM CSminusSEM
# 1 sham 3.884335 4.212531 0.2248404 0.2593122
# 2 ACCX 4.672486 3.619434 0.3476289 0.4902273
#
# step 1: reorganize
latencies = t(matrix( c(CSpluslatency, CSminuslatency), 2, 2)) # create a 2-by-2 matrix, then transpose it
colnames(latencies) = group
rownames(latencies) = c("CS+","CS-")
sems = t(matrix(c(CSplusSEM, CSminusSEM), 2, 2)) # do the same for the SEMs
colnames(sems) = group
rownames(sems) = c("CS+","CS-")
# latencies, for example, is now:
# sham ACCX
# CS+ 3.884335 4.672486
# CS- 4.212531 3.619434
fillcolours = c("gray","white")
x.abscis <- barplot(
latencies, beside=TRUE,
col=fillcolours,
space=c(0.4,2), # spacing between bars in the same group, and then between groups
ylim=c(0,5.5),
ylab="Latency to approach (s)",
main="Autoshaping approach latencies"
)
box(bty="L")
superpose.eb(x.abscis, latencies, ebl=0, ebu=sems) # +1 SEM, no descending error bar
# Note that the X coordinates were stored in x.abscis
legend(x=5, y=5.5, box.lty=0, legend=rownames(latencies), fill=fillcolours)
# Axis breaks, not reproduced here, are discussed at:
# http://tolstoy.newcastle.edu.au/R/help/05/11/15877.html
# https://stat.ethz.ch/pipermail/r-help/2005-May/072058.html
# Most use axis.break from the plotrix library for the break drawing itself.
# Others use gap.plot, gap.barplot, gap.boxplot from the same package.
# However, these options need a bit of careful massaging to get right. A simple example is shown below.
detach(fig22d) # clean up
rm(fig22d, latencies, sems, fillcolours, x.abscis)
That produces this:
Here's the code:
fig23 <- read.table(
"D:/Documents/Techniques/R/PhD_fig23.csv",
header=TRUE, sep=",", na.strings="NA", dec=".", strip.white=TRUE
)
attach(fig23)
superpose.eb <- function (x, y, ebl, ebu = ebl, length = 0.08, ...)
arrows(x, y + ebu, x, y - ebl, angle = 90, code = 3,
length = length, ...) # from http://users.fmg.uva.nl/rgrasman/rpages/2005/09/error-bars-in-plots.html
approaches = t(matrix( c(CSplus, CSminus), 2, 2)) # create a 2-by-2 matrix, then transpose it
colnames(approaches) = group
rownames(approaches) = c("CS+","CS-")
sems = t(matrix(c(SEMCSplus, SEMCSminus), 2, 2)) # do the same for the SEMs
colnames(sems) = group
rownames(sems) = c("CS+","CS-")
fillcolours = c("gray","white")
x.abscis <- barplot(
approaches, beside=TRUE,
col=fillcolours,
space=c(0.5,2), # spacing between bars in the same group, and then between groups
width=1, # bar widths
ylim=c(0,16),
yaxp=c(0,16,8),
ylab="Number of approaches",
xlim=c(1.5,9.5), # makes sense in the context of width and space parameters
xlab="Group",
axis.lty=1, # enable tick marks on the X axis
main="Autoshaping probe test",
font.lab=2 # bold for axis labels
)
box(bty="L")
superpose.eb(x.abscis, approaches, ebl=0, ebu=sems) # +1 SEM, no descending error bar
legend(x=7, y=16, box.lty=0, legend=rownames(approaches), fill=fillcolours, y.intersp=1)
segments(x0=2.5, y0=15.5, x1=2.5, y1=16.0) # add three line segments to make a "comparator" line
segments(x0=2.5, y0=16.0, x1=4.0, y1=16.0)
segments(x0=4.0, y0=16.0, x1=4.0, y1=6.0)
text(x=3.25, y=15.5, labels="***", family="mono", font=1, ps=8) # add a "significance" label
detach(fig23) # clean up
rm(fig23, approaches, sems, fillcolours, x.abscis)
And here's the output:
Axis breaks are a little tricky; see the links above for further discussion. This illustrates the simplest type of axis breaks, where there aren't really multiple simultaneous plotting scales.
library(gplots) # for plotCI
library(plotrix) # for axis.break
fig28c <- read.table(
"D:/Documents/Techniques/R/PhD_fig28C.csv",
header=TRUE, sep=",", na.strings="NA", dec=".", strip.white=TRUE
)
attach(fig28c) # this is perhaps not the most elegant method: it puts the contents of "fig37b" on the search path
gplots::plotCI(
x=block, # note that since there's a plotrix::plotCI and a gplots::plotCI, we must be explicit
y=sham,
uiw=SEMsham,
pch=24, # 24 = filled triangle pointing up
pt.bg="white", # fill colour for symbol
cex=1.5, # symbol size multiplier
lty=1, # line type: see help(par)
type="o", # p=points, l=lines, b=both, o=overplotted points/lines, etc.; see help(plot.default)
gap=0, # distance from symbol to error bar
sfrac=0.005, # width of error bar as proportion of x plotting region (default 0.01)
xlim=c(-3.5,5.5), # x axis limits
xaxp=c(-3,5,8), # x-min tick mark, x-max tick mark, number of intervals between tick marks
xaxs="i", # makes the axis just fit the range, rather than extending 4% beyond
xlab="Trial block", # x axis label
ylim=c(0.4,1), # NOTE AXIS FROM 0.4 TO 1.0
yaxp=c(0.4,1,6),
yaxs="i", # makes the axis just fit the range, rather than extending 4% beyond
axes=FALSE,
ylab="Difference score",
las=1, # axis labels horizontal (default is 0 for always parallel to axis)
font.lab=2 # 1 plain, 2 bold, 3 italic, 4 bold italic, 5 symbol
)
box() # draw four sides to the plot area
axis(
side=1, # X axis
at=c(-3,-2,-1, 1,2,3,4,5),
labels=c("-3","-2","-1", "1","2","3","4","5"),
)
axis(side=1,at=0,labels="surgery", tick=FALSE) # additional X axis label without a tick mark
axis(
side=2, # Y axis
las=1, # text horizontal
at=c(0.4,0.5,0.6,0.7,0.8,0.9,1.0), # position of labels
labels=c("0.0","0.5","0.6","0.7","0.8","0.9","1.0") # NOTE ACTUAL LABELS: THE "0.0" IS FALSELY POSITIONED
)
axis.break(axis=2,breakpos=0.45,style="slash") # break the left Y axis
axis.break(axis=4,breakpos=0.45,style="slash") # break the right Y axis
axis.break(axis=1,breakpos=0,style="slash") # break the bottom X axis
axis.break(axis=3,breakpos=0,style="slash") # break the top X axis
gplots::plotCI(
x=block,
y=ACCX,
uiw=SEMACCX,
pch=21, # symbol type 21 = filled circle
pt.bg="black",
cex=1.8,
lty=1,
type="o",
gap=0,
sfrac=0.005,
add=TRUE # ADD this plot to the previous one
)
abline(h=0.5, lty=3)
legend(
x=-3, # x coordinate of the top left of the legend
y=0.65, # y coordinate of the top left of the legend
box.lty=0, # line type to surround the legend box (0 for none)
legend=c("sham","ACCX","chance performance"), # sequence of text for the legend
pch=c(24,21,-1), # sequence of point types for the legend
pt.bg=c("white","black",NULL), # sequence of fill colours for the points
pt.cex=c(1.5,1.8,1),
lty=c(1,1,3) # sequence of line types for the legend
)
detach(fig28c) # for good form, detach it again after use...
rm(fig28c) # ... and delete it.
Here's the output, via the PDF renderer as above:
The code:
library(gplots) # for plotCI
# First, make a panel of plots. The par, layout, and split.screen commands are three ways to do this.
# We want a notional square to be divided so that the left half is figure 1, and the right half is figure 2.
# This means one row, two columns.
par(mfcol=c(1,2))
# Panel 1
mdfig30a <- read.table(
"D:/Documents/Techniques/R/MD_fig30A.csv",
header=TRUE, sep=",", na.strings="NA", dec=".", strip.white=TRUE
)
attach(mdfig30a) # this is perhaps not the most elegant method: it puts the contents of "fig37b" on the search path
par(mfg=c(1,1)) # select left-hand figure for drawing
plotCI(
x=delay, # note that since there's a plotrix::plotCI and a gplots::plotCI, we must be explicit
y=sham,
uiw=SEMsham,
pch=21, # 24 = filled circle
pt.bg="white", # fill colour for symbol
cex=1.8, # symbol size multiplier
lty=1, # line type: see help(par)
type="o", # p=points, l=lines, b=both, o=overplotted points/lines, etc.; see help(plot.default)
gap=0, # distance from symbol to error bar
sfrac=0.005, # width of error bar as proportion of x plotting region (default 0.01)
xlim=c(-5,23), # x axis limits
xaxp=c(0,20,2), # x-min tick mark, x-max tick mark, number of intervals between tick marks
xaxs="i", # makes the axis just fit the range, rather than extending 4% beyond
xlab="Programmed delay (s)", # x axis label
ylim=c(0,3.3),
yaxp=c(0,3,3),
yaxs="i", # makes the axis just fit the range, rather than extending 4% beyond
ylab=expression(sqrt("active lever presses per minute in session 10")),
las=1, # axis labels horizontal (default is 0 for always parallel to axis)
font.lab=1, # 1 plain, 2 bold, 3 italic, 4 bold italic, 5 symbol
bty="L" # box type
)
plotCI(
x=delay,
y=AcbC,
uiw=SEMAcbC,
pch=21, # symbol type 21 = filled circle
pt.bg="black",
cex=1.8,
lty=1,
type="o",
gap=0,
sfrac=0.005,
add=TRUE # ADD this plot to the previous one
)
legend(
x=0, y=0.6, box.lty=0,
legend=c("sham","AcbC"),
pch=c(21,21),
pt.bg=c("white","black"),
pt.cex=c(1.8,1.8),
lty=c(1,1)
)
text(x=0,y=3.2,labels="*", family="mono", font=2, ps=8)
text(x=10,y=1.7,labels="*", family="mono", font=2, ps=8)
text(x=20,y=1.2,labels="***", family="mono", font=2, ps=8)
detach(mdfig30a)
rm(mdfig30a)
# Panel 2
mdfig30b <- read.table(
"D:/Documents/Techniques/R/MD_fig30B.csv",
header=TRUE, sep=",", na.strings="NA", dec=".", strip.white=TRUE
)
attach(mdfig30b) # this is perhaps not the most elegant method: it puts the contents of "fig37b" on the search path
par(mfg=c(1,2)) # select right-hand figure for drawing
# first the AcbC data, with y-direction error bars
plotCI(
x=AcbCdelay, # note that since there's a plotrix::plotCI and a gplots::plotCI, we must be explicit
y=AcbCresponses,
uiw=SEMshamresponses, # upper error bar (default is lower = upper)
err="y", # Y-direction for error bars
pch=21, pt.bg="black", cex=1.8, lty=1, type="o",
gap=0, sfrac=0.005,
xlim=c(-3,25), xaxp=c(0,25,5), xaxs="i", xlab="Experienced delay from response\nto reinforcer collection (s)",
ylim=c(0,3.3), yaxp=c(0,3,3), yaxs="i", ylab="",
las=1, font.lab=1, bty="L"
)
# add X-direction error bars for that plot...
plotCI(
x=AcbCdelay, y=AcbCresponses, uiw=SEMAcbCdelay, err="x",
pch=21, pt.bg="black", cex=1.8, lty=1, type="o",
gap=0, sfrac=0.005,
add=TRUE
)
# now the sham data
plotCI(
x= shamdelay, y= shamresponses, uiw= SEMshamresponses, err="y",
pch=21, pt.bg="white", cex=1.8, lty=1, type="o",
gap=0, sfrac=0.005,
add=TRUE
)
# add X-direction error bars for that plot...
plotCI(
x=shamdelay, y=shamresponses, uiw=SEMshamdelay, err="x",
pch=21, pt.bg="white", cex=1.8, lty=1, type="o",
gap=0, sfrac=0.005,
add=TRUE
)
legend(
x=0, y=0.6, box.lty=0,
legend=c("sham","AcbC"),
pch=c(21,21),
pt.bg=c("white","black"),
pt.cex=c(1.8,1.8),
lty=c(1,1)
)
text(x=10,y=1.4,labels="###", family="mono", font=2, ps=8)
detach(mdfig30b)
rm(mdfig30b)
The output: