1
votes

I am trying to make a weighted dodged bar plot with ggplot2. With stacked bars the behavior is as expected:

df <- data.frame(group = rep(letters[1:3], each = 4), 
    sex = rep(c("m", "f"), times = 6),
    weight = 1:12)
ggplot(df, aes(x = group, fill = sex, y = weight)) +
    geom_bar(stat = "identity")

The bars have length equal to the total weight.

If I add position = "dodge", the length of the female group a bar is 4 rather than the expected 6. Similarly, all other bars are only as long as the highest weight in each group & sex combination rather than representing the total weight.

ggplot(df, aes(x = group, fill = sex, y = weight)) +
    geom_bar(stat = "identity", position = "dodge")

How do I make the bar lengths match the total weight?

2

2 Answers

3
votes

@kath's explanation is correct.

Another alternative, if you don't want to summarise the data frame before passing it to ggplot(): use the stat_summary() function instead of geom_bar():

ggplot(df, aes(x = group, fill = sex, y = weight)) +
  stat_summary(geom = "bar", position = "dodge", fun.y = sum)

plot

2
votes

You can first summarise the data in your desired way and then plot it:

library(dplyr)
library(ggplot2)

df %>% 
  group_by(group, sex) %>% 
  summarise(total_weight = sum(weight)) %>% 
  ggplot(aes(x = group, fill = sex, y = total_weight)) +
  geom_bar(stat = "identity", position = "dodge")

enter image description here

The problem with your original approach is that as you have several values of weight for one group, sex combination and then specify stat="identity", they are plotted on top of each other. This can be visualized:

ggplot(df, aes(x = group, fill = sex, y = weight)) +
  geom_bar(stat = "identity", position = "dodge", color = "black", alpha = 0.5)

enter image description here