rggplot2bar-chartvisualization

Stacked barplot for multi-level grouped barplot


library(ggplot2)
library(ggpattern)
library(tidyverse)

mydata <- structure(list(Metric = structure(c(2L, 1L, 3L, 2L, 1L, 3L, 2L, 
                                              1L, 3L, 2L, 1L, 3L, 2L, 1L, 3L, 2L, 1L, 3L, 2L, 1L, 3L, 2L, 1L, 
                                              3L, 2L, 1L, 3L, 2L, 1L, 3L), 
                                            levels = c("Median", "Average", "Copula"), class = "factor"), 
                         Model = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 2L, 2L, 
                                             2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L), 
                                           levels = c("Model1", "Model2", "Model3"), class = "factor"), 
                         Strategy = c("Exact", "Exact", "Exact", "Exact", "Exact", "Exact", "Exact", "Exact", 
                                      "Exact", "Exact", "Exact", "Exact", "Relaxed", "Relaxed", "Relaxed", "Relaxed", 
                                      "Relaxed", "Relaxed", "Relaxed", "Relaxed", "Relaxed", "Relaxed", "Relaxed", 
                                      "Relaxed", "Exact", "Exact", "Exact", "Relaxed", "Relaxed", "Relaxed"), 
                         Size = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L, 4L, 1L, 1L, 1L, 2L, 2L, 
                                            2L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L), 
                                          levels = c("Model1", "Small", "Medium", "Large"), class = "factor"), 
                         Score = c(0.168041526339948, 0.5572494356893, 0.384942351374775, 0.327734317164868, 
                                   0.11144915339537, 0.604394054040313, 0.124633444240317, 0.279732553754002, 
                                   0.228201881283894, 0.0153298925142735, 0.128981558606029, 0.0933819285128266, 
                                   0.534035353455693, 0.807516399072483, 0.86791948764585, 0.829708693316206, 
                                   0.602100674761459, 0.70368835888803, 0.897488264366984, 0.29460092424415, 
                                   0.577609919011593, 0.630979274399579, 0.512015897547826, 0.505023914156482, 
                                   0.236885007470846, 0.560424553928897, 0.599731565685943, 0.910147710936144, 
                                   0.791147409472615, 0.755704769166186)), row.names = c(NA, -30L), 
                    class = c("tbl_df", "tbl", "data.frame"))


# Reorder Parameter and Metric within each Parameter
mydata$Size <- factor(mydata$Size, levels = c("Model1", "Small", "Medium", "Large"))
mydata$Metric <- factor(mydata$Metric, levels = c("Median", "Average", "Copula"))
mydata$Model <- factor(mydata$Model, levels = c("Model1", "Model2", "Model3"))



palette <- c("Model1" = "darkgreen", "Small" = "lightblue", "Medium" = "blue", "Large" = "darkblue")
pattern_map <- c("Median" = "none", "Average" = "circle", "Copula" = "stripe")
density_map <- c("Median" = 0, "Average" = 0.05, "Copula" = 0.05)
angle_map <- c("Median" = 0, "Average" = 45, "Copula" = 90)


# Create the bar plot with improved aesthetics, customized patterns, densities, and bar outlines
ggplot(mydata, aes(x = Model, y = Score, fill = Size, pattern = Metric, pattern_density = Metric)) +
  geom_bar_pattern(stat = "identity", 
                   position = position_dodge2(padding = 0.15, preserve = "single"), 
                   width = 1,
                   color = "black",  # Adds black outline around each bar
                   pattern_fill = "white", pattern_spacing = 0.05,
                   pattern_color = "white", pattern_angle = 45) +
  scale_pattern_manual(values = pattern_map) +  # Apply custom patterns
  scale_pattern_density_manual(values = density_map) +  # Apply custom densities
  labs(x = "Model", y = "", fill = "Size", pattern = "Metric") +
  scale_fill_manual(values = palette) +
  theme_classic() +
  theme(axis.text.x = element_text(hjust = 1),
        legend.position = "right",
        legend.box = "vertical",
        strip.text = element_text(size = 12, face = "bold"),  # Enhances facet labels
        panel.spacing = unit(1, "lines"),  # Adds more spacing between panels
        panel.border = element_rect(color = "black", fill = NA, size = 0.5)) +  # Adds a border to panels
  guides(pattern = guide_legend(override.aes = list(fill = NA)))

enter image description here

I have a multi-level side-by-side barplot that looks like the above. It comes with 3 levels:

For the inner-most level, the bars for Strategy are currently placed side by side. How can I change it so that it becomes a stacked bar?

Please note that I already looked at the answer provided here, but I think my setup is a bit more complicated due to the multi-level grouping structure.


Solution

  • Similar to the referenced post you could use faceting to get a stacked barchart but instead of faceting by just one variable you could facet by two, e.g. Model and Size. Instead of using facet_wrap I use ggh4x::facet_nested_wrap:

    library(ggplot2)
    library(ggpattern)
    library(ggh4x)
    
    ggplot(mydata, aes(
      x = Metric, y = Score, fill = Size,
      pattern = Metric, pattern_density = Strategy
    )) +
      geom_col_pattern(
        width = 1,
        color = "black",
        pattern_fill = "white", pattern_spacing = 0.05,
        pattern_color = "white", pattern_angle = 45
      ) +
      ggh4x::facet_nested_wrap(
        . ~ Model + Size,
        scales = "free_x",
        nrow = 1,
        strip.position = "bottom"
      ) +
      ## Fake the panel borders
      geom_hline(
        yintercept = c(-Inf, Inf),
        linewidth = .5
      ) +
      geom_vline(
        data = data.frame(
          Model = c("Model1", "Model3"),
          Size = c("Model1", "Large"),
          xintercept = c(-Inf, Inf)
        ),
        aes(xintercept = xintercept),
        linewidth = .5
      ) +
      ##
      scale_x_discrete(
        expand = c(0, .8),
        breaks = NULL
      ) +
      # scale_pattern_manual(values = pattern_map) + # Apply custom patterns
      # scale_pattern_density_manual(values = density_map) + # Apply custom densities
      scale_fill_manual(values = palette) +
      theme_classic() +
      theme(
        legend.position = "right",
        legend.box = "vertical",
        strip.text = element_text(size = 12, face = "bold"),
        panel.spacing.x = unit(0, "lines"),
        panel.border = element_blank(),
        strip.background = element_rect(linewidth = .25),
        strip.placement = "outside",
        axis.line = element_line(linewidth = .25),
      ) +
      labs(x = "Model", y = "", fill = "Size", pattern = "Metric")
    

    enter image description here