33

I have a dataset where measurements are made for different groups at different days.

I want to have side by side bars representing the measurements at the different days for the different groups with the groups of bars spaced according to day of measurement with errorbars overlaid to them.

I'm having trouble with making the dodging in geom_bar agree with the dodge on geom_errorbar.

Here is a simple piece of code:

days          = data.frame(day=c(0,1,8,15));
groups        = data.frame(group=c("A","B","C","D", "E"), means=seq(0,1,length=5));


my_data       = merge(days, groups);


my_data$mid   = exp(my_data$means+rnorm(nrow(my_data), sd=0.25));
my_data$sigma = 0.1;


png(file="bar_and_errors_example.png", height=900, width=1200);
plot(ggplot(my_data, aes(x=day, weight=mid, ymin=mid-sigma, ymax=mid+sigma, fill=group)) +
     geom_bar      (position=position_dodge(width=0.5))                                   +
     geom_errorbar (position=position_dodge(width=0.5), colour="black")                   +
     geom_point    (position=position_dodge(width=0.5), aes(y=mid, colour=group)));
dev.off();

In the plot, the errorsegments appears with a fixed offset from its bar (sorry, no plots allowed for newbies even if ggplot2 is the subject).

When binwidth is adjusted in geom_bar, the offset is not fixed and changes from day to day.

Notice, that geom_errorbar and geom_point dodge in tandem. How do I get geom_bar to agree with the other two?

Any help appreciated.

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
user1771185
  • 331
  • 1
  • 3
  • 3

4 Answers4

28

The alignment problems are due, in part, to your bars not representing the data you intend. The following lines up correctly:

ggplot(my_data, aes(x=day, weight=mid, ymin=mid-sigma, ymax=mid+sigma, fill=group)) +
     geom_bar      (position=position_dodge(), aes(y=mid), stat="identity") +
     geom_errorbar (position=position_dodge(width=0.9), colour="black") +
     geom_point    (position=position_dodge(width=0.9), aes(y=mid, colour=group))

enter image description here

Brian Diggs
  • 57,757
  • 13
  • 166
  • 188
  • it doesn't find position.dodge(), is this been replaced by position='dodge'? I have a similar problem and have used your solution but unless I set width of the error bars close to 1 the error bars don't line up with the geom_bars. – Herman Toothrot Apr 26 '18 at 15:38
  • @HermanToothrot I just checked and `position_dodge` is a function in `ggplot2` version 2.2.1 (which is the most current version as of writing this comment). I would point out that the separator in the function name is an underscore (`_`), not a dot (`.`). That may be why your code did not find it. – Brian Diggs May 02 '18 at 21:43
  • Thanks Brian, I guess what I am wondering is what is the difference between position=position_dodge() and position="dodge"? – Herman Toothrot May 03 '18 at 11:00
  • @HermanToothrot I don't think there is any difference, but I can't say that for sure. – Brian Diggs May 18 '18 at 19:44
15

This is an old question, but since i ran into the problem today, i want to add the following:

In

 geom_bar(position = position_dodge(width=0.9), stat = "identity") + 
   geom_errorbar( position = position_dodge(width=0.9), colour="black") 

the width-argument within position_dodge controls the dodging width of the things to dodge from each other. However, this produces whiskers as wide as the bars, which is possibly undesired. An additional width-argument outside the position_dodge controls the width of the whiskers (and bars):

 geom_bar(position = position_dodge(width=0.9), stat = "identity", width=0.7) + 
   geom_errorbar( position = position_dodge(width=0.9), colour="black", width=0.3) 
nouse
  • 3,315
  • 2
  • 29
  • 56
  • 2
    I have noticed this problem but using width changes the position of the whiskers and now they are not lined up anymore with the bars – Herman Toothrot Apr 26 '18 at 15:46
5

sometimes you put aes(x=tasks,y=val,fill=group) in geom_bar rather than ggplot. This causes the problem since ggplot looks forward x and you specify it by the location of each group.

4

The first change I reformatted the code according to the advanced R style guide.

days <- data.frame(day=c(0,1,8,15))

groups <- data.frame(
    group=c("A","B","C","D", "E"), 
    means=seq(0,1,length=5)
    )

my_data <- merge(days, groups)

my_data$mid <- exp(my_data$means+rnorm(nrow(my_data), sd=0.25))
my_data$sigma <- 0.1

Now when we look at the data we see that day is a factor and everything else is the same.

str(my_data)

To remove blank space from the plot I converted the day column to factors. CHECK that the levels are in the proper order before proceeding.

my_data$day <- as.factor(my_data$day) 
levels(my_data$day)

The next change I made was defining y in your aes arguments. As I'm sure you are aware, this lets ggplot know where to look for y values. Then I changed the position argument to "dodge" and added the stat="identity" argument. The "identity" argument tells ggplot to plot y at x. geom_errorbar inherits the dodge position from geom_bar so you can leave it unspecified, but geom_point does not so you must specify that value. The default dodge is position_dodge(.9).

ggplot(data = my_data, 
 aes(x=day,
     y= mid, 
     ymin=mid-sigma, 
     ymax=mid+sigma,       
     fill=group)) +
   geom_bar(position="dodge", stat = "identity") + 
   geom_errorbar( position = position_dodge(), colour="black") +
   geom_point(position=position_dodge(.9), aes(y=mid, colour=group))

enter image description here

Collin
  • 440
  • 1
  • 4
  • 11