Error bars from geom_errorbar have wrong horizontal placement

I have something like this and error bars are out of whack. This same code snippet has worked for me before.

ggplot(aes(date, probability, fill = type)) +
  geom_col(position = 'dodge', colour = 'black') +
  geom_errorbar(aes(ymin=conf.low, ymax=conf.high), 
                size=.5, width=.2, colour = 'black',
                position = position_dodge(0.9))

11%20am

Here is what I get from your call to ggplot() with some data I invented and modified variable names, due to the invented data. Can you post an example showing your data?

library(ggplot2)

DF <- data.frame(Date = c("A", "A", "B", "B", "C", "C"),
                 Type = c("X", "Y", "X", "Y", "X", "Y"),
                 Value = c(6, 9, 8, 4, 6, 7),
                 conf.low = c(5,8,7,3,5,6),
                 conf.high = c(7,10, 9,5,7,8))
ggplot(DF, aes(Date, Value, fill = Type)) +
  geom_col(position = 'dodge', colour = 'black') +
  geom_errorbar(aes(ymin=conf.low, ymax=conf.high), 
                size=.5, width=.2, colour = 'black',
                position = position_dodge(0.9))

Created on 2019-10-10 by the reprex package (v0.3.0.9000)

1 Like

Thanks for that. I ran your toy examples and works fine for me too.

I unfortunately can't share the data I'm working with but the data types are as follows:

DF <- tibble(Date = c(ymd('2019-01-01'), ymd('2019-01-02'), ymd('2019-01-01'), ymd('2019-01-02'), ymd('2019-01-01'), ymd('2019-01-02')),
             Type = c("A", "A", "B", "B", "C", "C"),
             Value = c(6, 9, 8, 4, 6, 7),
             conf.low = c(5,8,7,3,5,6),
             conf.high = c(7,10, 9,5,7,8))
  
ggplot(DF, aes(Date, Value, fill = Type)) +
  geom_col(position = 'dodge', colour = 'black') +
  geom_errorbar(aes(ymin=conf.low, ymax=conf.high), 
                size=.5, width=.2, colour = 'black',
                position = position_dodge(0.9))

This works fine too!

image

What in the data could be causing this issue?

The date format seems to be the difference. Can you reproduce this?

Here as strings is fine.

DF <- tibble(Date = c("2018-06-01", "2018-07-01", "2018-08-01", "2018-09-01", "2018-06-01", "2018-07-01",  "2018-08-01", "2018-09-01"),
             Type = c("A", "A", "A", "A", "B", "B", "B", "B"),
             Value = c(6, 9, 8, 4, 6, 7, 5, 6),
             conf.low = c(5,8,7,3,5,6, 3, 5),
             conf.high = c(7,10, 9,5,7,8, 7, 8))
  
ggplot(DF, aes(Date, Value, fill = Type)) +
  geom_col(position = 'dodge', colour = 'black') +
  geom_errorbar(aes(ymin=conf.low, ymax=conf.high), 
                size=.5, width=.2, colour = 'black',
                position = position_dodge(0.9))

As dates,

DF <- tibble(Date = ymd("2018-06-01", "2018-07-01", "2018-08-01", "2018-09-01", "2018-06-01", "2018-07-01",  "2018-08-01", "2018-09-01"),
             Type = c("A", "A", "A", "A", "B", "B", "B", "B"),
             Value = c(6, 9, 8, 4, 6, 7, 5, 6),
             conf.low = c(5,8,7,3,5,6, 3, 5),
             conf.high = c(7,10, 9,5,7,8, 7, 8))
  
ggplot(DF, aes(Date, Value, fill = Type)) +
  geom_col(position = 'dodge', colour = 'black') +
  geom_errorbar(aes(ymin=conf.low, ymax=conf.high), 
                size=.5, width=.2, colour = 'black',
                position = position_dodge(0.9))

image

Seriously weird!

By having the x axis using real dates, the scaling of dodge in the error bars is not matching the distance between the tick marks. There are 30 units between June 1 and July 1 when the x axis is numeric. When the x axis comes from a factor (character) there is only one unit from June 1 to July 1. Look what happens when I make a dedicated column for the position of the error bars. I shift them by 7 days and they line up with the centers of the columns in the graph, more or less.

library(ggplot2)
library(tibble)
library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following object is masked from 'package:base':
#> 
#>     date

DF <- tibble(Date = ymd("2018-06-01", "2018-07-01", "2018-08-01", "2018-09-01", "2018-06-01", "2018-07-01",  "2018-08-01", "2018-09-01"),
             Type = c("A", "A", "A", "A", "B", "B", "B", "B"),
             Value = c(6, 9, 8, 4, 6, 7, 5, 6),
             conf.low = c(5,8,7,3,5,6, 3, 5),
             conf.high = c(7,10, 9,5,7,8, 7, 8),
             ErrorDate = ymd("2018-05-25", "2018-06-25", "2018-07-25", "2018-08-25", "2018-06-07", "2018-07-07",  "2018-08-07", "2018-09-07"))
#> Warning: `list_len()` is deprecated as of rlang 0.2.0.
#> Please use `new_list()` instead.
#> This warning is displayed once per session.

ggplot(DF, aes(Date, Value, fill = Type)) +
  geom_col(position = 'dodge', colour = 'black') +
  geom_errorbar(aes(x = ErrorDate, ymin=conf.low, ymax=conf.high), 
                size=.5, width=.2, colour = 'black')

Created on 2019-10-10 by the reprex package (v0.3.0.9000)

2 Likes

I think you can use dodging with real dates as long as you use the same dodge amount in geom_errorbar and geom_col. For example, in the following d sets the amount of dodging using 30.5 as the baseline width (the (more or less) average distance between months) and the factor of 0.9, applied to both the dodging and the width argument, gives the default bar widths. I've used FJCC's second data frame for my examples:

d = 30.5*0.9
ggplot(DF, aes(Date, Value, fill = Type)) +
  geom_col(position = position_dodge(d), colour = 'black', width=d) +
  geom_errorbar(aes(ymin=conf.low, ymax=conf.high), 
                size=.5, width=.2,
                position=position_dodge(d))

Rplot02

In the width argument, you can multiply d by any value less than one to create some space between the two bars in each pair (a factor greater than one will cause the bars to overlap).

ggplot(DF, aes(Date, Value, fill = Type)) +
  geom_col(position = position_dodge(d), colour = 'black', width=d*0.9) +
  geom_errorbar(aes(ymin=conf.low, ymax=conf.high), 
                size=.5, width=.2,
                position=position_dodge(d))

Rplot03

And you can also use different values for d to get thinner or fatter bars.

d = 30.5*0.5
ggplot(DF, aes(Date, Value, fill = Type)) +
  geom_col(position = position_dodge(d), colour = 'black', width=d) +
  geom_errorbar(aes(ymin=conf.low, ymax=conf.high), 
                size=.5, width=.2,
                position=position_dodge(d))

Rplot04

Another option that uses a lot less ink is to use points instead of bars. You can do this with a single call to geom_pointrange, but I've used geom_errorbar and geom_point so that the size of the points and the errorbars can be controlled independently:

d = 30.5*0.2
ggplot(DF, aes(Date, Value, colour = Type)) +
  geom_errorbar(aes(ymin=conf.low, ymax=conf.high), 
                size=.5, width=0.1,
                position=position_dodge(d)) +
  geom_point(position = position_dodge(d)) +
  expand_limits(y=0) +
  scale_y_continuous(expand=expand_scale(mult=c(0,0.02))) +
  theme_bw()

Rplot06

2 Likes

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.