Subplots and secondary y axes

Plotly · Practical · 12 min

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=True link axes; vertical_spacing and horizontal_spacing set gaps as fractions;
  • column_widths=[0.7, 0.3] and row_heights=[...] for unequal sizes;
  • specs describes each cell: {"colspan": 2} to span, None for 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 grids
  • specs sets 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.

Check yourself

  1. You want bars for revenue and a line for growth rate on different y scales in one panel. What do you use?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: plotly.com/python/subplots/

Builds on

Unlocks

Nothing builds on this yet.