16

I have some data with standard errors associated and would like to display these with error bars. That's what I have:

# generate some data
hod <- data.frame(h = c(1:24,1:24,1:24), mean = 1:(24*3) + runif(24*3, 0, 5),ci = runif(24*3, 0, 2), t = c(rep("a",24),rep("b",24),rep("c",24)))

pd <- position_dodge(0.3)
  dayplot <- ggplot(hod, aes(x=h, y=mean, colour=as.factor(t),group=as.factor(t))) + 
    geom_line(position=pd, size=1) +
    geom_errorbar(aes(ymin=mean-ci, ymax=mean+ci),
                  width=1,
                  size=0.5,
                  position=pd) +
    geom_point(position=pd, shape=21, size=1, fill="white") +
    scale_x_continuous(limits=c(-0.5,23.5),
                       breaks=c(0:8*3),
                       labels=ifelse(
                              c(0:8*3) < 10,
                              paste('0',c(0:8*3),':00',sep=''),
                              paste(c(0:8*3),':00',sep='')
                              )
                       ) +
    xlab("Hour of day") + ylab(ylabel) + labs(title = varlabels[var]) +
    theme_minimal() + 
    theme(plot.margin = unit(c(1,0,1,1), "cm"), 
          axis.title.x = element_text(vjust=-1),
          axis.title.y = element_text(angle=90, vjust=0),
          legend.margin = unit(c(0), "cm"),
          legend.key.height = unit(c(0.9), "cm"),
          panel.grid.major = element_line(colour=rgb(0.87,0.87,0.87)),
          panel.grid.minor = element_blank(),
          plot.background = element_rect(fill = rgb(0.97,0.97,0.97), linetype=0)
    )

The only thing of interest is probably:

geom_errorbar(aes(ymin=mean-ci, ymax=mean+ci),
                      width=1,
                      size=0.5,
                      position=pd)

It gives: all

Now when I group the data by a factor variable (as.factor(t)), I get several lines instead of one, which is what I want, BUT, as you can see, the horizontal lines at the error bars are more narrow, and I can't figure out why. I tried changing and even taking away the width and size attribute of geom_errorbar, but nothing happens. Is there a way to have the same width of the horizontal lines for every chart, no matter the data? I mean, why should it vary? Or does that width convey some information?

enter image description here

YBS
  • 19,324
  • 2
  • 9
  • 27
grssnbchr
  • 2,877
  • 7
  • 37
  • 71
  • 1
    I think it would already help if somebody could explain to me the use and function of `width` and `size` for `geom_errorbar`, as it should be data- and thus problem-independent. – grssnbchr Oct 17 '13 at 09:11
  • I tried to reproduce the problem with a simplified example and couldn't. It's not obvious, what is going wrong. If you provide data it's much easier to find the problem. – Roland Oct 17 '13 at 09:57
  • No. But I haven't provided @Roland with any data. – grssnbchr Jan 13 '14 at 16:21

2 Answers2

18

Below is a reproducible example using random data. The fix to the problem is to multiply the width by the number of classes/factors that you have. In the plot below, since I used three factors, using a width of 3 fixes the problem. ggplot2 seems to calculate the relative width by the number of data points in your dataset, rather than the numeric values on the x-axis. This is (IMO) a bug.

library(ggplot2)
library(grid)

#plot with factors
hod <- data.frame(h = c(1:24,1:24,1:24), mean = 1:(24*3) + runif(24*3, 0, 5),ci = runif(24*3, 0, 2), t = c(rep("a",24),rep("b",24),rep("c",24)))
pd <- position_dodge(0.3)
  dayplot <- ggplot(hod, aes(x=h, y=mean, colour=as.factor(t),group=as.factor(t))) + 

    geom_line(position=pd, size=1) +
    geom_errorbar(aes(ymin=mean-ci, ymax=mean+ci),
                  width=1,
                  size=0.5,
                  position=pd) +
    geom_point(position=pd, shape=21, size=1, fill="white") +
    scale_x_continuous(limits=c(-0.5,23.5),
                       breaks=c(0:8*3),
                       labels=ifelse(
                              c(0:8*3) < 10,
                              paste('0',c(0:8*3),':00',sep=''),
                              paste(c(0:8*3),':00',sep='')
                              )
                       ) +
    xlab("Hour of day") +
    theme_minimal() + 
    theme(plot.margin = unit(c(1,0,1,1), "cm"), 
          axis.title.x = element_text(vjust=-1),
          axis.title.y = element_text(angle=90, vjust=0),
          legend.margin = unit(c(0), "cm"),
          legend.key.height = unit(c(0.9), "cm"),
          panel.grid.major = element_line(colour=rgb(0.87,0.87,0.87)),
          panel.grid.minor = element_blank(),
          plot.background = element_rect(fill = rgb(0.97,0.97,0.97), linetype=0)
    )
