One common frustration that I have heard expressed about R is that there is no automatic way to plot error bars (whiskers really) on bar plots. I just encountered this issue revising a paper for submission and figured I'd share my code. The following simple function will plot reasonable error bars on a bar plot.

error.bar < function(x, y, upper, lower=upper, length=0.1,...){

if(length(x) != length(y)  length(y) !=length(lower)  length(lower) != length(upper))

stop("vectors must be same length")

arrows(x,y+upper, x, ylower, angle=90, code=3, length=length, ...)

}
Now let's use it. First, I'll create 5 means drawn from a Gaussian random variable with unit mean and variance. I want to point out another mild annoyance with the way that R handles bar plots, and how to fix it. By default, barplot() suppresses the Xaxis. Not sure why. If you want the axis to show up with the same line style as the Yaxis, include the argument axis.lty=1, as below. By creating an object to hold your bar plot, you capture the midpoints of the bars along the abscissa that can later be used to plot the error bars.

y < rnorm(500, mean=1)

y < matrix(y,100,5)

y.means < apply(y,2,mean)

y.sd < apply(y,2,sd)

barx < barplot(y.means, names.arg=1:5,ylim=c(0,1.5), col="blue", axis.lty=1, xlab="Replicates", ylab="Value (arbitrary units)")

error.bar(barx,y.means, 1.96*y.sd/10)
Now let's say we want to create the very common plot in reporting the results of scientific experiments: adjacent bars representing the treatment and the control with 95% confidence intervals on the estimates of the means. The trick here is to create a 2 x n matrix of your bar values, where each row holds the values to be compared (e.g., treatment vs. control, male vs. female, etc.). Let's look at our same Gaussian means but now compare them to a Gaussian r.v. with mean 1.1 and unit variance.

y1 < rnorm(500, mean=1.1)

y1 < matrix(y1,100,5)

y1.means < apply(y1,2,mean)

y1.sd < apply(y1,2,sd)


yy < matrix(c(y.means,y1.means),2,5,byrow=TRUE)

ee < matrix(c(y.sd,y1.sd),2,5,byrow=TRUE)*1.96/10

barx < barplot(yy, beside=TRUE,col=c("blue","magenta"), ylim=c(0,1.5), names.arg=1:5, axis.lty=1, xlab="Replicates", ylab="Value (arbitrary units)")

error.bar(barx,yy,ee)
Clearly, a sample size of 100 is too small to show that the means are significantly different. The effect size is very small for the variability in these r.v.'s. Try 10000.

y < rnorm(50000, mean=1)

y < matrix(y,10000,5)

y.means < apply(y,2,mean)

y.sd < apply(y,2,sd)

y1 < rnorm(50000, mean=1.1)

y1 < matrix(y1,10000,5)

y1.means < apply(y1,2,mean)

y1.sd < apply(y1,2,sd)

yy < matrix(c(y.means,y1.means),2,5,byrow=TRUE)

ee < matrix(c(y.sd,y1.sd),2,5,byrow=TRUE)*1.96/sqrt(10000)

barx < barplot(yy, beside=TRUE,col=c("blue","magenta"), ylim=c(0,1.5), names.arg=1:5, axis.lty=1, xlab="Replicates", ylab="Value (arbitrary units)")

error.bar(barx,yy,ee)
That works. Maybe I'll show some code for doing power calculations next time...
JCobb // Mar 21, 2013 at 13:08
So when I call the error.bar function (on my own data or on the simulated data provided I get a "plot.new has not been called yet" error message. Any thoughts?
jhj1 // Mar 21, 2013 at 13:17
You need to do the barplot first. error.bar.R adds the error bars to an existing bar plot.