R’s plot function, the 1970’s retro look is not cool any more
Casual users of a system want to learn a few simple rules that enable them to get most things done. Many languages have a design principle of only providing one way of doing things. Members of one language family are known for providing umpteen different ways of doing something and R is no exception.
R comes with the plot
function as part of the base system. I am an admirer of plot
‘s ability to take whatever is thrown at it and generally produce a workman-like graphical image; workman-like is a kinder description than 1970’s retro look.
R has a thriving library of add-on packages and the package ggplot2 is a byword for fancy graphics in the R community. Anybody reading the description of the qplot
function, in this package, would think it is the death kneel for plot
. They would be wrong, qplot
contains a fatal flaw, it does a very poor job of handling the simple stuff (often generating weird error messages in the process).
In the beginning I’m sure Hadley Wickham, the design+implementor of ggplot/ggplot2, was more concerned with getting his ideas implemented and was not looking to produce a replacement for plot. Unfortunately it looks as-if the vision for functions in the ggplot
package is as high-end plot
replacements (i.e., for power users) and not as universal plot
replacements (i.e., support for casual users).
This leaves me pulling my hair out trying to produce beautiful looking graphs for a book I am working on. The readership are likely to be casual users of R and I am trying to recommend one way of doing something for every task. The source code+data of all the examples will be freely available and I’m eating my own dog food, so its plot
I have to use.
Derek – Don’t lose hope with “plot” as a decent workable solution. With a very few additional commands one can readily produce publication-quality material. See “minor.text”, “minor.tick”, “textinplot” and “xyline” below.
## ….place extra text on any axis of existing graphic
minor.text <- function(txt, side=1, off = 0.55, cex=1,
srt = if(side == 2) 90 else
if(side == 4) 270 else 0, …) {
# adapted from http://biostatmatt.com/archives/2522
# off – numeric, offset in inches from the edge of the plotting region
# srt – string rotation in degrees
usr <- par('usr')
pin <- par('pin')
upi <- c(usr[2]-usr[1],
usr[4]-usr[3]) / pin
xpos <- (usr[1] + usr[2])/2
ypos <- (usr[3] + usr[4])/2
if(1 == side) ypos <- usr[3] – upi[2] * off
if(2 == side) xpos <- usr[1] – upi[1] * off
if(3 == side) ypos <- usr[4] + upi[2] * off
if(4 == side) xpos <- usr[2] + upi[1] * off
text(x=xpos, y=ypos, txt, xpd=NA, srt=srt, cex=cex, …)
}
## ….place extra ticks and/or crosshairs on x or y axes of existing graphic
## adapted from Hmisc::minor.tick
minor.tick <- function (nx = 0, ny = 0, tick.ratio = 0.5, topright=NULL, gridlines=NULL)
{
ax <- function(w, n, tick.ratio) {
range <- par("usr")[if (w == "x")
1:2
else 3:4]
tick.pos <- if (w == "x")
par("xaxp")
else par("yaxp")
distance.between.minor <- (tick.pos[2] – tick.pos[1])/tick.pos[3]/n
possible.minors <- tick.pos[1] – (0:100) * distance.between.minor
low.minor = range[1]])
if (is.na(low.minor))
low.minor <- tick.pos[1]
possible.minors <- tick.pos[2] + (0:100) * distance.between.minor
hi.minor <- max(possible.minors[possible.minors <= range[2]])
if (is.na(hi.minor)) hi.minor 1) ax(“x”, nx, tick.ratio = tick.ratio)
if (ny > 1) ax(“y”, ny, tick.ratio = tick.ratio)
if(!is.null(gridlines)) {
xpos <- par('xaxp')
ypos <- par('yaxp')
xyline(seq(xpos[1],xpos[2],(xpos[2]-xpos[1])/(xpos[3])),col='grey')
xyline(y=seq(ypos[1],ypos[2],(ypos[2]-ypos[1])/(ypos[3])),col='grey')
}
invisible()
}
## …. puts text inside plot region at mouseclick
textinplot <- function(saywhat='*', cex=.9, …)
{
print ('…waiting for mouseclick on plot')
x <-locator(2)
text(x[[1]][1], x[[2]][1], saywhat, cex=cex,…)
invisible(x)
}
## ….draw specific vertical and/or horizontal line on existing graphic
xyline <- function (x=NA,y=NA,col='black',…)
{ abline (v=x,col=col,…)
invisible()
abline (h=y,col=col,…)
invisible()
}
In the right circumstances you might even use “arrowinplot”:
## https://github.com/kbroman/broman/blob/master/R/arrowlocator.R
arrowinplot <- function(reverse=FALSE, horizontal=FALSE,
vertical=FALSE, length=0.1, col='steelblue2', lwd=2,…)
{
print ("…waiting for initial then start & end clicks on plot")
x <- locator(2)
if(reverse) x <- lapply(x, rev)
if(horizontal) x[[2]] <- rep(mean(x[[2]]), 2)
if(vertical) x[[1]] <- rep(mean(x[[1]]), 2)
arrows(x[[1]][1], x[[2]][1], x[[1]][2], x[[2]][2], length=length,
col=col, lwd=lwd,…)
x <- matrix(unlist(x), ncol=2)
dimnames(x) <- list(c("tail","head"), c("x","y"))
invisible(x)
}
For the purposes of the code in your book, one thing to consider is that it is highly unlikely that the plot() function (and other base R plotting functions) will be changed in non-backward-compatible ways in the future. I don’t think it’s particularly safe to assume that this will be true of functions from the ggplot2 package.
I’d consider just teaching people ggplot2 across your entire book if you’re looking to do all or nothing.
I find that if I start people off entirely with gpplot2, it’s a bit more cognitive work up front to comprehend the model but after that it’s more logical and easier to extend. There’s 5 minutes of thinking-hard faces whilst they put their first chart or two together and after that, they’re off.
I teach very little base R though. After struggling as a newb with base R and then finding packages that did things easier, cleaner, faster I like to shortcut folks to the newer stuff and save them learning lots of the old ways of doing things.
@D L McArthur
Thanks for the coded suggestions. My continuing pet hate is the use of ‘programming language’ notation for powers of 10 for axis labels. The magicaxis package offers support for post-1970 formatting of large/small values at the cost of having to write extra code for every plot (and the package author is not interested in fully supporting all relevant plot options :-(; disinterest in supporting the common way of doing thing is a common refrain in authors of packages).
@Jake Westfall (@CookieSci)
I’m not sure that’s an issue to be concerned with since ggplot2 is in maintenance mode.
I want to formally announce that ggplot2 is shifting to maintenance
mode. This means that we are no longer adding new features, but we
will continue to fix major bugs, and consider new features submitted
as pull requests. In recognition this significant milestone, the next
version of ggplot2 will be 1.0.0.
From: https://groups.google.com/forum/#!topic/ggplot2/SSxt8B8QLfo
I think that the systems can co-exist; in fact, both serve a purpose. I can do things quickly in R Basic Graphics that would likely take at least a bit longer to accomplish in ggplot2.
Recently, I had occasion to develop a graphic that had a “stack” of data. First came a waveform (from a WAV file); below it, the contents of the speech in the file in IPA notation (Unicode); above that that came two separate waveforms which plotted the output of two loudness estimation models. The “primitives” in R Basic Graphics proved more than equal to the task and worked so intuitively that I was done in a matter of 90 minutes or so—what with all the tweaking. (Here’s the code in RPubs.)
@Edward Carney
Yes, they can co-exist. But if qplot did a better job on the simple stuff it would make it the function of choice for occasional users that offered a simple path to more sophisticated plots.
I could have done with a phonetician a few weeks ago
Author of magicaxis here! What are you referring to Derek? In willing to be convinced to improve it 🙂
@Aaron Robotham
Hi, I was referring to our email discussion about support for various
par
options. Like all people who write open source software, including me, you have your own ideas about what you want to do; I would probably also have limited interest in spending time supporting lots of compatibility stuff.The ideal solution is for the maintainers of
plot
to pick up your code, or the ideas behind it, and support it in the base system (so an extra function call is not needed to change the axis).Ah yes, I managed to find what you were referring to. It’s hard because I have a strong view that the defaults are poor, so over write them by default (saves me doing it each time). This means you have to over ride my changes within magplot, you can’t simply change the par options globally.
They should really add the nice log formatting to base, the default is not publishable quality in my field (astrophysics).
You might want to look at the R Graph Catalog – it’s a collection of plots reproduced only with ggplot2 and shows you what code to use for each wanted plot. I agree that doing very basic plots can be more verbose with ggplot2, but going beyond extremely basic I think ggplot2 wins
http://shinyapps.stat.ubc.ca/r-graph-catalog/