rggplot2

Overlay two population pyramid plots in one ggplot graph


I'm trying to overlay two population pyramid plots in one ggplot2 plot. I first created a plot using the census data with lighter colors and then try to superimpose another plot (created using the survey data with darker colors, say darkred, steelblue) onto the former. Is there any way I can do that while having their gender (sex) labels displayed on the legend? (Male (c/s) refers to Male (census/survey), the same goes to Female). This post is a reference.

library(tidyverse)
library(ggplot2)

# create data
census <- data.frame(age = c("20-29", "30-39", "40-49", "50-59", "60-69", "70+"), PopPerc = c(12.45, 14.14, 16.56, 14.73, 13.77, 11.00, -11.18, -13.13, -16.67, -15.10, -14.60, -13.82), sex = c("Male (c)", "Male (c)", "Male (c)", "Male (c)", "Male (c)", "Male (c)", "Female (c)", "Female (c)", "Female (c)", "Female (c)", "Female (c)", "Female (c)"), signal = c(1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1))

survey <- data.frame(age = c("20-29", "30-39", "40-49", "50-59", "60-69", "70+"), PopPerc = c(12.18, 24.70, 27.54, 19.22, 11.77, 4.60, -11.94, -25.76, -29.59, -18.11, -10.84, -3.76), sex = c("Male (s)", "Male (s)", "Male (s)", "Male (s)", "Male (s)", "Male (s)", "Female (s)", "Female (s)", "Female (s)", "Female (s)", "Female (s)", "Female (s)"), signal = c(1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1))

# create base plot using census data

base <- census %>%
  ggplot() + # build the plot with ggplot2
  geom_bar(aes(x = age, y = PopPerc, fill = sex), stat = 'identity') + 
  geom_text(aes(
    # create the text
    x = age,
    y = PopPerc + signal * .3,
    label = abs(PopPerc)
  )) +
  coord_flip() + # flip the plot
  scale_fill_manual(name = '', values = alpha(c('lightpink', 'lightblue1'), alpha = 0.5)) + (darkred = female, steelblue = male)
  scale_y_continuous(
    # scale the y-lab
    labels = function(x) {
      paste(abs(x), '%')
    }
  ) +
  labs(
    # name the labs
    x = '',
    y = 'Participants by %',
    title = ''
  ) +
  theme(
    # costume the theme
    axis.text.x = element_text(vjust = .5),
    panel.grid.major.y = element_line(color = 'lightgray', linetype =
                                        'dashed'),
    legend.position = 'top',
    legend.justification = 'center'
  ) +
  theme_classic() 

base

Solution

  • I'm not a 100% clear on what you want, but I think this should get you started at least. I bind the two data.frames together, like so:

    bind_rows(census, survey, .id = 'type') %>%
      ggplot() + 
      geom_col(aes(PopPerc, age, fill = sex), position = 'identity', alpha = 0.5) + 
      geom_text(aes(PopPerc + signal * 1, age, label = abs(PopPerc))) +
      scale_fill_manual(values = c('lightpink', 'darkred', 'lightblue1', 'steelblue')) + 
      scale_x_continuous(labels = \(x) paste(abs(x), '%')) +
      labs(
        # name the labs
        x = 'Participants by %',
        y = NULL, fill = NULL
      ) +
      theme(
        # customize the theme
        axis.text.x = element_text(vjust = .5),
        panel.grid.major.y = element_line(color = 'lightgray', linetype = 'dashed'),
        legend.position = 'top',
        legend.justification = 'center'
      ) +
      theme_classic() 
    

    enter image description here