Black Lives Matter. Please consider donating to Black Girls Code today.
Dash HoloViews is now available! Check out the docs.

Plotly/Dash leaving unexplained gaps in bar chart when Y contains negative and positive values

Edit: I solved this by adding category_orders={'Team' : list(dff['Team'])} as an argument to px.bar(). This worked because dff was already sorted in the way I wanted it to appear, so I could explicitly define the order using category_orders

Original question:
I am developing a web app that plots college basketball data scraped from KenPom.com (can be seen at kenpomgraphs.pythonanywhere.com). The first figure is a bar chart using px.bar(), and it is working as intended for the most part. However, when I select conferences that include both positive and negative values for Y, the figure leaves a huge gap between the positive and negative bars.

If I copy the exact same code into a jupyter notebook (replacing return [fig1] with fig1.show() ) I get the desired output.

Here is the code that produces the graph along with the callbacks that provide the arguments. df is a dataframe that contains all the data. I filter it down to dff based on the arguments provided to the function. I tried adding things like .reset_index() and .copy() thinking maybe it was a problem with the index or somehow referencing the unfiltered dataframe, but that has not changed the output.

@app.callback(
    [Output('fig1', 'figure')],
    [Input('stat-column', 'value'),
    Input('number-teams', 'value'),
    Input('conf', 'value')]
)

def update_figure_1(stat_column_name, number_teams, conf):
    if stat_column_name == 'AdjD' or stat_column_name == 'OppD':
        dff = df.loc[df['Conf'].isin(conf)].sort_values(by=stat_column_name, ascending=True).head(number_teams).reset_index().copy()
    else: dff = df.loc[df['Conf'].isin(conf)].sort_values(by=stat_column_name, ascending=False).head(number_teams).reset_index().copy()

    fig1 = px.bar(data_frame=dff,
                x='Team',
                y=stat_column_name,
                color='Conf',
                color_discrete_map=COLORS
                )
    fig1.update_traces(hovertemplate='%{x}: %{y}')
    fig1.update_traces(marker=dict(line=dict(
                                            width=2,
                                            color='DarkSlateGrey')))
    fig1.update_yaxes(range=[min(dff[stat_column_name]) - abs((min(dff[stat_column_name])*.2)), max(dff[stat_column_name])*1.15])
    if stat_column_name == 'AdjD' or stat_column_name == 'OppD' or stat_column_name == 'Rk':
        fig1.update_layout(xaxis_categoryorder = 'total ascending')
    else: fig1.update_layout(xaxis_categoryorder = 'total descending')
    fig1.update_layout(transition_duration=500)
    fig1.update_yaxes(title=stat_column_name)
    fig1.update_xaxes(title='')
    return [fig1]

I am deploying the app using pythonanywhere. When I execute the code in a bash console I can confirm that dff only contains the rows I wish to display, so I can’t understand why this gap is appearing, and why the same code does not produce the gap when run in a jupyter notebook.

Edit: I solved this by adding category_orders={'Team' : list(dff['Team'])} as an argument to px.bar()

I’m thinking the problem has to do with setting the xaxis_categoryorder. When I remove the following lines of code:

if stat_column_name == 'AdjD' or stat_column_name == 'OppD' or stat_column_name == 'Rk':
        fig1.update_layout(xaxis_categoryorder = 'total ascending')
else: fig1.update_layout(xaxis_categoryorder = 'total descending')

It eliminates the gap, but now the bars are grouped based on color like the image below. Is there another way to order the bars so they are all ascending or descending without introducing the previous gap?