print(dayplot)


#plot without factors
hod <- data.frame(h = c(1:24,1:24,1:24), mean = 1:(24) + runif(24, 0, 5),ci = runif(24, 0, 2))
pd <- position_dodge(0.3)
  dayplot <- ggplot(hod, aes(x=h, y=mean)) + 

    geom_line(position=pd, size=1) +
    geom_errorbar(aes(ymin=mean-ci, ymax=mean+ci),
                  width=1,
                  size=0.5,
                  position=pd) +
    geom_point(position=pd, shape=21, size=1, fill="white") +
    scale_x_continuous(limits=c(-0.5,23.5),
                       breaks=c(0:8*3),
                       labels=ifelse(
                              c(0:8*3) < 10,
                              paste('0',c(0:8*3),':00',sep=''),
                              paste(c(0:8*3),':00',sep='')
                              )
                       ) +
    xlab("Hour of day") +
    theme_minimal() + 
    theme(plot.margin = unit(c(1,0,1,1), "cm"), 
          axis.title.x = element_text(vjust=-1),
          axis.title.y = element_text(angle=90, vjust=0),
          legend.margin = unit(c(0), "cm"),
          legend.key.height = unit(c(0.9), "cm"),
          panel.grid.major = element_line(colour=rgb(0.87,0.87,0.87)),
          panel.grid.minor = element_blank(),
          plot.background = element_rect(fill = rgb(0.97,0.97,0.97), linetype=0)
    )

print(dayplot)
thc
  • 9,527
  • 1
  • 24
  • 39
  • Now, where lies the difference to my example? You're still using `width = 1`, as far as I can see. Do you mean you should use `width = 3`? – grssnbchr Mar 16 '15 at 08:51
  • Yes. Try your own example with width = 9 (or however many levels you have). – thc Mar 17 '15 at 02:43
  • 2
    Thanks. I posted an issue on GitHub https://github.com/hadley/ggplot2/issues/1068 – grssnbchr Mar 17 '15 at 07:50
  • 1
    This issue seems to remain unresolved for many of us for whom the standard in the field is NOT to scale errorbar width by number of elements. I know that ggplot developers think this is a feature not a bug, but our colleagues/journals beg to differ. @hadley, help? thanks! – curious lab rat Jan 12 '21 at 13:44
  • @curiouslabrat It's definitely a bug. Check out the github issue by grssnbchr and maybe re-open with a simple rep. ex. – thc Jan 12 '21 at 20:01
  • I am still dealing with this right now. Very frustrating. – Mark Zurbrügg Jun 24 '21 at 15:20
  • @MarkZurbrügg I would repost the issue on github. The previous one by gssnbchr was closed due to inactivity without a fix. – thc Jun 24 '21 at 16:42
3

I have managed to solve a similar issue. In my case I wanted to set both horizontal and vertical errorbar heads to the same size - regardless of the aspect ratio of the plot.

Based on the original posted code:

f <- ggplot_build(dayplot)

f$plot$layers[[5]]$geom_params$width  <- 0.02 * diff(f$layout$panel_params[[1]]$x.range)
f$plot$layers[[6]]$geom_params$height <- 0.02 * diff(f$layout$panel_params[[1]]$y.range)

dayplot <- f$plot

This will set the errorbar head to 2% of the axis range. Maybe could solve your issue.

cfnerd
  • 3,658
  • 12
  • 32
  • 44
ThiagoMAF
  • 41
  • 1