2

So I'm making a group bar chart about the prevalence of pre-existing conditions among age groups. As expected, younger people do not have as many conditions as older people, so there are some groups that are 0.

PreEx <- c("Cardiovascular Disease", "Lung Disease", "Diabetes", "Hypertension", 
           "Renal Disease", "Autoimmune/Endocrine/Metabolic Disease", 
           "Neurological/Psychiatric Illness", "Other Disease")

PreBar <- data.frame(Freq=c(0 ,0 , 0, 0, 0, 0, 0, 9.1, 0, 3.4, 0, 0, 0, 0, 1.7, 
                            0, 3.4, 4.3, 3.9, 3, 0.8, 1.4, 1.3, 1.6, 14.2, 3.1, 
                            17, 8, 1.7, 4.5, 1.7, 2.8, 30.6, 9.9, 25.2, 13.5, 
                            4.5, 6.3, 1.8, 3.6),
                     Age=c(rep("0-4 yrs", 8), rep("5-17 yrs", 8), rep("18-49 yrs", 8), 
                           rep("50-64 yrs", 8), rep("65+ yrs", 8)), 
                     PreCond= rep(PreEx, 5))

The issue is, the bar chart keeps their placement as if the values that are 0 are there. This makes the chart looks like the bars are not centered. I've attached an image of what the chart currently looks like.

enter image description here

I also cannot remove the zero values because then the bars are not equivalent widths.

enter image description here

Here is what the plot code looks like (I did take out the specific color scheme I am using).

library(ggplot2)
ggplot(data=PreBar, aes(x=Age, y=Freq, fill=PreCond)) +
  geom_bar(position="dodge", stat="identity") +
  theme_light() +
  ylab("Percentage of Pre-existing Condition Among Positives") +
  xlab("Age Category") 

What would be the easiest way to center each grouped bar set while still keeping all the bar widths the same?

Z.Lin
  • 28,055
  • 6
  • 54
  • 94
OKLM
  • 85
  • 7
  • 2
    Please do not post an image of code/data/errors: it cannot be copied or searched (SEO), it breaks screen-readers, and it may not fit well on some mobile devices. Ref: https://meta.stackoverflow.com/a/285557 (and https://xkcd.com/2116/). Please just include the code, console output, or data (e.g., `dput(head(x))` or `data.frame(...)`) directly. – r2evans Jul 24 '20 at 00:21
  • 2
    Please make this question *reproducible*. This includes sample code you've attempted (including listing non-base R packages, and any errors/warnings received), sample *unambiguous* data (e.g., `dput(head(x))` or `data.frame(x=...,y=...)`), and intended output given that input. Refs: https://stackoverflow.com/q/5963269, [mcve], and https://stackoverflow.com/tags/r/info. – r2evans Jul 24 '20 at 00:21
  • The first graph is correct. The bars are not supposed to be centred - each category should be in the same position across the different age groups. Centring them or moving them to another position will confuse readers. – Edward Jul 24 '20 at 01:09

2 Answers2

2

You can achieve the effect using facets with free scales. (I'm not saying the effect is necessarily a good idea, as other comments have pointed out, but it can be done.)

ggplot(subset(PreBar, Freq > 0),
       aes(x = PreCond, y = Freq, fill = PreCond)) +
  geom_col() +
  facet_grid(~Age, space = "free_x", scales = "free_x", switch = "x") +
  theme_light() +
  theme(axis.text.x = element_blank(), 
        axis.ticks.x = element_blank(),
        panel.spacing = unit(0, "pt"),
        panel.border = element_blank(),
        panel.grid.major.x = element_blank(),
        strip.background = element_blank(),
        strip.text = element_text(colour = "black"))  +
  scale_x_discrete(name = "Age Category",
                   expand = c(0, 1)) +
  ylab("Percentage of Pre-existing Condition Among Positives") +
  scale_fill_brewer(palette = "RdYlBu")

plot

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

The simplest solution is to use the preserve= argument of position_dodge2(). When you remove your "0" values, as you observed, the bars are not equivalent widths. This is the principle behind the preserve= argument of position_dodge2(), which is to say, should the widths of the bars be preserved across each x value("total") or preserve the width of all bars within all x values ("single")? The second is what you want here.

What's the difference between position_dodge() and position_dodge2()? Well, position_dodge() works, but does not center the groupings on the x value (your original problem). position_dodge2() does just that:

PreBar <- PreBar[which(PreBar$Freq!=0),]  # remove your zeros

library(ggplot2)
ggplot(data=PreBar, aes(x=Age, y=Freq, fill=PreCond)) +
  geom_bar(position=position_dodge2(preserve='single'), stat="identity") +
  theme_light() +
  ylab("Percentage of Pre-existing Condition Among Positives") +
  xlab("Age Category") 

enter image description here

chemdork123
  • 12,369
  • 2
  • 16
  • 32