# How to add horizontal lines to ggplot2 boxplot?

I have a boxplot output in R using ggplot2:

p <- ggplot(data, aes(y = age, x = group))
p <- p + geom_boxplot()
p <- p + scale_x_discrete(name= "Group",)
p <- p + scale_y_continuous(name= "Age")
p


I need to add horisontal lines like on common boxplot (and to change vertical line style if possible):

boxplot(age~group,data=data,names=c('1','2'),ylab="Age", xlab="Group")


How I could do this using ggplot2?

• I am also interested in this. The only way I know how to add those "caps" at the ends of the whiskers is to add a "+ geom_errorbar()" but then the line will overlap the boxplot and look quite messy. Mar 10, 2011 at 17:59
• @Casey Returns error:geom_errorbar requires the following missing aesthetics: ymin, ymax :( Should I add extra data? Mar 10, 2011 at 18:03
• @Yuriy By horizontal line, you mean arrow-like whiskers or add e.g., the mean together with the median inside the box?
– chl
Mar 10, 2011 at 19:01
• @Yuriy Yes I think you'd have to add the min and max for each group to get errorbar to display correctly. Maybe there's a way to pass the min and max values from the boxplot function to geom_errorbar. Mar 10, 2011 at 19:21
• @Casey Just invert the order of layers and this solution will work as wanted.
– user88
Mar 10, 2011 at 20:23

Found solution myself. Maybe someone could use it:

#step 1: preparing data
mean=mean(age),
sd=sd(age),
min=min(age),
max=max(age),
median=median(age),
Q1=summary(age)['1st Qu.'],
Q3=summary(age)['3rd Qu.']
)
#step 2: correction for outliers
out <- data.frame() #initialising storage for outliers
for(group in 1:length((levels(factor(data$group))))){ bps <- boxplot.stats(data$age[data$group == group],coef=1.5) ageMetaData[ageMetaData$group == group,]$min <- bps$stats[1] #lower wisker
ageMetaData[ageMetaData$group == group,]$max <- bps$stats[5] #upper wisker if(length(bps$out) > 0){ #adding outliers
for(y in 1:length(bps$out)){ pt <-data.frame(x=group,y=bps$out[y])
out<-rbind(out,pt)
}
}
}
#step 3: drawing
p <- ggplot(ageMetaData, aes(x = group,y=mean))
p <- p + geom_errorbar(aes(ymin=min,ymax=max),linetype = 1,width = 0.5) #main range
p <- p + geom_crossbar(aes(y=median,ymin=Q1,ymax=Q3),linetype = 1,fill='white') #box
# drawning outliers if any
if(length(out) >0) p <- p + geom_point(data=out,aes(x=x,y=y),shape=4)
p <- p + scale_x_discrete(name= "Group")
p <- p + scale_y_continuous(name= "Age")
p


The quantile data resulution is ugly, but works. Maybe there is another way. The result looks like this:

Also improved boxplot a little:

1. added second smaller dotted errorbar to reflect sd range.
2. added point to reflect mean
3. removed background

maybe this also could be useful to someone:

p <- ggplot(ageMetaData, aes(x = group,y=mean))
p <- p + geom_errorbar(aes(ymin=min,ymax=max),linetype = 1,width = 0.5) #main range
p <- p + geom_crossbar(aes(y=median,ymin=Q1,ymax=Q3),linetype = 1,fill='white') #box
p <- p + geom_errorbar(aes(ymin=mean-sd,ymax=mean+sd),linetype = 3,width = 0.25) #sd range
p <- p + geom_point() # mean
# drawning outliers if any
if(length(out) >0) p <- p + geom_point(data=out,aes(x=x,y=y),shape=4)
p <- p + scale_x_discrete(name= "Group")
p <- p + scale_y_continuous(name= "Age")
p + opts(panel.background = theme_rect(fill = "white",colour = NA))


The result is:

and the same data with smaller range (boxplot coef = 0.5)

• Looks nice. How about outliers?! Your whiskers show the min/max, but in case there're some outliers they should show Q1/Q3 ± 1.5 x IQR, instead (at least, for a Tukey bxp). You can get outlying values (according to this definition) by using boxplot.stats (see the vector named out), as well as the five numbers btw, and add them with a geom_point. Otherwise, +1.
– chl
Mar 13, 2011 at 15:51
• @chl Thank you for noting! Added outliers and correction of interval. Code maybe is not so beautiful, but works well. Mar 13, 2011 at 19:24
• (+1) I didn't manage to do better myself (i.e., alternating plotting layers -- not very pretty, but works the way we want)
– chl
Mar 13, 2011 at 20:03

There is a simpler solution using stat_boxplot(geom ='errorbar')

I provide an example:

bp <- ggplot(iris, aes(factor(Species), Sepal.Width, fill = Species))
bp + geom_boxplot() + stat_boxplot(geom ='errorbar')


Result:

I think that it looks better if stat_boxplot(geom ='errorbar') on the first line as it hides the vertical line.

    bp <- ggplot(iris, aes(factor(Species), Sepal.Width, fill = Species))+     stat_boxplot(geom ='errorbar')
bp + geom_boxplot()


• Not sure why this is here rather than SO
– FTF
Nov 17, 2015 at 3:46