0

This very useful post shows how to display error bars in one direction only with ggplot. To do this, the geom_errorbar function is modified as follows (as proposed by Sean Hughes):

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,
         ...
      )
   )
}

GeomUperrorbar <- ggproto("GeomUperrorbar", Geom,
   default_aes = aes(colour = "black", size = 0.5, linetype = 1, width = 0.5,
      alpha = NA),

   draw_key = draw_key_path,
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
      )
   },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)
   }
)


"%||%" <- function(a, b) {
   if (!is.null(a)) a else b
} 

But now I have the case that the last bar is negative. Thus, the error bar at the last bar must head in the other direction. Does anyone have an idea how this could work?

Here is a reproducible example:

df <- data.frame(trt = factor(c(1, 1, 2, 2)), resp = c(1, 5, 3, -2),
                 group = factor(c(1, 2, 1, 2)), se = c(0.1, 0.3, 0.3, 0.2))
df2 <- df[c(1,3), ]

limits <- aes(ymax = resp + se, ymin = resp - se)
dodge <- position_dodge(width = 0.9)

p <- ggplot(df, aes(fill = group, y = resp, x = trt))
p + geom_bar(position = dodge, stat = "identity") +
  geom_errorbar(limits, position = dodge, width = 0.25)

enter image description here

バシル
  • 35
  • 4

1 Answers1

1

The solution in the linked question will actually produce this result. You just need to make sure that the standard error is subtracted instead of added when the bar points down:

limits <- aes(ymax = resp + se * sign(resp))

Now your plotting code is pretty much unaltered:

dodge  <- position_dodge(width = 0.9)

ggplot(df, aes(fill = group, y = resp, x = trt)) +
  geom_bar(position = dodge, stat = "identity") +
  geom_uperrorbar(limits, position = dodge, width = 0.25)

enter image description here

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87