rggplot2plotlyr-plotlyggplotly

keep two legends from ggplot plot using plotly::ggplotly


When I convert a ggplot with two separate legends to plotly (using ggplotly), the two legends merge. Does anyone know how to prevent this?

library(tidyverse)
library(plotly)
df <- data.frame(date = 
                   as.Date(c("01/01/1998", "10/01/1998", "15/01/1998", 
                             "25/01/1998", "01/02/1998", "12/02/1998", "20/02/1998"), "%d/%m/%Y"),
                 date2 = as.Date(c(NA, "10/01/1998", NA, 
                                   NA, NA, NA, NA), "%d/%m/%Y"),
                 counts = c(12, 10, 2, 24, 15, 1, 14),
                 yes_no = c("yes", "yes", "yes", "no", "no", "no", "no"))
gg <- ggplot(df, aes(x = date, y = counts)) +
  geom_line() +
  geom_ribbon(aes(ymin = 0, ymax = counts, fill = yes_no), color = NA, alpha = 0.5) +
  ggplot2::scale_fill_brewer(name = "status 1", palette = "Accent") +
  geom_vline(mapping = aes(xintercept = as.numeric(date2), col = "mystatistic")) +
  scale_color_manual(name = "statistics", values = c("mystatistic" = "red")) 
gg

which produces two legends:

enter image description here

but when I convert to plotly:

ggplotly(gg)

it returns one legend with second legend title missing:

enter image description here

This link is useful to remove the parenthesis and comma from the current plot but that's not the fix I am looking for.

thanks


Solution

  • In general the concept of legends is different in ggplot2 and plotly.

    In ggplot2 legends reflect aesthetics, i.e. for your example you get a legend for the fill aes and one for the color aes. Additionally for each legend the legend entries reflect the categories of the variable mapped on the aesthetic.

    In contrast, in plotly versions < 5.15 there is only one legend (while plotly >= 5.15 supports multiple legends, this feature is at present not supported by the plotly for R package) and each legend entry reflects a trace.

    But one option to achieve your desired result would be to use legendgroups which allows to collect traces in groups and requires to manipulate the plotly object, e.g. in the code below I group the traces into two groups which I call fill and color (Note: There is also a fourth trace with index 1 reflecting the black geom_line, which however is not displayed in the legend). Second, we can assign a title to each legend group and finally get rid of the overall legend title.

    library(ggplot2)
    library(plotly)
    
    gg <- ggplot(df, aes(x = date, y = counts)) +
      geom_line() +
      geom_ribbon(aes(ymin = 0, ymax = counts, fill = yes_no), color = NA, alpha = 0.5) +
      ggplot2::scale_fill_brewer(name = "status 1", palette = "Accent") +
      geom_vline(mapping = aes(xintercept = as.numeric(date2), col = "mystatistic")) +
      scale_color_manual(name = "statistics", values = c("mystatistic" = "red"))
    
    ggp <- ggplotly(gg)
    
    # Assign the traces to legendgroups
    ggp$x$data[[2]]$legendgroup <- "fill"
    ggp$x$data[[3]]$legendgroup <- "fill"
    ggp$x$data[[4]]$legendgroup <- "color"
    
    # Add titles for the legendgroups
    ggp$x$data[[2]]$legendgrouptitle <- list(text = "status 1")
    ggp$x$data[[3]]$legendgrouptitle <- list(text = "status 1")
    ggp$x$data[[4]]$legendgrouptitle <- list(text = "statistics")
    
    # Remove overall legend title
    ggp$x$layout$legend$title <- NULL
    
    ggp
    

    enter image description here