2

I was trying to plot error bars on a bar plot in ggplot2 with different number of bars per group. I would like:

  • Fixed width of bars, regardless of number of bars per group
  • Error bars with the same width of whiskers, aligned with the corresponding bars

This should be pretty standard, but I am struggling with the error bars, as the settings in position_dodge() and position_dodge2() did not seem to be as straight-forward as the examples in the geom_crossbar() and the position_dodge documentation.

My closest attempt is:

df <- data.frame(
  mean = 2:8,
  loc = c(rep(1, 4), 2, rep(3, 2)),
  # spcs = c(1:4, 1, 1:2),
  spcs = c(1:4, 1, 2, 4)  # Updated on 29 Dec 2018 in response to @Roman Luštrik's comment
)
ggplot(aes(x = factor(loc), y = mean, fill = factor(spcs)), data = df) + 
  geom_col(position = position_dodge2(preserve = "single")) +
  geom_errorbar(
    aes(ymin = mean - 0.2, ymax = mean + 0.2),
    position = position_dodge(width = 0.9),
    width = 0.2
  )

enter image description here

However, neither the error bars were aligned with the bars (Loc 3), nor did they have the same width of whiskers as I wished (Locs 2 and 3).

I have googled and found a few similar questions here, but not exactly my case. I would therefore appreciate a solution with a bit explanation on why my attempt failed.

ps. I know if I facet_grid() by loc and set scales = "free_x", space = "free_x" I would get a close alternative, but I would rather not use facet here. Thanks!

elarry
  • 521
  • 2
  • 7
  • 20

1 Answers1

2

If I add dodge to error bars, too, I get this:

ggplot(aes(x = factor(loc), y = mean, fill = factor(spcs)), data = df) + 
  geom_col(position = position_dodge(preserve = "single")) +
  geom_errorbar(
    aes(ymin = mean - 0.2, ymax = mean + 0.2),
    position = position_dodge(width = 0.9, preserve = "single"),
    width = 0.2)

enter image description here

EDIT

My guess is that some factor dropping is happening for a combination of factors loc*spcs, but I'm not motivated enough right now to go check it out. In any case, a workaround would be to add missing values for missing factors.

df <- data.frame(mean = 2:8, loc = c(rep(1, 4), 2, rep(3, 2)), spcs = c(1:4, 1, 2, 4))
df <- rbind(df, data.frame(mean = NA, loc = 3, spcs = c(1, 3)))

ggplot(aes(x = factor(loc), y = mean, fill = factor(spcs)), data = df) + 
  geom_col(position = position_dodge(preserve = "single")) +
  geom_errorbar(
    aes(ymin = mean - 0.2, ymax = mean + 0.2),
    position = position_dodge(width = 0.9, preserve = "single"),
    width = 0.2)

enter image description here

Roman Luštrik
  • 69,533
  • 24
  • 154
  • 197
  • Thanks very much for your quick reply! Just for (arguable) aesthetic pleasure, is it possible to centre-justify the groups of bars at their x position, instead of left-justified as shown in your solution? I tried to replace both `position_dodge` with `position_dodge2` in your solution while leaving everything else the same, but the error bars were misaligned again. The documentation only mentioned that "`position_dodge2` is a special case of `position_dodge` for arranging box plots, which can have variable widths." Could you improve the answer and explain the difference? Many thanks again! – elarry Dec 28 '18 at 20:27
  • 1
    @elarry I don't think that's correct. The position of the bars should be comparable between `loc`, I think. If you want something like that, consider using `facet_wrap`. – Roman Luštrik Dec 29 '18 at 13:03
  • OK, thanks for your advice. I accepted it as the answer. Do you know why `position_dodge2` doesn't work in this case? – elarry Dec 29 '18 at 13:38
  • [Update] I'm really sorry, but I've just undone my acceptance, as I tested your solution with a new set of data `df <- data.frame(mean = 2:8, loc = c(rep(1, 4), 2, rep(3, 2)), spcs = c(1:4, 1, 2, 4))`, and the bars in Loc 3 were still left-justified - in other words, they were not "comparable" across `loc` as expected. Actually, if they were really "comparable", there would be a gap between the two bars at Loc 3 for the new data, which is ambiguous. Would you be able to fix it? Thanks in advance. – elarry Dec 29 '18 at 14:07
  • Thank you very much for kindly trying again, Roman Luštrik (somehow the @ didn't work)! As there has been no better solution, I have accepted your answer. Please leave a comment if you ever figure out how to counteract the level dropping, instead of manually setting `NA` to dropped levels. – elarry Jan 11 '19 at 20:55