Plotly updatemenus clipping into subplots
Question:
I have a plotly figure with two subplots. I want the top subplot to use the updatemenus to switch between different options. The bottom plot is static. The issue I have is the first time a new option is selected the top chart expands to become as tall as both charts. Simplified code below.
Some other things I’ve tried:
- changing the method to
restyle
fixes the expanding top subplot but then doesn’t change the axis title, ideally I can have both.
- I’ve tried a few combination of other yaxis parameters (anchor, position) but nothing has worked so far. I’m guessing this is where the solution is but I’m not certain.
- I tried putting row_heights into the second argument of
args
but that had no effect
It seems like the row_height is defaulting back to 1 on an update but I can’t find the parameter to change it. Picture of what it looks like after changing from square to cube below.
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
def main():
figure = make_subplots(rows=2, cols=1, shared_xaxes=True)
df = pd.DataFrame([[1, 1, 1, 1], [2, 4, 8, 1.41], [3, 9, 27, 1.73]], columns=['num', 'square', 'cube', 'sqrt'])
figure.add_trace(go.Scatter(x=df['num'], y=df['square']), row=1, col=1)
figure.add_trace(go.Scatter(x=df['num'], y=df['sqrt']), row=2, col=1)
figure.update_yaxes(title='square', row=1, col=1)
figure.update_yaxes(title='sqrt', row=2, col=1)
buttons = []
options = ['square', 'cube']
for option in options:
buttons.append({'method': 'update',
'label': option,
'args': [{'y': [df[option]]},
{'yaxis': {'title': option}},
[0]
]
})
figure.update_layout(updatemenus=[dict(buttons=buttons, direction='down')])
figure.show()
if __name__ == '__main__':
main()
Answers:
After making a comment that just shows how you don’t have to be all that smart to answer questions on SO…sorry about that. Obviously, you’re right. I had to delete that comment, my pride just couldn’t handle it!!
Either way, the solution to your original question.
For restyling y
, you’ve nailed it.
For relayout
of the yaxis
, you need to know a few things:
- What did Plotly name the
yaxis
? Chances are, if it’s the second listed graph, the y-axis is named yaxis2
.
- What is the
yaxis.domain
for the affected graph?
- Is there a title to begin with? (You need to have a title to change it. The title can be
""
, though.)
For both of these needs, you can use the figure you’ve already created to get the information. Unless it’s an overwhelmingly complicated setup, it’s a safe guess on the name of the yaxis
.
To get the domain that you need for the buttons, create an object with that information before creating the buttons. You only need to collect the domain
of the yaxis
that is getting the new title.
y1dom = fig.layout.yaxis.domain
y2dom = fig.layout.yaxis2.domain
Here’s a simple but effective example that works. This updates the graph at the bottom.
x, y = ([1, 2, 3], [1, 3, 2])
fig = make_subplots(rows = 2, cols = 1, shared_xaxes = True)
fig.add_trace(go.Scatter(x = x, y = y, mode = "lines"), row = 1, col = 1)
fig.add_trace(go.Scatter(y = y, x = x, mode = "markers"), row = 2, col = 1)
y2dom = fig.layout.yaxis2.domain # collect the domain assigned in the subplot
fig.update_yaxes(title_text = "y-axis title of bottom subplot", row = 2, col = 1)
fig.update_layout(
updatemenus = [go.layout.Updatemenu(
buttons = list([
dict(
args = [{'y':[[1, 1, 1]]},
{'yaxis2':{'title': 'title', 'domain': y2dom}},
[1]],
method = 'update', label = 'update 1'),
dict(
args = [{'y':[[1, 3, 2]]},
{'yaxis2':{'title': 'different title', 'domain': y2dom}},
[1]],
method = 'update', label = 'update 2')
])
)])
fig.show()
The images are the original data, update 1, then update 2.
I have a plotly figure with two subplots. I want the top subplot to use the updatemenus to switch between different options. The bottom plot is static. The issue I have is the first time a new option is selected the top chart expands to become as tall as both charts. Simplified code below.
Some other things I’ve tried:
- changing the method to
restyle
fixes the expanding top subplot but then doesn’t change the axis title, ideally I can have both. - I’ve tried a few combination of other yaxis parameters (anchor, position) but nothing has worked so far. I’m guessing this is where the solution is but I’m not certain.
- I tried putting row_heights into the second argument of
args
but that had no effect
It seems like the row_height is defaulting back to 1 on an update but I can’t find the parameter to change it. Picture of what it looks like after changing from square to cube below.
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
def main():
figure = make_subplots(rows=2, cols=1, shared_xaxes=True)
df = pd.DataFrame([[1, 1, 1, 1], [2, 4, 8, 1.41], [3, 9, 27, 1.73]], columns=['num', 'square', 'cube', 'sqrt'])
figure.add_trace(go.Scatter(x=df['num'], y=df['square']), row=1, col=1)
figure.add_trace(go.Scatter(x=df['num'], y=df['sqrt']), row=2, col=1)
figure.update_yaxes(title='square', row=1, col=1)
figure.update_yaxes(title='sqrt', row=2, col=1)
buttons = []
options = ['square', 'cube']
for option in options:
buttons.append({'method': 'update',
'label': option,
'args': [{'y': [df[option]]},
{'yaxis': {'title': option}},
[0]
]
})
figure.update_layout(updatemenus=[dict(buttons=buttons, direction='down')])
figure.show()
if __name__ == '__main__':
main()
After making a comment that just shows how you don’t have to be all that smart to answer questions on SO…sorry about that. Obviously, you’re right. I had to delete that comment, my pride just couldn’t handle it!!
Either way, the solution to your original question.
For restyling y
, you’ve nailed it.
For relayout
of the yaxis
, you need to know a few things:
- What did Plotly name the
yaxis
? Chances are, if it’s the second listed graph, the y-axis is namedyaxis2
. - What is the
yaxis.domain
for the affected graph? - Is there a title to begin with? (You need to have a title to change it. The title can be
""
, though.)
For both of these needs, you can use the figure you’ve already created to get the information. Unless it’s an overwhelmingly complicated setup, it’s a safe guess on the name of the yaxis
.
To get the domain that you need for the buttons, create an object with that information before creating the buttons. You only need to collect the domain
of the yaxis
that is getting the new title.
y1dom = fig.layout.yaxis.domain
y2dom = fig.layout.yaxis2.domain
Here’s a simple but effective example that works. This updates the graph at the bottom.
x, y = ([1, 2, 3], [1, 3, 2])
fig = make_subplots(rows = 2, cols = 1, shared_xaxes = True)
fig.add_trace(go.Scatter(x = x, y = y, mode = "lines"), row = 1, col = 1)
fig.add_trace(go.Scatter(y = y, x = x, mode = "markers"), row = 2, col = 1)
y2dom = fig.layout.yaxis2.domain # collect the domain assigned in the subplot
fig.update_yaxes(title_text = "y-axis title of bottom subplot", row = 2, col = 1)
fig.update_layout(
updatemenus = [go.layout.Updatemenu(
buttons = list([
dict(
args = [{'y':[[1, 1, 1]]},
{'yaxis2':{'title': 'title', 'domain': y2dom}},
[1]],
method = 'update', label = 'update 1'),
dict(
args = [{'y':[[1, 3, 2]]},
{'yaxis2':{'title': 'different title', 'domain': y2dom}},
[1]],
method = 'update', label = 'update 2')
])
)])
fig.show()
The images are the original data, update 1, then update 2.