Search code examples
rggplot2histogramstacked-bar-chart

ggplot2: Display shift in stacked histogram


I am trying to replicate a figure used in Stroke research to display results of clinical trials. I have included a figure down below as an example of the end result.

In a nutshell, the figure is a stacked histogram in percentage for two conditions (such as drug vs placebo). To indicate the shift, line are drawn from one bar to the next to emphasize the effect of treatment. Image: Image from published paper here https://doi.org/10.3389/fneur.2022.973095

The data will be something like:

mRS      Drug1      Drug2
0         10%        8%
1         12%        10%
2         22%        18%
3         9%         14%
4         15%        16%
5         12%        12%
6         20%        22%

Creating an horizontal stacked histogram is relatively simple, but I have no clue on how to create the line between the horizontal bars. Any helps or just hint toward an appropriate method to do so would be much appreciated! Thanks,

I tried to look on google for images of "horizontal stacked histogram" and apparently no one published something like that ;-)


Solution

  • A very similar approach to stefan here, but trying to match the original aesthetic more closely:

    library(tidyverse)
    
    df %>% 
      mutate(across(Drug1:Drug2, ~ as.numeric(sub('%', '', .x))/100)) %>%
      pivot_longer(-mRS, names_to = 'Drug', values_to = 'percent') %>%
      mutate(mRS = factor(mRS), y = as.numeric(factor(Drug))) %>%
      ggplot(aes(percent, y, fill = mRS)) +
      geom_segment(data = . %>% select(-y) %>% 
                     pivot_wider(names_from = 'Drug', values_from = 'percent') %>%
                     mutate(across(Drug1:Drug2, cumsum)),
                   aes(y = 1.2, yend = 1.8, x = Drug1, xend = Drug2),
                   alpha = 0.3) +
      geom_col(orientation = 'y', position = position_stack(reverse = TRUE),
               width = 0.4, col = 'gray60', linewidth = 0.2) +
      geom_text(data = . %>% 
                  group_by(Drug) %>% 
                  mutate(value = cumsum(percent),
                         stackval = lag(value, default = 0) + 0.5 * percent),
                aes(x = stackval, label = scales::percent(percent))) +
      scale_fill_brewer(palette = 'Blues') +
      scale_y_continuous(NULL, breaks = 1:2, labels = c('Drug 1', 'Drug 2'),
                         expand = c(0.3, 0)) +
      scale_x_continuous(NULL, labels = scales::percent, expand = c(0, 0)) +
      guides(fill = guide_legend(nrow = 1, byrow = TRUE)) +
      theme_minimal() +
      theme(legend.position = 'bottom',
            panel.grid = element_blank(),
            axis.line.y = element_line(color = 'gray50'),
            plot.margin = margin(20, 20, 20, 20)) 
    

    enter image description here