rggplot2ggpattern

How to dodge and stack a bar plot with a pattern in R?


I have a dataframe in R which is as below :

library(dplyr)
library(ggplot2)
library(ggraph)
library(scales)
library(ggpattern)

df <- structure(list(Category_A = c("Class_A", "Class_A", "Class_A", 
"Class_A", "Class_A", "Class_A", "Class_C", "Class_C", "Class_C", 
"Class_C", "Class_C", "Class_C", "Class_B", "Class_B", "Class_B", 
"Class_B", "Class_B", "Class_B"), Category_B = c("Class_A", "Class_A", 
"Class_C", "Class_C", "Class_B", "Class_B", "Class_A", "Class_A", 
"Class_C", "Class_C", "Class_B", "Class_B", "Class_A", "Class_A", 
"Class_C", "Class_C", "Class_B", "Class_B"), Score = c("Fail", 
"Pass", "Fail", "Pass", "Fail", "Pass", "Fail", "Pass", "Fail", 
"Pass", "Fail", "Pass", "Fail", "Pass", "Fail", "Pass", "Fail", 
"Pass"), count = c(19837.25, 156448.75, 134.5, 1105.75, 33738, 
162531, 322.75, 2134.5, 190.25, 2119.25, 1514, 8450.25, 139259, 
549000.5, 1419.75, 7180, 37118.25, 231676)), row.names = c(NA, 
-18L), groups = structure(list(Category_A = c("Class_A", "Class_A", 
"Class_A", "Class_B", "Class_B", "Class_B", "Class_C", "Class_C", 
"Class_C"), Category_B = c("Class_A", "Class_B", "Class_C", "Class_A", 
"Class_B", "Class_C", "Class_A", "Class_B", "Class_C"), .rows = structure(list(
    1:2, 5:6, 3:4, 13:14, 17:18, 15:16, 7:8, 11:12, 9:10), ptype = integer(0), class = c("vctrs_list_of", 
"vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame"
), row.names = c(NA, -9L), .drop = TRUE), class = c("grouped_df", 
"tbl_df", "tbl", "data.frame"))

I am plotting it using the following code :

ggplot(df, aes(fill=Category_B, y=count, x=Category_A, pattern=Score)) + 
  geom_bar_pattern( position="dodge", stat="identity", pattern_spacing = 0.01,
                    pattern_frequency = 5, pattern_angle = 45)+
  theme_bw()+
  labs(y = "Count", x="")+
  scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x),
                labels = trans_format("log10", math_format(10^.x)))+
  scale_fill_manual(values=c("cyan3","darkgoldenrod1","brown2")) +
  scale_pattern_manual(values=c('stripe', 'none'))+
  guides(fill = guide_legend(override.aes = list(pattern = c("none", "none", "none")))) +
  theme(legend.key.width = unit(1, "cm"), legend.title = element_blank(),
        legend.background = element_rect(color = "transparent"), legend.position = "right",
        legend.direction = "vertical", legend.spacing.y = unit(0.4, 'cm'),
        panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        text = element_text(size=16))

And this is what I get

enter image description here

Upon changing position="stack", this is what I get : enter image description here

I would like it to be something like this though; where we have only three bars and they are highlighted by "Score" pattern.

enter image description here

Any solutions please?


Solution

  • OP's question has more to do with wanting to dodge and stack at the same time with two different values in the dataset than it does specifically to do with using ggpattern. With that said, this question might be quite helpful with some options.

    Simply put - there is no way to both set position="dodge" and position="stack" at the same time right out of the box. The simplest way to approximate this behavior is to be creative in how you use faceting.

    Here I'll show a solution which separates out Category_A into facets, which means we actually set Category_B as the x axis variable. We keep fill=Category_B and remove the actual x axis labels, and use the facet labels to act as our x axis labels. Here's the code, the resulting plot, and a brief explanation. I've made a bunch of edits and added comments to OP's original plot code to help identify what the changes are doing and why.

    The Code

    ggplot(df, aes(fill=Category_B, y=count, x=Category_B, pattern=Score)) + 
      geom_col_pattern(
        position="stack",
        pattern_spacing = 0.02,  # had to fiddle to look right
        pattern_frequency = 5,
        pattern_fill=NA,   # removes gray parts you see between pattern lines
        pattern_angle = 45,
        color='white', linewidth=1   # white lines between stacked bars
      ) +
      theme_bw()+
      
      # add facets and push facet labels to the bottom
      facet_grid(.~Category_A, switch = "x") +
      
      labs(y = "Count", x="")+
      scale_y_log10(breaks = trans_breaks("log10", function(x) 10^x),
                    labels = trans_format("log10", math_format(10^.x)),
                    expand = expansion(mult=c(0, 0.05))  # removes space at bottom
      ) +
      
      # adds some space on the x axis between Category_B values
      scale_x_discrete(expand=expansion(mult=0.6)) +  # adds space between Category_B
      
      scale_fill_manual(values=c("cyan3","darkgoldenrod1","brown2")) +
      scale_pattern_manual(values=c('stripe', 'none'))+
      guides(fill = guide_legend(override.aes = list(pattern = c("none", "none", "none")))) +
      
      # format changes noted
      theme(
        legend.key.width = unit(1, "cm"), legend.title = element_blank(),
        legend.background = element_rect(color = "transparent"), legend.position = "right",
        legend.direction = "vertical", legend.spacing.y = unit(0.4, 'cm'),
        panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        panel.border = element_rect(color=NA),  # remove the border around facets
        panel.spacing = unit(0, 'pt'),  # smoosh facets together
        strip.background = element_rect(color=NA, fill=NA),  # facet label format
        strip.placement = "outside", # get those facet labels outside the axis!
        axis.line = element_line(),  # add back in the axis lines
        axis.text.x = element_blank(),  # removes x axis labels
        axis.ticks.x = element_blank(), # removes x axis ticks
        text = element_text(size=16)
      )
    

    The Plot

    enter image description here

    The Explanation

    Note: OP shows originally a chart where all bars go to the top of the screen. If that is indeed the case, then OP would want to change position="stack" to read position="fill". If that change is met, the y axis labels wouldn't really mean much - might want to change to represent proportion using the scales package if needed.