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