rggplot2plotlylegend

plotly drops grouped legend (by color, by symbol) when converted from ggplot


I don't quite understand why the legend disappeared when I converted a plot made by ggplot to plotly using ggplotly. The plotly help page did not have any information. I don't think their examples even worked properly on that page. Any help is greatly appreciated!

Sample data

library(scales)
packageVersion("ggplot2")
#> [1] '3.4.0'
library(plotly)
packageVersion("plotly")
#> [1] '4.10.1'

data <- data.frame(
  stringsAsFactors = FALSE,
  Level = c("Fast","Fast","Fast","Fast",
            "Fast","Fast","Slow","Slow","Slow",
            "Slow","Slow","Slow"),
  Period = c("1Year","3Month","1Year","3Month",
             "1Year","3Month","1Year","3Month",
             "1Year","3Month","1Year","3Month"),
  X = c(0.002,0.002,0.1,0.1,0.9,0.9,
        0.002,0.002,0.1,0.1,0.9,0.9),
  Y = c(1.38,1.29,1.61,1.61,1.74,0.98,
        1.14,0.97,1.09,1.1,0.94,0.58)
)

ggplot2

plt <- ggplot(data = data,
             aes(x = X,
                 y = Y,
                 shape = Period,
                 color = Level)) +
  geom_point(alpha = 0.6, size = 3) +
  labs(x = " ",
       y = "Value") +
  scale_y_continuous(labels = number_format(accuracy = 0.1)) +
  guides(color = guide_legend(title = "Level", order = 1),
         shape = guide_legend(title = "Period", order = 2)) +
  theme(axis.text.x = element_text(angle = 90))
plt

Convert to plotly, legend disappeared

ggplotly(plt, height = 500) %>%
  layout(xaxis = list(autorange = "reversed"))

Edit

There was an issue with guides(). If I removed it, the legend in ggplotly showed up

plt2 <- ggplot(data = data,
             aes(x = X,
                 y = Y,
                 shape = Period,
                 color = Level)) +
  geom_point(alpha = 0.6, size = 3) +
  labs(x = " ",
       y = "Value") +
  scale_y_continuous(labels = number_format(accuracy = 0.1)) +
  theme(axis.text.x = element_text(angle = 90))
plt2

ggplotly(plt2, height = 500) %>%
  layout(
    xaxis = list(autorange = "reversed"),
    legend = list(
      title = list(text = '(Period, Level)'))
  )


Solution

  • After OPs Edit:

    Here is a workaround using basic R {plotly} to modify the legend according to @Tung's requirements:

    result

    library(scales)
    library(ggplot2)
    library(plotly)
    library(data.table)
    
    DT <- data.frame(
      stringsAsFactors = FALSE,
      Level = c("Fast","Fast","Fast","Fast",
                "Fast","Fast","Slow","Slow","Slow",
                "Slow","Slow","Slow"),
      Period = c("1Year","3Month","1Year","3Month",
                 "1Year","3Month","1Year","3Month",
                 "1Year","3Month","1Year","3Month"),
      X = c(0.002,0.002,0.1,0.1,0.9,0.9,
            0.002,0.002,0.1,0.1,0.9,0.9),
      Y = c(1.38,1.29,1.61,1.61,1.74,0.98,
            1.14,0.97,1.09,1.1,0.94,0.58)
    )
    
    setDT(DT)
    
    LevelDT <- unique(DT, by = "Level")
    PeriodDT <- unique(DT, by = "Period")
    LevelDT[, Y := min(DT$Y)-1]
    PeriodDT[, Y := min(DT$Y)-1]
    
    plt2 <- ggplot(data = DT,
                   aes(x = X,
                       y = Y,
                       shape = Period,
                       color = Level)) +
      geom_point(alpha = 0.6, size = 3) +
      labs(x = " ",
           y = "Value") +
      scale_y_continuous(labels = number_format(accuracy = 0.1)) +
      theme(axis.text.x = element_text(angle = 90))
    plt2
    
    markercolors <- hue_pal()(2)
    
    ggplotly(plt2, height = 500) |>
      layout(
        xaxis = list(autorange = "reversed"),
        legend = list(
          title = list(text = ''),
          itemclick = FALSE,
          itemdoubleclick = FALSE,
          groupclick = FALSE
        )
      ) |>
      add_trace(
        data = LevelDT,
        x = ~ X,
        y = ~ Y,
        inherit = FALSE,
        type = "scatter",
        mode = "markers",
        marker = list(
          color = markercolors,
          size = 14,
          opacity = 0.6,
          symbol = "circle"
        ),
        name = ~ Level,
        legendgroup = "Level",
        legendgrouptitle = list(text = "Level")
      ) |>
      add_trace(
        data = PeriodDT,
        x = ~ X,
        y = ~ Y,
        inherit = FALSE,
        type = "scatter",
        mode = "markers",
        marker = list(
          color = "darkgrey",
          size = 14,
          opacity = 0.6,
          symbol = c("circle", "triangle-up")
        ),
        name = ~Period,
        legendgroup = "Period",
        legendgrouptitle = list(text = "Period")
      ) |> style(showlegend = FALSE, traces = 1:4)
    

    PS: Here the related plotly.js GitHub issue can be found.


    Original answer:

    I'm not sure why they are set to FALSE in the first place, but setting showlegend = TRUE in layout() and style() (for the traces) brings back the legend:

    library(scales)
    library(ggplot2)
    library(plotly)
    
    data <- data.frame(
      stringsAsFactors = FALSE,
      Level = c("Fast","Fast","Fast","Fast",
                "Fast","Fast","Slow","Slow","Slow",
                "Slow","Slow","Slow"),
      Period = c("1Year","3Month","1Year","3Month",
                 "1Year","3Month","1Year","3Month",
                 "1Year","3Month","1Year","3Month"),
      X = c(0.002,0.002,0.1,0.1,0.9,0.9,
            0.002,0.002,0.1,0.1,0.9,0.9),
      Y = c(1.38,1.29,1.61,1.61,1.74,0.98,
            1.14,0.97,1.09,1.1,0.94,0.58)
    )
    
    # ggplot2
    plt <- ggplot(data = data,
                  aes(x = X,
                      y = Y,
                      shape = Period,
                      color = Level)) +
      geom_point(alpha = 0.6, size = 3) +
      labs(x = " ",
           y = "Value") +
      scale_y_continuous(labels = number_format(accuracy = 0.1)) +
      guides(color = guide_legend(title = "Period", order = 1),
             shape = guide_legend(title = "", order = 2)) +
      theme(axis.text.x = element_text(angle = 90))
    plt
    
    # Convert to plotly, legend disappeared
    fig <- ggplotly(plt, height = 500) %>%
      layout(showlegend = TRUE, xaxis = list(autorange = "reversed")) %>%
      style(showlegend = TRUE)
    fig
    

    result