Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Colorfill boxplot in R-cran with lines, dots, or similar

I need to use black and white color for my boxplots in R. I would like to colorfill the boxplot with lines and dots. For an example:

enter image description here

I imagine ggplot2 could do that but I can't find any way to do it.

Thank you in advance for your help!

like image 732
user2427949 Avatar asked Jul 02 '13 10:07

user2427949


1 Answers

I thought this was a great question and pondered if it was possible to do this in base R and to obtain the checkered look. So I put together some code that relies on boxplot.stats and polygon (which can draw angled lines). Here's the solution, which is really not ready for primetime, but is a solution that could be tinkered with to make more general.

boxpattern <- 
function(y, xcenter, boxwidth, angle=NULL, angle.density=10, ...) {
    # draw an individual box
    bstats <- boxplot.stats(y)
    bxmin <- bstats$stats[1]
    bxq2 <- bstats$stats[2]
    bxmedian <- bstats$stats[3]
    bxq4 <- bstats$stats[4]
    bxmax <- bstats$stats[5]
    bleft <- xcenter-(boxwidth/2)
    bright <- xcenter+(boxwidth/2)
    # boxplot
    polygon(c(bleft,bright,bright,bleft,bleft),
        c(bxq2,bxq2,bxq4,bxq4,bxq2), angle=angle[1], density=angle.density)
    polygon(c(bleft,bright,bright,bleft,bleft),
        c(bxq2,bxq2,bxq4,bxq4,bxq2), angle=angle[2], density=angle.density)
    # lines
    segments(bleft,bxmedian,bright,bxmedian,lwd=3) # median
    segments(bleft,bxmin,bright,bxmin,lwd=1) # min
    segments(xcenter,bxmin,xcenter,bxq2,lwd=1)
    segments(bleft,bxmax,bright,bxmax,lwd=1) # max
    segments(xcenter,bxq4,xcenter,bxmax,lwd=1)
    # outliers
    if(length(bstats$out)>0){
        for(i in 1:length(bstats$out))
            points(xcenter,bstats$out[i])
    }
}

drawboxplots <- function(y, x, boxwidth=1, angle=NULL, ...){
    # figure out all the boxes and start the plot
    groups <- split(y,as.factor(x))
    len <- length(groups)
    bxylim <- c((min(y)-0.04*abs(min(y))),(max(y)+0.04*max(y)))
    xcenters <- seq(1,max(2,(len*(1.4))),length.out=len)
    if(is.null(angle)){
        angle <- seq(-90,75,length.out=len)
        angle <- lapply(angle,function(x) c(x,x))
    }
    else if(!length(angle)==len)
        stop("angle must be a vector or list of two-element vectors")
    else if(!is.list(angle))
        angle <- lapply(angle,function(x) c(x,x))
    # draw plot area
    plot(0, xlim=c(.97*(min(xcenters)-1), 1.04*(max(xcenters)+1)),
        ylim=bxylim, 
        xlab="", xaxt="n",
        ylab=names(y), 
        col="white", las=1)

    axis(1, at=xcenters, labels=names(groups))
    # draw boxplots
    plots <- mapply(boxpattern, y=groups, xcenter=xcenters,
        boxwidth=boxwidth, angle=angle, ...)
}

Some examples in action:

mydat <- data.frame(y=c(rnorm(200,1,4),rnorm(200,2,2)),
                    x=sort(rep(1:2,200)))
drawboxplots(mydat$y, mydat$x)

enter image description here

mydat <- data.frame(y=c(rnorm(200,1,4),rnorm(200,2,2),
                        rnorm(200,3,3),rnorm(400,-2,8)),
                    x=sort(rep(1:5,200)))
drawboxplots(mydat$y, mydat$x)

enter image description here

drawboxplots(mydat$y, mydat$x, boxwidth=.5, angle.density=30)

enter image description here

drawboxplots(mydat$y, mydat$x, # specify list of two-element angle parameters
             angle=list(c(0,0),c(90,90),c(45,45),c(45,-45),c(0,90)))

enter image description here

EDIT: I wanted to add that one could also obtain dots as a fill by basically drawing a pattern of dots, then covering them a "donut"-shaped polygon, like so:

x <- rep(1:10,10)
y <- sort(x)
plot(y~x, xlim=c(0,11), ylim=c(0,11), pch=20)
outerbox.x <- c(2.5,0.5,10.5,10.5,0.5,0.5,2.5,7.5,7.5,2.5)
outerbox.y <- c(2.5,0.5,0.5,10.5,10.5,0.5,2.5,2.5,7.5,7.5)
polygon(outerbox.x,outerbox.y, col="white", border="white") # donut
polygon(c(2.5,2.5,7.5,7.5,2.5),c(2.5,2.5,2.5,7.5,7.5)) # inner box

enter image description here

But mixing that with angled lines in a single plotting function would be a bit difficult, and is generally a bit more challenging, but it starts to get you there.

like image 164
Thomas Avatar answered Sep 21 '22 11:09

Thomas