Update_yaxes behavior when using make_subplots vs specifying axes directly

To my knowledge, there’s two main ways to create subplots:

  1. make_subplots(), then add trace data - example here
  2. create list of data, inject into go.Figure() - example here (see hover on subplots)

I’m not sure which one’s the preferred one, but update_yaxes affects them differently.

If we use method 1, the update_yaxes affects all yaxes as intended
If we use method 2, the update_yaxes only affects the first subplot

Can someone help me investigate why the update_yaxes doesn’t play well with the second way of creating subplots?

example code for method 1 - using subplots

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

df = data.stocks()


fig = make_subplots(rows=3, cols=1, shared_xaxes=True, vertical_spacing=0.03)
layout = dict(
    hoversubplots="axis",
    title="Stock Price Changes",
    hovermode="x",
    grid=dict(rows=3, columns=1),
)

fig.add_trace(go.Scatter(x=df["date"], y=df["AAPL"], name="Apple"), row=1, col=1)
fig.add_trace(go.Scatter(x=df["date"], y=df["GOOG"], name="Google"), row=2, col=1)
fig.add_trace(go.Scatter(x=df["date"], y=df["AMZN"], name="Amazon"), row=3, col=1)
fig.update_layout(layout)
fig.update_yaxes(dict(rangemode='tozero'))

fig.show()

example code for method 2 - specifying axis directly

import plotly.graph_objects as go
import pandas as pd
from plotly import data

df = data.stocks()

layout = dict(
    hoversubplots="axis",
    title=dict(text="Stock Price Changes"),
    hovermode="x",
    grid=dict(rows=3, columns=1),
)

data = [
    go.Scatter(x=df["date"], y=df["AAPL"], xaxis="x", yaxis="y", name="Apple"),
    go.Scatter(x=df["date"], y=df["GOOG"], xaxis="x", yaxis="y2", name="Google"),
    go.Scatter(x=df["date"], y=df["AMZN"], xaxis="x", yaxis="y3", name="Amazon"),
]

fig = go.Figure(data=data, layout=layout)
fig.update_yaxes(dict(rangemode='tozero'))
fig.show()

The property I’m playing with is the yaxes’ rangemode, where I’d like all subplots to have a lower y value to be 0, the first method modified all three subplots, the second one modified the first subplot only

In this case, there are three Y-axes, each of which must be specified.

fig.update_layout(yaxis=dict(rangemode="tozero"))
fig.update_layout(yaxis2=dict(rangemode="tozero"))
fig.update_layout(yaxis3=dict(rangemode="tozero"))

I do not understand why we have to specify this manually, feels wrong.

fig.update_yaxes()
fig.for_each_yaxis(lambda x: x.update(rangemode="tozero")

should work IMHO. It seems that subplots uses the yaxes differently.

@RSDummy if you want to dig deeper into this, you migt compare the figure structure oft the two figures fig.to_dict()

1 Like

The current confusion at hand is the inconsistent behavior. update_yaxes’ documentation states that the supposed behavior is to modify all y-axes at once, link

“By default, these methods apply to all of the x axes or y axes in the figure. The row and col arguments can be used to control which axes are targeted by the update.”

method 1’s fig.layout

method 2’s fig.layout

The function is able to recognise that it needs to modify yaxis, yaxis2, etc, but only when creating subplots via method 1.

@RSDummy, would you mind creating an issue on gitHub?

I’ve opened an issue on github, I hope the format is legible!

1 Like