From bolker at zoo.ufl.edu  Thu Sep 11 12:39:55 2003
From: bolker at zoo.ufl.edu (Ben Bolker)
Date: Thu Sep 11 17:28:27 2003
Subject: [Rd] extending boxplot with space() argument?
Message-ID: <Pine.LNX.4.44.0309111132510.5239-100000@bolker.zoo.ufl.edu>


  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="",
            boxwex=0.8,
            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
    at=w.m
  }
  ## 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) {
    plot.new()
    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, ...)
  invisible(b)
}



-- 
620B Bartram Hall                            bolker@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


[Rd] extending boxplot with space() argument?
Ben Bolker