1

I have been using the wonderful ggplot library in R but love the interactivity of plotly. For most of the plots I want, ggplotly() does what I need, but for a few key ones, it doesn't. The plot is pretty straightforward in ggplot.

Here is some sample data alongside the plotting code:

sample = crossing(x = 1:10, 
                  g1 = c("A", "B", "C"), 
                  g2 = c("a", "b", "c", "d"), 
                  c = 1:5) %>%
    mutate(mid = runif(600),
                 low = mid * 0.8,
                 hi = mid * 1.2)

p1 = sample %>%
    ggplot(aes(x = x, colour = as_factor(c))) +
    geom_line(aes(y = mid)) +
    geom_ribbon(aes(ymin = low, ymax = hi, fill = as_factor(c)), alpha = 0.2) +
    facet_grid(rows = vars(g1), cols = vars(g2))

p1 looks like this: sample ggplot plot

p1 %>% ggplotly() doesn't handle the legend that well. The ribbon fill is attached to all subplots but the line colour is matched to each of the 15 subplots individually. And they are broken into separate legend items. enter image description here

I almost get there if I just remove the colour aesthetic from p1, but then all the ribbons are gray. Is there an easy way to modify this for ggplotly? I'd also be open to doing this natively in plotly, but it looks like the subplot function requires much more coding to get it to work.

Thanks for any advice.

buggaby
  • 359
  • 2
  • 13
  • TBMK there is no easy option to fix that. A possible duplicate: [Avoid legend duplication in plotly conversion from ggplot with facet_wrap](https://stackoverflow.com/questions/69289623/avoid-legend-duplication-in-plotly-conversion-from-ggplot-with-facet-wrap/69290220#69290220) – stefan Apr 17 '22 at 03:32

1 Answers1

2

It can be done. It can be a bit painful if you don't know where to start, though.

The first thing I did was build your plotly plot.

p2 <- plotly_build(p1)

I looked at how many traces were created and if legend groups were assigned.

You usually will have a trace for each color for each trace. You have 12 plots, which include ribbons and lines-each with 5 colors. So, that is 12 * 2 * 5 traces or 120 traces or 120 separate subplots.

You can look at the legend groups per trace like this:

lapply(1:length(p2$x$data), function(j){p2$x$data[[j]]$legendgroup}) %>% unlist()

Next, make the legendgroup assignments of all subplots a value of 1:5. After modifying, hide all but one legend for each group.

# use legendgroups as legend, make them uniform
lapply(1:length(p2$x$data),
       function(i){
         val <- p2$x$data[[i]]$legendgroup
         if(nchar(val) > 1){                      # if val is (1, 3) or similar
           val = substr(val, start = 2, stop = 2) # pick the color val
         }
         p2$x$data[[i]]$legendgroup <<- val
         p2$x$data[[i]]$name <<- val
         }
       ) 
# give me one trace for each group, inactivate the rest of the legends
tellMe <- lapply(1:length(p2$x$data), function(j){p2$x$data[[j]]$legendgroup})
tellMeMore <- which(!duplicated(tellMe))

lapply(1:length(p2$x$data),
       function(k){
         if(!(k %in% tellMeMore)){
           p2$x$data[[k]]$showlegend <<- FALSE # hide duplicated legends
         }
       })

The last thing that I did was hide the title of the legend.

p2 %>% layout(legend = list(title = list(text = "")))

enter image description here

I didn't like how the legend was overlapping the 'A' label, so I made one more modification.

p2 %>% layout(legend = list(title = list(text = ""),
                            x = 1.05))

enter image description here

Kat
  • 15,669
  • 3
  • 18
  • 51