Black Lives Matter. Please consider donating to Black Girls Code today.

How to automatically adjust plot axis range after filtering data

Hi,

I’m kind of confused with Dash’s behavior when filtering data.
In few words, I have a bar plot, where each bar is a country. This bar plot is connected to a dropdown that filters out countries. But, even if I filter out the country associated to the maximum, the range won’t change. A couple images might help:

You see, after filtering out United States (even if I filter it by default), Dash still “remembers” that US was the maximum.

This is the relevant code:

html.Div(dcc.Dropdown(id='filter-testeos',
                      options=options_countries,
                      value=['United States'],
                      multi=True
                      )),
dcc.Graph(id='testeos')

And then:

@app.callback(Output(component_id='testeos', component_property='figure'),
              [Input(component_id='filter-testeos', component_property='value')])
def plot_testeos(countries):
    dte = data[~data.isin(countries)]
    fig = go.Bar(x=dte['country'], y=dte['cumulative_tests'])
    fig.update_yaxes(tickformat='.1%', row=3, col=1, autorange=True)
    return fig

My actual code is a 3x1 subplot grid, but I guess that doesn’t matter. Is there any method to make my y-axis update when changing the plotting data?

Hi @Juanouo the fact that you have subplots might still matter if your subplots have matching axes (as facet plots created by plotly.express), ie the range of the subplot is maybe imposed by another subplot? If this is not the case, could you share here a minimal standalone app so that we can reproduce easily the problem?

@Emmanuelle, I managed to create a minimal example:

import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objects as go
from dash.dependencies import Input, Output


app = dash.Dash(__name__)
server = app.server

df = pd.DataFrame(pd.DataFrame({'country': {0: 'Iceland',
  1: 'Bahrain',
  2: 'Norway',
  3: 'Estonia',
  4: 'Switzerland',
  5: 'Israel',
  6: 'Slovenia'},
 'test_million': {0: 102658.70704717531,
  1: 42367.37939352402,
  2: 23969.44609101287,
  3: 23037.322451160784,
  4: 22929.918218991366,
  5: 17042.22280880952,
  6: 16865.661240773756}}))


country_options = [{'label': i, 'value': i} for i in df.country.unique()]


app.layout = html.Div(children=[
    # Títulos

    # Gráfico testeos
    html.Div(dcc.Dropdown(id='filter-tests',
                          options=country_options,
                          value=['Iceland'],
                          multi=True
                          )),
    dcc.Graph(id='tests')
    ])


@app.callback(Output(component_id='tests', component_property='figure'),
              [Input(component_id='filter-tests', component_property='value')])
def plot_tests(countries):
    dte = df[~df.isin(countries)].sort_values('test_million', ascending=False)[['country', 'test_million']].head(5)
    fig = go.Figure()
    fig.add_trace(go.Bar(x=dte['country'], y=dte['test_million']))

    return fig

You’ll se even if Iceland comes filtered by default, the range of my plot is still based on Iceland’s value.

Do you know what might be happening?

Hi @Juanouo thanks for sharing a minimal app. If you print dte inside the callback you’ll see that it does not remove the line with Iceland it just replaces Iceland with a NaN value and the numerical value is still here, which is way the range of the plot does not change. I always do a lot of print inside callbacks when debugging :-). You can get the desired behaviour by doing instead

dte = df[~df['country'].isin(countries)].sort_values('test_million', ascending=False)[['country', 'test_million']].head(5)

Thanks @Emmanuelle! Didn’t expect that behavior, it surely is a good idea to check what’s happening behind the curtains, will have that in mind