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
Upon changing position="stack"
, this is what I get :
I would like it to be something like this though; where we have only three bars and they are highlighted by "Score" pattern.
Any solutions please?
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.
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)
)
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.