Error bars for barplot only in one direction
It's actually pretty easy to implement this for use in the general case (where you can't hide the lower error bar under another plot element) now that they've made it easy to extend ggplot2
with custom geom
s.
Copy the code from geom_errorbar
from the github repository for ggplot2
into a new .R file. Then make a few modifications, as follows:
geom_uperrorbar <- function(mapping = NULL, data = NULL,
stat = "identity", position = "identity",
...,
na.rm = FALSE,
show.legend = NA,
inherit.aes = TRUE) {
layer(
data = data,
mapping = mapping,
stat = stat,
geom = GeomUperrorbar,
position = position,
show.legend = show.legend,
inherit.aes = inherit.aes,
params = list(
na.rm = na.rm,
...
)
)
}
The only two changes necessary above were to change geom_errorbar
to geom_uperrorbar
and geom = GeomErrorbar
to geom = GeomUperrorbar
.
GeomUperrorbar <- ggproto("GeomUperrorbar", Geom,
default_aes = aes(colour = "black", size = 0.5, linetype = 1, width = 0.5,
alpha = NA),
draw_key = draw_key_path,
Note that this comment is interrupting partway through a function which continues in the code blocks below. Above we just changed GeomErrorbar
to GeomUperrorbar
twice.
required_aes = c("x", "y", "ymax"),
setup_data = function(data, params) {
data$width <- data$width %||%
params$width %||% (resolution(data$x, FALSE) * 0.9)
transform(data,
xmin = x - width / 2, xmax = x + width / 2, width = NULL
)
},
Interrupting the function again. Above we changed the required aesthetics to be x
, y
and ymax
, i.e. replacing ymin
with y
. We need y
to start the vertical line there (instead of at ymin
) and we no longer need ymin
because there's not going to be a horizontal line there.
draw_panel = function(data, panel_scales, coord, width = NULL) {
GeomPath$draw_panel(data.frame(
x = as.vector(rbind(data$xmin, data$xmax, NA, data$x, data$x)),
y = as.vector(rbind(data$ymax, data$ymax, NA, data$ymax, data$y)),
colour = rep(data$colour, each = 5),
alpha = rep(data$alpha, each = 5),
size = rep(data$size, each = 5),
linetype = rep(data$linetype, each = 5),
group = rep(1:(nrow(data)), each = 5),
stringsAsFactors = FALSE,
row.names = 1:(nrow(data) * 5)
), panel_scales, coord)
}
)
Here we removed the last three elements of the vectors passed to x
and y
, which were for the lower error bar. In addition, we changed the last element from ymin
to y
, because we want the line to start at y
, not ymin
.
"%||%" <- function(a, b) {
if (!is.null(a)) a else b
}
This last bit is just a convenience function used in the code that needs to be defined.
If you source the document including all of this code, then you can use geom_uperrorbar
just like geom_errorbar
, or even pass geom = "uperrorbar"
to stat_summary
, using y
instead of ymin
.
An easy work-around would be to plot the error bars first:
p +
geom_errorbar(limits, position = dodge, width=0.25) +
geom_bar(position = dodge, stat = "identity")
Henrik's advice is excellent in this case, but I would suggest that you take a look at where the upper and lower limits are set.
limits <- aes(ymax = resp + se, ymin = resp - se)
In that line, you explicitly tell ggplot to put the lower extension by setting ymin as resp - se
; if you just set it as resp
, then you'd have only the upper extension.
limits <- aes(ymax = resp + se, ymin = resp)
... although you'd also have a black line at the top of the bar. To make this look cleaner, you could add a black outline to the entire bar.
p <- ggplot(df, aes(fill = group, y = resp, x = trt))+
geom_bar(position = dodge, stat = "identity") +
geom_bar(position = dodge, stat = "identity",
color="black", show_guide=FALSE)+
geom_errorbar(limits, position = dodge, width = 0.25)
p
Notice how I duplicated the bar plot layer, but with a "black" color, which adds that outline. The legend was turned off in the colored layer because I personally prefer to avoid the diagonal lines in the legend.