Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ggplot2 position_dodge affects error bar width

Tags:

r

ggplot2

I am trying to make a line and point graph with errorbars. It has different factors, however some factors have only one value. I found out that if i use position_dodge, one of the single value factor has a much wider error bar compared to the other error bars in the graphs. Somehow position_dodge has an influence on the width on the error bar. I did not found anyone that had the same problem before so I hope that someone can help me.

The dummy data:

require(ggplot2)

x <- c(1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,3,3,5)
y <- c(3,5,6,3,5,3,5,6,2,6,3,7,3,6,2,1,5,8,7)
se <- x*0.2
treatment <- c("A", "B","C", "D","A", "B","C", "D","A", "B","C", "D","A",    "B","C", "D","E", "F", "G" )
data <- data.frame(x, y, se ,treatment)
data$treatment <- as.factor(data$treatment)

First a plot without position_dodge - everything is fine

# Without position dodge
myplot <- ggplot(data, aes(x=x, y=y, group= treatment,  fill = treatment, colour = treatment)) +
  geom_line(stat="identity", size = 1) +
  geom_point(stat="identity", size = 3, shape = 21) + 
  geom_errorbar(aes(ymin = y-se, ymax = y+se), width = 0.2)

myplot

plot without position_dodge

Now a plot with position dodge:

# With position dodge
myplot <- ggplot(data, aes(x=x, y=y, group= treatment,  fill = treatment, colour = treatment)) +
  geom_line(stat="identity", size = 1, position=position_dodge(width=0.2)) +
  geom_point(stat="identity", size = 3, shape = 21, position=position_dodge(width=0.2)) + 
  geom_errorbar(aes(ymin = y-se, ymax = y+se), width = 0.2, position=position_dodge(width=0.2))

myplot

Plot with position_dodge

As you can see the error bar on the far right has a much larger width compared to the other error bars. This is probably because there are no overlapping x variables for this point, and than the error bars can have a normal size. I still would like to know how I can get the error bars to have the same width.

like image 328
Marinka Avatar asked Mar 10 '17 10:03

Marinka


2 Answers

As @aosmith suggests, the fix for this is to scale the width of the error bar to the number of points with that x. However, this does not need to be done manually. Below I use dplyr to create a new column in the data.frame based on the number of points at that x. I've also removed the group and fill mappings since neither is needed here (provided the shape is changed to the version of a filled circle coloured by colour rather than fill). Finally, to avoid repetition I've defined the position once and then used a variable for each geom.

library(dplyr)
data <- data %>%
  group_by(x) %>%
  mutate(
    width = 0.1 * n()
  )

pos <- position_dodge(width = 0.2)
myplot <-
  ggplot(data,
         aes(
           x = x,
           y = y,
           colour = treatment,
           width = width
         )) +
  geom_line(size = 1, position = pos) +
  geom_point(size = 3, shape = 16, position = pos) +
  geom_errorbar(aes(ymin = y - se, ymax = y + se), position = pos)

myplot

Final image

like image 104
Nick Kennedy Avatar answered Oct 12 '22 08:10

Nick Kennedy


A somewhat awkward work-around I've used in the past is to manually set the width of each error bar by using width inside of aes. The number of values within each group tells you how much to scale each error bar.

For example, if I have a group with a single value and a group with 3 values, the group of 3 width must be 3 times larger than the group of 1 width.

Your case is much more complicated, as you have a group of 1, a group of 6, and 3 groups of 4. You could start by figuring out what a good width is for the single group. I chose .1. So the group of 6 needs to be .6 wide and the group of 4 needs to be .4 wide.

Then the trick is figuring out the order the lines are drawn for getting the widths. This is easier for simpler situations.

Plot code:

ggplot(data, aes(x = x, y = y, group = treatment,  fill = treatment, colour = treatment)) +
    geom_line(stat = "identity", size = 1, position = position_dodge(width = 0.2)) +
    geom_point(stat = "identity", size = 3, shape = 21, position = position_dodge(width = 0.2)) + 
    geom_errorbar(aes(ymin = y-se, ymax = y+se, 
                   width = c(rep(.4, 8), rep(.6, 4), rep(.4, 4), .6, .6, .1)), 
                position = position_dodge(width = 0.2))

enter image description here

like image 28
aosmith Avatar answered Oct 12 '22 07:10

aosmith