1

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?

chemdork123
  • 12,369
  • 2
  • 16
  • 32
AneesBaqir
  • 423
  • 1
  • 12
  • Can you add the library calls to ggpattern and scales? I would edit but the que is full. Also I'm not familiar with the package but does setting `position='stack'` achieve what you want? – dandrews Mar 25 '23 at 11:51
  • As a last resort, you might want to try faceting by `Category_A` – Dmitry Zotikov Mar 25 '23 at 11:53
  • @dandrews thank you for replying. I added the libraries, and I added additional figure what "stack" would achieve. – AneesBaqir Mar 25 '23 at 12:13
  • 1
    @DmitryZotikov I would try avoiding faceting at the moment :( but if its not possible, then I'll try it. – AneesBaqir Mar 25 '23 at 12:14
  • I think I understand now, you want to use the "dodge" position for `Category_A` and the "stack" position for `Category_B` right? – dandrews Mar 25 '23 at 12:32
  • https://stackoverflow.com/questions/12715635/ggplot2-bar-plot-with-both-stack-and-dodge an option with faceting as suggested – dandrews Mar 25 '23 at 12:33
  • Final thought, perhaps it would be possible to summarise your data and add another geom layer and use transparency to achieve the desired results – dandrews Mar 25 '23 at 13:50

2 Answers2

3

I use ggplot geom_bar but it's simple and beautiful.
let's see:

df$Category_A <- as.factor(df$Category_A)
df$Category_B <- as.factor(df$Category_B)
df$Score <- as.factor(df$Score)
str(df)

ggplot(df, aes(fill=Category_B, x = Category_B, y = count, alpha = Score)) +
  geom_bar(position="fill", stat="identity") +
  facet_grid(.~Category_A) +
  scale_fill_manual(values=c("cyan3","darkgoldenrod1","brown2")) +
  scale_alpha_manual(values = c(0.6,1)) +
  xlab("")+
  ylab("")+
  theme(axis.text.x = element_blank(), 
        axis.ticks.x = element_blank(), 
        axis.text.y = element_blank(), 
        axis.ticks.y = element_blank()) +
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank()) +
  theme(strip.text.x = element_blank(), 
        strip.text.y = element_blank())

your desired plot

you can also delete legend if you want

3

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

  • Changes to pattern geom: I changed to geom_col_pattern() to remove the need for stat="identity". Most of the changes here are fiddling with the pattern arguments to make the pattern look right.

  • Adding white spaces: To add the white space between the stacked bars, we set linewidth and color=white within geom_col_pattern(). This draws a "white box" around each of the bars... therefore creating the illusion of space between the stacked bars.

  • Facets: I use facet_grid() to make the facets and set the property of switch="x", which makes the facet labels appear at the bottom of each facet (not the top, which is default).

  • Expanding axes: I use expand= within scale_x_log10() to ensure the bottom of the bars sit on the axis (no white space). I similarly use scale_x_discrete(expand=...), but the purpose of this term is to add whitespace to the sides of each facet. This allows us to separate the facets a bit without actually separating them... (more on that below)

  • Panel spacing: In concert with the note above, we put the panels next to each other with panel.spacing=unit(0, 'pt'). If we did not add the scale_x_discrete(..) term above, then we would have the bars too close between facets. This ensures we can have a single x axis line, but separate the stuff in the facet by setting the whitespace in between (hope this makes sense).

  • Lines and stuff in theme: There's a whole lot of theme stuff going on. Basically, we remove the default x axis labels and tick marks, remove the background and format for the facet labels, we put the labels outside the axis with the strip.position term, we remove the default facet border and add back in the axis lines.

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.

chemdork123
  • 12,369
  • 2
  • 16
  • 32