Dashboards and reports often need different chart types side by side, which Express facets cannot produce.
plotly.subplots.make_subplots(rows, cols) creates a figure with a grid of axes. Add traces to a cell with row= and col=:
fig = make_subplots(rows=1, cols=2, subplot_titles=("Revenue", "Users"))
fig.add_trace(go.Bar(x=m, y=rev), row=1, col=1)
fig.add_trace(go.Scatter(x=m, y=users), row=1, col=2)Options:
shared_xaxes=True/shared_yaxes=Truelink axes;vertical_spacingandhorizontal_spacingset gaps as fractions;column_widths=[0.7, 0.3]androw_heights=[...]for unequal sizes;specsdescribes each cell:{"colspan": 2}to span,Nonefor empty,{"type": "domain"}for pie-like traces that have no x/y axes,{"secondary_y": True}for a second y axis.
A secondary y axis puts two scales on one panel:
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(go.Bar(...), secondary_y=False)
fig.add_trace(go.Scatter(...), secondary_y=True)
fig.update_yaxes(title_text="Rate", secondary_y=True)In Plotly 7, overlaying y axes default to tickmode="sync", so both axes share gridline positions; set tickmode="auto" on the secondary axis for independent ticks.
Axis updates target cells with fig.update_xaxes(..., row=2, col=1). Internally the axes are named xaxis, xaxis2, yaxis3 ..., which you see in fig.layout.
Example
import plotly.graph_objects as go
from plotly.subplots import make_subplots
m = ["Q1", "Q2", "Q3", "Q4"]
fig = make_subplots(
rows=1, cols=2, column_widths=[0.65, 0.35],
specs=[[{"secondary_y": True}, {"type": "domain"}]],
subplot_titles=("Revenue and margin", "Revenue by channel"),
)
fig.add_trace(go.Bar(x=m, y=[10, 14, 13, 18], name="revenue"), row=1, col=1)
fig.add_trace(go.Scatter(x=m, y=[0.21, 0.24, 0.22, 0.27], name="margin"),
row=1, col=1, secondary_y=True)
fig.add_trace(go.Pie(labels=["web", "store"], values=[35, 20]), row=1, col=2)
fig.update_yaxes(title_text="Revenue ($M)", secondary_y=False)
fig.update_yaxes(title_text="Margin", tickformat=".0%", secondary_y=True)
print(fig.data[1].yaxis) # y2
print(fig.layout.yaxis2.overlaying) # y
print([round(v, 2) for v in fig.data[2].domain.x]) # [0.68, 0.94]: right column
fig.write_html("quarterly.html")Remember
make_subplots+add_trace(..., row=, col=)builds mixed-chart gridsspecssets cell types, spans and secondary y axes- Pie-like traces need
{'type': 'domain'}cells
Watch out for
- Adding a pie trace to a regular xy cell, which raises an error about the subplot type.