[Rd] extending boxplot with space() argument?

Ben Bolker bolker at zoo.ufl.edu
Thu Sep 11 12:39:55 MEST 2003

  A student asked me if it was possible to draw boxplots where the boxes 
themselves were grouped: I was able to hack boxplot.formula() to do the 
right thing, more or less, by incorporating an argument (space) and some 
code from barplot.

with the extended boxplot.formula() below, the following commands "do the 
right thing" (produce boxes grouped by levels of the second factor):

r = runif(480)
f1 = gl(4,120)
f2 = gl(10,4,480)
boxplot(r ~ f1*f2,space=c(0.5,2),col=2:5)

  I think this *might* be generally useful: for example, the last example
in example(boxplot) sets up a grouped boxplot by hand, plotting one group
with an offset and then the other.  This would be much easier with my
hack. (I know we're in feature-freeze for 1.8.0 right now ... my usual
good timing.)

  The one thing that makes this hack difficult/ugly is that all the
spacing calculations (e.g. if one specifies varwidth=TRUE) are handled in
the bxp() function: the calling sequence is boxplot -> boxplot.formula ->
boxplot.default -> bxp , so I had to replicate some of the spacing
calculations within boxplot.formula, which is ugly and incomplete.  Can
anyone suggest an elegant way to deal with this?

"boxplot.formula" <-
  function (formula, data = NULL, width=1,
            space = 0.5, xlim=NULL, ylim=NULL,
            horizontal = FALSE, at = NULL, log="",
            add=FALSE, ..., subset) 
  if (missing(formula) || (length(formula) != 3)) 
    stop("formula missing or incorrect")
  m <- match.call(expand.dots = FALSE)
  if (is.matrix(eval(m$data, parent.frame()))) 
    m$data <- as.data.frame(data)
  m$... <- m$space <- m$xlim <- m$ylim <- m$add <- m$horizontal <-
    m$at <- m$log <- m$boxwex <- NULL
  m[[1]] <- as.name("model.frame")
  mf <- eval(m, parent.frame())
  response <- attr(attr(mf, "terms"), "response")
  ## spacing code adapted from barplot()
  f <- mf[-response]
  f <- lapply(f,as.factor) ## just in case
  dims <- sapply(lapply(f,levels),length)
  n <- prod(dims)
  width <- if (!is.null(width)) {
    if (any(is.na(width)) | any(width <= 0))
      stop("invalid boxplot widths")
    boxwex * width/max(width)
  width <- rep(width, length=n)
  delta <- width/2
  if (is.null(at)) {
    if (length(space) == 2) { 
      space <- rep(c(space[2], rep(space[1], dims[1] - 1)), 
                   dims[2]) }
    w.r <- cumsum(space + width)
    w.m <- w.r - delta
  ## ugly: have to replicate spacing/plotting code from
  ## bxp() here (since boxplot doesn't take xlim as a parameter)
  if (is.null(xlim)) xlim <- range(at)+c(-delta[1],delta[length(delta)])
  if (is.null(ylim)) ylim <- range(mf[[response]])
  if (!add) {
    if (horizontal) 
      plot.window(ylim = xlim, xlim = ylim, log = log)
    else plot.window(xlim = xlim, ylim = ylim, log = log)
  b <- boxplot(split(mf[[response]], f),at=at,
               horizontal=horizontal, boxwex=boxwex,
               add=TRUE, ...)

620B Bartram Hall                            bolker at zoo.ufl.edu
Zoology Department, University of Florida    http://www.zoo.ufl.edu/bolker
Box 118525                                   (ph)  352-392-5697
Gainesville, FL 32611-8525                   (fax) 352-392-3704

More information about the R-devel mailing list