rggplot2facet-gridggpattern

ggplot2: How to stack patterned columns in a facet grid plot?


Suppose that I have data on gender distribution (female, male, divers) in three different countries (USA, Nepal, Japan) with three different locations each (province, small town, city) for two years (2019, 2022).

I want to display all of these data in a facet grid of bar plots, dividing columns by gender type. Specifically, I want the gender types to be displayed as stacked, patterned (not coloured!) subcolumns. Is that possible?

My code and the "dream illustriation" are provided below:

data <- structure(list(year = c(2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 
2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2022, 2022, 
2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 
2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 
2022, 2022, 2022), country = c("USA", "USA", "USA", "USA", "USA", 
"USA", "USA", "USA", "USA", "Nepal", "Nepal", "Nepal", "Nepal", 
"Nepal", "Nepal", "Nepal", "Nepal", "Nepal", "Japan", "Japan", 
"Japan", "Japan", "Japan", "Japan", "Japan", "Japan", "Japan", 
"USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", 
"Nepal", "Nepal", "Nepal", "Nepal", "Nepal", "Nepal", "Nepal", 
"Nepal", "Nepal", "Japan", "Japan", "Japan", "Japan", "Japan", 
"Japan", "Japan", "Japan", "Japan"), location = c("province", 
"province", "province", "small town", "small town", "small town", 
"city", "city", "city", "province", "province", "province", "small town", 
"small town", "small town", "city", "city", "city", "province", 
"province", "province", "small town", "small town", "small town", 
"city", "city", "city", "province", "province", "province", "small town", 
"small town", "small town", "city", "city", "city", "province", 
"province", "province", "small town", "small town", "small town", 
"city", "city", "city", "province", "province", "province", "small town", 
"small town", "small town", "city", "city", "city"), gender = c("male", 
"female", "divers", "male", "female", "divers", "male", "female", 
"divers", "male", "female", "divers", "male", "female", "divers", 
"male", "female", "divers", "male", "female", "divers", "male", 
"female", "divers", "male", "female", "divers", "male", "female", 
"divers", "male", "female", "divers", "male", "female", "divers", 
"male", "female", "divers", "male", "female", "divers", "male", 
"female", "divers", "male", "female", "divers", "male", "female", 
"divers", "male", "female", "divers"), quantity = c(6, 5, 1, 
7, 7, 2, 15, 18, 4, 4, 8, 0, 5, 6, 1, 11, 12, 4, 9, 4, 3, 8, 
8, 1, 16, 13, 0, 7, 5, 0, 6, 4, 2, 18, 17, 2, 5, 7, 0, 6, 6, 
2, 14, 10, 1, 5, 6, 1, 6, 4, 2, 14, 10, 5), total = c(12, NA, 
NA, 16, NA, NA, 37, NA, NA, 12, NA, NA, 12, NA, NA, 27, NA, NA, 
16, NA, NA, 17, NA, NA, 29, NA, NA, 12, NA, NA, 12, NA, NA, 37, 
NA, NA, 12, NA, NA, 14, NA, NA, 25, NA, NA, 12, NA, NA, 12, NA, 
NA, 29, NA, NA)), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, 
-54L))

data <- data %>%
  mutate(across(year, factor, levels = c("2019","2022"))) %>%
  mutate(across(country, factor, levels = c("USA", "Nepal", "Japan"))) %>%
  mutate(across(location, factor, levels = c("province", "small town", "city"))) %>%
  mutate(across(gender, factor, levels = c("male", "female", "divers")))

ggplot(data, aes(y = total, x = location, fill = country)) +
  geom_col(position = position_dodge(width = 0.5), size = .5, width = .5) +
  labs(x = "Year", y = "Count") +
  scale_y_continuous(expand = c(0, 0), limits = c(0, 40), breaks = seq(0, 40, by = 10)) +
  scale_fill_manual(values = c("blue", "green", "red"), name = "Country") +
  scale_color_manual(values = c("black", "black", "black")) +
  facet_grid(rows = vars(country), cols = vars(year)) +
  theme(panel.background = element_rect(fill = "white"),
        panel.spacing = unit(.7, "cm"),
        strip.background = element_blank(),
        panel.border = element_rect(fill = "transparent", colour = "black"),
        strip.text = element_text(size = 14),
        axis.text = element_text(size = 12, colour="black"),
        axis.title = element_text(size = 14, colour = "black"),
        legend.position = "bottom",
        legend.background = element_rect(fill = "white"),
        legend.text = element_text(size = 12),
        legend.title = element_text(size = 14))

enter image description here


Solution

  • ggplot(data, aes(y = quantity, x = location)) +
      ggpattern::geom_col_pattern(aes(pattern = gender), 
                                  # this 
                                  pattern_key_scale_factor = 0.3,
                                  colour = 'black',
                                  fill = 'white',
                                  position = position_stack()) +
      theme(legend.key.size = unit(0.5, 'cm')) +
      ...
    

    You may want to experiment with the scale parameters and the size of your output to get the stripes to be the scale you want.

    enter image description here