Use UpdateMenu Buttons to Filter Data for a given value in a column of the underlying dataframe

Hi All,

I had a question recently answered where I had to use buttons in an updatemenu to update the underlying dataframe behind a plotly figure here. I’m still working on a similar set-up, this time with a px.bar , and I’m failing to get the same code working

import plotly.express as px
df = px.data.gapminder()
dfstart = df[df['year'] == 1967]
bar = px.bar(
                        x = dfstart["continent"],
                        color= dfstart["country"],
                        y= dfstart["pop"]
                        #log_y = False
                        )

df67 = df[df['year'] == 1967]
yearbuttons = [
    {'label': "67", 'method': 'update',
     'args': 
     [{'x': [df67['continent']], 
       'y': [df67['pop']], 
       'color': [df67['country']]
       },{'title': '1967'}]}
    ] 
bar.update_layout(
    updatemenus=[
        dict(
            type = "dropdown",
            direction = "down",
            buttons=yearbuttons,
            x=1,
            xanchor="left",
            y=1.3,
            yanchor="top",
            active = 0
        )
    ]
)
bar.show()

I’m expecting the update button to do nothing once selected ( except add a title), but it instead blows up the numbers to much larger than they should be. I’m planning to extend this example to have multiple buttons for different years/ point towards my own dataframe, but for now, I can’t understand what the issue is.
Any help would be greatly appreciated,
Thanks

hi @Ehard
Can you please add your code in preformatted text instead of Blockquote. Also, are you able to add the complete code of the app so that we can run it locally?

Hi Adam,

I’ve updated the formatting in the original post so hopefully it’s preformatted now.
I’ve tried to extract all of my app’s code from this chunk - so hopefully it should work in other user’s systems without any extra code. - let me know if I’ve missed anything

hi @Ehard
As you try more and more complex things with the updatemenus, it might become easier to just do it with Dash:

import plotly.express as px
from dash import Dash, Input, Output, callback, dcc, html

app = Dash(__name__)

df = px.data.gapminder()
dfstart = df[df['year'] == 1967]
df67 = df[df['year'] == 1967]

bar = px.bar(
            x = dfstart["continent"],
            color= dfstart["country"],
            y= dfstart["pop"]
            #log_y = False
            )

app.layout = html.Div([
    html.Button("1967", id='button-year'),
    dcc.Graph(figure=bar, id='bar-graph')
])

@callback(
    Output('bar-graph', 'figure'),
    Input('button-year', 'n_clicks'),
    prevent_initial_call=True
)
def update_title(n_clicks):
    fig = px.bar(x=df67["continent"],
                 color=df67["country"],
                 y=df67["pop"],
                 title='1967')
    return fig

if __name__ == "__main__":
    app.run_server(debug=True)

Hi Adam,
My final use for this is within a Dash dashboard. I have a callback which populates my px.bar figure, and updates the figure based on other components on my dashboard. This is all working well.

However I also want to have some filtering determined by an updatemenu which is permanently present on the figure ( which is what my example above is trying to recreate). Adding the title is simple - what I really want to do is have a different button for a set number of years and have the button determine the filtering, but I’m getting my values blown up in an unexpected way which can be seen in the sample code

This is probably a better example of what I’m trying to achieve:

import plotly.express as px
df = px.data.gapminder()
dfstart = df[df['year'] == 1967]
bar = px.bar(
                        x = dfstart["continent"],
                        color= dfstart["country"],
                        y= dfstart["pop"]
                        )

df67 = df[df['year'] == 1967]
df72 = df[df['year'] == 1972]

yearbuttons = [
    {'label': "67", 'method': 'update',
     'args': 
     [{'x': [df67['continent']], 
       'y': [df67['pop']], 
       'color': [df67['country']]
       },{'title': '1967'}]},
    {'label': "72", 'method': 'update',
     'args': 
     [{'x': [df72['continent']], 
       'y': [df72['pop']], 
       'color': [df72['country']]
       },{'title': '1972'}]}
    ] 
bar.update_layout(
    updatemenus=[
        dict(
            type = "dropdown",
            direction = "down",
            buttons=yearbuttons,
            x=1,
            xanchor="left",
            y=1.3,
            yanchor="top",
            active = 0
        )
    ]
)
bar.show()

That is weird. I might be missing something, because I can’t see why the button updates the y axis incorrectly. It adds too many countries under each continent, despite the fact that the filtered dataset (df67) is the same as the one used at the beginning (dfstart)

Definitely weird. I’ve tried to simplify my code to try and figure out what’s happening.

import plotly.express as px
df = px.data.gapminder()
dfstart = df.sort_values('pop',ascending = False)[:5] #[df['year'] == 1967]
bar = px.bar(
                        x = dfstart["continent"],
                        color= dfstart["country"],
                        y= dfstart["pop"]
                        )

top5 = df.sort_values('pop',ascending = False)[:5] #[df['year'] == 1967]
top3 = df.sort_values('pop',ascending = False)[:3] #[df['year'] == 1972]
print(top3)
yearbuttons = [
    {'label': "Top 5", 'method': 'update',
     'args': 
     [{'x': [top5['continent']], 
       'y': [top5['pop']], 
       'color': [top5['country']]
       },{'title': 'Top 5'}]},
    {'label': "Top 3", 'method': 'update',
     'args': 
     [{'x': [top3['continent']], 
       'y': [top3['pop']], 
       'color': [top3['country']]
       },{'title': 'Top 3'}]}
    ] 
bar.update_layout(
    updatemenus=[
        dict(
            type = "dropdown",
            direction = "down",
            buttons=yearbuttons,
            x=1,
            xanchor="left",
            y=1.3,
            yanchor="top",
            active = 0
        )
    ]
)
bar.show()

Here you can see the dataframe rows with the 5 highest values, which has 4 entries for china, and 1 for india. If you then filter it for the 3 highest values , you will find just 3 entries for china.
If you plot the graph and select the “Top 3” option, you will see 6 values, the 3for china, and then those same 3 values for India.
It looks like the y axis is being plot as the combination of colours in the original plot, and values in the updated args. There shouldn’t be any India in the “Top 3” as there is no India in the top3 dataframe.


Edit:
Further evidence that the color arg is messing it up - if you plot the original 1967/1972 plot without the color argument, then the updatemenu buttons work fine. Perhaps the buttons aren’t updating the color arg as expected, so it’s trying to merge the new y and old color in an unexpected way