6

Consider some sample data where a level does not occur:

dat <- data.frame(sex = c("F","M","F"),
                  status = c("Pregnant","Not pregnant","Not pregnant"),
                  frequency = c(25,100,75))

In the sample data males cannot become pregnant so they have no data.

I would like to plot the data using ggplot2 in R using this code:

library(ggplot2)

p <- ggplot(dat,aes(x=status,y=frequency,fill=sex))+
  geom_bar(stat = "identity",position="dodge")

print(p)

I get this output:

enter image description here

As you can see, the bar for female and pregnant is wider than the other bars. My question is how can I suppress the bar widening to have the bars the same width?

  • 4
    If your sample data is representative, is it feasible to just add data entries with frequencies of 0? This would keep the relative column placing to the xlabel. For example, if your data set was `dat <- data.frame(sex = c("F","M","F","M"), status = c("Pregnant","Not pregnant","Not pregnant","Pregnant"), frequency = c(25,100,75,0))` then your plotting code works as intended. – Michael Bird Aug 11 '17 at 10:41
  • 1
    Possible duplicate of [The same width of the bars in geom\_bar(position = "dodge")](https://stackoverflow.com/questions/38101512/the-same-width-of-the-bars-in-geom-barposition-dodge) – divibisan Jun 18 '18 at 20:35

2 Answers2

6

# By default, dodging preserves the total width. You can choose

# to preserve the width of each element:

ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
  geom_bar(position = position_dodge(preserve = "single"))

enter image description here

From the documentation of position_dodge: http://ggplot2.tidyverse.org/reference/position_dodge.html

But I particularly like this solution: https://stackoverflow.com/a/38103511/3330437

Community
  • 1
  • 1
Brian
  • 7,900
  • 1
  • 27
  • 41
2

Adding on to Michael Bird's comment above, adding zero frequency rows to your dataframe will resolve the issue.

It may be tedious to do this manually, so my preferred workaround is to use spread followed by gather from the tidyr library:

ggplot(dat %>% 
         tidyr::spread(sex, frequency, fill = 0) %>% 
         tidyr::gather(sex, frequency, -status),
       aes(x=status,y=frequency,fill=sex))+
  geom_bar(stat = "identity", position = "dodge")

This will add an additional row with zero frequency for every combination of sex and status.

Z.Lin
  • 28,055
  • 6
  • 54
  • 94