Search code examples
rplotly

Is it possible to overlay a stacked barchart onto a barchart that isn't stacked?


I am trying to create a graph using plot_ly that looks something like this (i.e. there is a stacked plot over the top of an unstacked plot). FYI the numbers above the bars are not relevant for this.

enter image description here

I want separate layouts for each trace but when I run this it seems that the layout is global rather than separate per trace:

plot_ly()%>%
    add_trace(data = data1, x = ~From, y = ~Value,  type = "bar", color=~To) %>% 
    layout(barmode = "stack") %>%
    add_trace(data = data2, x = ~From, y = ~total_value,  type = "bar") %>%
    layout(barmode = "overlay")

Is it possible to do this or is there a workaround?

Here is the data to play with:

data1=structure(list(From = structure(c(2L, 5L, 6L, 7L, 1L, 3L, 4L,  7L, 8L, 2L, 4L, 5L, 6L, 7L, 8L, 2L, 3L, 5L, 7L, 1L, 3L, 4L, 8L,  1L, 3L, 1L, 2L, 3L, 4L, 8L, 2L, 3L, 5L, 7L, 2L, 5L, 6L, 7L, 3L,  4L, 7L, 8L, 4L, 5L, 6L, 7L, 8L, 2L, 3L, 5L, 7L, 1L, 3L, 4L, 8L,  1L, 3L, 1L, 2L, 3L, 4L, 8L, 2L, 3L, 5L, 7L, 2L, 5L, 6L, 7L, 3L,  4L, 7L, 8L, 4L, 5L, 6L, 7L, 8L, 2L, 3L, 5L, 7L, 3L, 4L, 8L, 1L,  3L, 1L, 2L, 3L, 4L, 8L, 2L, 3L, 5L, 7L), levels = c("Lucy", "Jimmy",  "Luke", "Paul", "Sarah", "David", "Farms", "Iain"), class = "factor"), 
        To = c("Lucy", "Lucy", "Lucy", "Lucy", "Jimmy", "Jimmy", 
        "Jimmy", "Jimmy", "Jimmy", "Luke", "Luke", "Luke", "Luke", 
        "Luke", "Luke", "Paul", "Paul", "Paul", "Paul", "Sarah", 
        "Sarah", "Sarah", "Sarah", "David", "David", "Farms", "Farms", 
        "Farms", "Farms", "Farms", "Iain", "Iain", "Iain", "Iain", 
        "Lucy", "Lucy", "Lucy", "Lucy", "Jimmy", "Jimmy", "Jimmy", 
        "Jimmy", "Luke", "Luke", "Luke", "Luke", "Luke", "Paul", 
        "Paul", "Paul", "Paul", "Sarah", "Sarah", "Sarah", "Sarah", 
        "David", "David", "Farms", "Farms", "Farms", "Farms", "Farms", 
        "Iain", "Iain", "Iain", "Iain", "Lucy", "Lucy", "Lucy", "Lucy", 
        "Jimmy", "Jimmy", "Jimmy", "Jimmy", "Luke", "Luke", "Luke", 
        "Luke", "Luke", "Paul", "Paul", "Paul", "Paul", "Sarah", 
        "Sarah", "Sarah", "David", "David", "Farms", "Farms", "Farms", 
        "Farms", "Farms", "Iain", "Iain", "Iain", "Iain"), Value = c(10, 
        10, 20, 10, 10, 10, 10, 20, 10, 10, 10, 20, 10, 10, 10, 10, 
        10, 10, 10, 10, 20, 10, 10, 20, 10, 10, 20, 10, 10, 10, 10, 
        10, 10, 10, 10, 10, 20, 10, 10, 10, 20, 10, 10, 20, 10, 10, 
        10, 10, 10, 10, 10, 10, 20, 10, 10, 20, 10, 10, 20, 10, 10, 
        10, 10, 10, 10, 10, 10, 10, 20, 10, 10, 10, 20, 10, 10, 10, 
        10, 10, 10, 10, 10, 10, 10, 20, 10, 10, 20, 10, 10, 20, 10, 
        10, 10, 10, 10, 10, 10)), row.names = c(NA, -97L), class = c("tbl_df",  "tbl", "data.frame"))
    
    data2=structure(list(From = structure(1:8, levels = c("Lucy", "Jimmy", "Luke", "Paul", "Sarah", "David", "Farms", "Iain"), class = "factor"),
    
        total_value = c(50, 50, 60, 40, 50, 30, 60, 40)), class = c("tbl_df",  "tbl", "data.frame"), row.names = c(NA, -8L))

Solution

  • You can construct your plot as a ggplot2 object before calling ggplotly().

    library(ggplot2)
    library(plotly)
    library(dplyr)
    
    data1_sum <- data1 |> summarize(
      Value = sum(Value), .by = c(From, To)
    )
    
    p1 <- data2 |>
      ggplot(aes(x = From)) +
      geom_col(aes(y = total_value, fill = From)) +
      geom_col(data = data1_sum,
               aes(y = Value, fill = To),
               width = 0.3) +
      scale_fill_brewer(palette = 'Dark2')
    
    p1 
    
    # Commented out for reprex
    # ggplotly(p1) 
    

    Created on 2024-06-24 with reprex v2.1.0