Search code examples
rggplot2dplyrposition

Back-to-back bar plot with ggplot2 in R


I am trying to obtain a back-to-back bar plot (or pyramid plot) similar to the ones shown here: Population pyramid with gender and comparing across two time periods with ggplot2 Basically, a pyramid plot of a quantitative variable whose values have to be displayed for combinations of three categorical variables.

library(ggplot2)
library(dplyr)
df <- data.frame(Gender = rep(c("M", "F"), each = 20),
                 Age = rep(c("0-10", "11-20", "21-30", "31-40", "41-50",
                             "51-60", "61-70", "71-80", "81-90", "91-100"), 4),
                 Year = factor(rep(c(2009, 2010, 2009, 2010), each=  10)),
                 Value = sample(seq(50, 100, 5), 40, replace = TRUE)) %>%
  mutate(Value = ifelse(Gender == "F", Value *-1 , Value))


ggplot(df) +
  geom_col(aes(fill = interaction(Gender, Year, sep = "-"),
               y = Value,
               x = Age),
           position = "dodge") +
  scale_y_continuous(labels = abs,
                     expand = c(0, 0)) +
  scale_fill_manual(values = hcl(h = c(15,195,15,195),
                                 c = 100,
                                 l = 65,
                                 alpha=c(0.4,0.4,1,1)),
                    name = "") +
  coord_flip() +
  facet_wrap(.~ Gender,
             scale = "free_x",
             strip.position = "bottom") +
  theme_minimal() +
  theme(legend.position = "bottom",
        panel.spacing.x = unit(0, "pt"),
        strip.background = element_rect(colour = "black"))

example of back-to-back barplot I want to mimick

Trying to mimick this example on my data, things go wrong from the first ggplot function call as the bars are not dodged on both sides of the axis:

mydf = read.table("https://raw.githubusercontent.com/gilles-guillot/IPUMS_R/main/tmp/df.csv",
                header=TRUE,sep=";")

ggplot(mydf) + 
  geom_col(aes(fill =  interaction(mig,ISCO08WHO_yrstud, sep = "-"),
               x = country,
               y = f), 
           position = "dodge") 

failed attempt to get a back-to-back bar plot

as I was expected from:

ggplot(df) +
  geom_col(aes(fill = interaction(Gender, Year, sep = "-"),
               y = Value,
               x = Age),
           position = "dodge")

geol_col plot with bar dodged symmetrically around axis


Solution

  • In the example you are following, df$Value is made negative if Gender == 'F'. You need to do similar to achieve "bar dodged symmetrically around axis".