Reputation: 10022
I am still designing what kind of plot with subplots I want to do but when we see the example in the documentation Multiple Subplots with Titles we have
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig = make_subplots(
rows=2, cols=2,
subplot_titles=("Plot 1", "Plot 2", "Plot 3", "Plot 4"))
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6]),
row=1, col=1)
fig.add_trace(go.Scatter(x=[20, 30, 40], y=[50, 60, 70]),
row=1, col=2)
fig.add_trace(go.Scatter(x=[300, 400, 500], y=[600, 700, 800]),
row=2, col=1)
fig.add_trace(go.Scatter(x=[4000, 5000, 6000], y=[7000, 8000, 9000]),
row=2, col=2)
fig.update_layout(height=500, width=700,
title_text="Multiple Subplots with Titles")
fig.show()
which gives
which is fine but notice that there is only one place for the legends.(trace 0, trace 1.etc)
In my design the upper left plot has one legend and the other three share some other legend . Is there a way to individualize or customize the legends of subplots?
Upvotes: 4
Views: 17070
Reputation: 3820
Here's a solution using the same technique as the existing answer - creating a new legend for the traces of each subplot and positioning it accordingly - that also handles row-based positioning, and manages subplots with varying numbers of rows and columns, and adjusts for the horizontal and vertical spacing between subplots.
from plotly.subplots import make_subplots
import plotly.graph_objects as go
from itertools import product
import numpy as np
rows = 3
cols = 5
horizontal_spacing = 0.05
vertical_spacing = 0.1
legend_horizontal_spacing = -0.005 # vertical buffer between legend and subplot
subplot_height = (1 - (rows - 1) * vertical_spacing) / rows
subplot_width = (1 - (cols - 1) * horizontal_spacing) / cols
fig = make_subplots(rows=rows, cols=cols,
vertical_spacing=vertical_spacing,
horizontal_spacing=horizontal_spacing,
# shared_yaxes=True,
shared_xaxes=True
)
for i, (row, col) in enumerate(product(range(1, rows+1), range(1, cols+1))): # rows & cols are 1-indexed
x_data = np.arange(100)
y_data = np.convolve(np.random.normal(0, 50, 100), np.ones(10), mode='same')
fig.append_trace(go.Scatter(x=x_data, y=y_data, name=f"Trace {i}"), row=row, col=col)
# Add second series to some subplots
if np.random.choice(3) == 0: # Condition to add a second series
y_data2 = np.convolve(np.random.normal(0, 50, 100), np.ones(10), mode='same')
fig.add_trace(go.Scatter(x=x_data, y=y_data2, name=f"Trace {i}b"), row=row, col=col)
legend_name = f"legend{i+2}" # legend1 is the theme's default. start at legend2 to avoid.
x = ((col - 1) * (subplot_width + horizontal_spacing)) + (subplot_width / 2)
y = 1 - ((row - 1) * (subplot_height + vertical_spacing)) + legend_horizontal_spacing
fig.update_traces(row=row, col=col, legend=legend_name)
fig.update_layout({legend_name: dict(x=x, y=y, xanchor='center', yanchor="bottom", bgcolor='rgba(0,0,0,0)')})
fig.update_layout(height=600, title_text="<b>Unique Legend Per Subplot Demonstration")
fig.show()
Upvotes: 3
Reputation: 19610
This topic was discussed on the plotly forum here, and it seems that multiple legends aren't possible in plotly. However, in the same thread @Jaydeep Mistry gives a partial workaround.
He uses legend groups to group traces together, then uses the parameter legend_tracegroupgap
in the update_layout
method to give the legend the appearance of being more than one legend. However, this spacing only works vertically so your multiple legends will still be vertically spaced apart on the right side of the plot. For example:
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig = make_subplots(
rows=2, cols=2,
subplot_titles=("Plot 1", "Plot 2", "Plot 3", "Plot 4"))
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6], legendgroup = '1'),
row=1, col=1)
fig.add_trace(go.Scatter(x=[20, 30, 40], y=[50, 60, 70], legendgroup = '2'),
row=1, col=2)
fig.add_trace(go.Scatter(x=[300, 400, 500], y=[600, 700, 800], legendgroup = '2'),
row=2, col=1)
fig.add_trace(go.Scatter(x=[4000, 5000, 6000], y=[7000, 8000, 9000], legendgroup = '2'),
row=2, col=2)
fig.update_layout(height=500, width=700,
title_text="Multiple Subplots with Titles",
legend_tracegroupgap=180)
fig.show()
Alternatively, you could add another legend by using an annotation to draw a box with the accompanying text, but it wouldn't have any functionality.
Upvotes: 9