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()

enter image description here

Asked By: Sobigen

||

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.

enter image description here

enter image description here

enter image description here

Answered By: Kat
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.