Plotly subplots - legend item next to its plot

Viewed 953

When using plotly subplots, here's a typical example (from https://plotly.com/python/subplots/):

from plotly.subplots import make_subplots
import plotly.graph_objects as go

fig = make_subplots(rows=3, cols=1)
fig.append_trace(go.Scatter(x=[3, 4, 5], y=[1000, 1100, 1200]), row=1, col=1)
fig.append_trace(go.Scatter(x=[2, 3, 4], y=[100, 110, 120]), row=2, col=1)
fig.append_trace(go.Scatter(x=[0, 1, 2], y=[10, 11, 12]), row=3, col=1)
fig.update_layout(height=600, width=600, title_text="Stacked Subplots")
fig.show()

enter image description here

This doesn't look too bad with just 3 data series, but when I have e.g. 5 subplots that each have a few series, it gets quite difficult to visually associate an item in the legend with where it appears in a plot.

Is it possible to move the legend items downward to align them with the plots in which they appear?

Incidentally, I'm actually working in R, not Python, and I'm generating a list of the individual plots using ggplot and ggplotly(), then assembling them like so:

  p <- subplot(plots, shareX=TRUE, nrows=length(plots))
  layout_args <- c(list(p,
                        title=title,
                        xaxis=list(title = 'Time (Local)'),
                        legend=list(orientation = 'v')),
                   yaxis_args)
  p <- do.call(layout, layout_args)
  htmlwidgets::saveWidget(htmlwidgets::as_widget(p), outfile)

But I'm guessing a solution will probably apply equally to both R and Python.

2 Answers

There are multiple ways, IMHO simplest is to add additional scatter traces that have text label of line. x and y of text scatter are line x/y of traces

from plotly.subplots import make_subplots
import plotly.graph_objects as go

fig = make_subplots(rows=3, cols=1)
fig.append_trace(go.Scatter(x=[3, 4, 5], y=[1000, 1100, 1200]), row=1, col=1)
fig.append_trace(go.Scatter(x=[2, 3, 4], y=[100, 110, 120]), row=2, col=1)
fig.append_trace(go.Scatter(x=[0, 1, 2], y=[10, 11, 12]), row=3, col=1)
fig.append_trace(go.Scatter(mode="text", text=["trace 1"], x=[5], y=[1200]), row=1, col=1)
fig.append_trace(go.Scatter(mode="text", text=["trace 2"],x=[4], y=[120]), row=2, col=1)
fig.append_trace(go.Scatter(mode="text", text=["trace 3"],x=[2], y=[12]), row=3, col=1)

fig.update_layout(height=600, width=600, title_text="Stacked Subplots", showlegend=False)
fig.show()

enter image description here

Since you stated that you need to have an interactive legend in which traces can be toggled on and off, it won't be possible to just use annotations on the subplots.

Its a bit of a messy workaround, but, to achieve this you can try using tracegroupgap. The downside is that you will need to tinker with the value you supply to this option to match the size of the window.

p1 = plot_ly(data.frame(x=c(3, 4, 5), y=c(1000, 1100, 1200))) %>%
       add_lines(~x, ~y, legendgroup = 'group1', name = 'Trace 1')
p2 = plot_ly(data.frame(x=c(3, 4, 5), y=c(1000, 1100, 1200))) %>%
       add_lines(~x, ~y, legendgroup = 'group2', name = 'Trace 2')
p3 = plot_ly(data.frame(x=c(3, 4, 5), y=c(1000, 1100, 1200))) %>%
       add_lines(~x, ~y, legendgroup = 'group3', name = 'Trace 3')
subplot(p1,p2,p3,nrows = 3, shareX = TRUE) %>%
  layout(legend=list(tracegroupgap=150))

enter image description here

Related