Home > Uncategorized > R’s plot function, the 1970’s retro look is not cool any more

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.

Tags: ,
  1. D L McArthur
    April 22nd, 2015 at 20:06 | #1

    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()
    }

  2. D L McArthur
    April 22nd, 2015 at 20:19 | #2

    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)
    }

  3. April 22nd, 2015 at 20:45 | #3

    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.

  4. April 22nd, 2015 at 20:50 | #4

    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.

  5. April 22nd, 2015 at 21:47 | #5

    @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).

  6. Adam
    April 24th, 2015 at 00:46 | #6

    @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

  7. Edward Carney
    April 24th, 2015 at 19:08 | #7

    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.)

  8. April 24th, 2015 at 22:43 | #8

    @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

  9. Aaron Robotham
    April 25th, 2015 at 06:07 | #9

    Author of magicaxis here! What are you referring to Derek? In willing to be convinced to improve it :-)

  10. April 25th, 2015 at 07:12 | #10

    @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).

  11. Aaron Robotham
    April 28th, 2015 at 06:11 | #11

    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).

  12. May 11th, 2015 at 23:02 | #12

    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/

  1. No trackbacks yet.

A question to answer *