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

Dash DataTable - usage.py subplot sorts are borked if x axis looks like a number

Hi, I have been struggling for a while now to make use of the code in “usage.py”.


EDIT: I just needed to step away and come back to this it seems. Leaving this post since it has useful repro info. Problem description in post #2 on this thread.

I went quite a ways down the path of making the table selections feed a scatterbox map and managed to get all of that working, but after a while realized that my subplots were not honoring the sort criteria from the table. So, I went back and made my changes one at a time unti I found the point at which sorting broke. Steps to repro below:

I have usage.py alone in its own project dir(only .py file). the CSV input looks like the following:

"id","home-street","home-city","home-state","home-zip","original address","latitude","longitude"
"4","24424 Big Basin Way, Los Gatos CA 95033",NULL,NULL,NULL,"24424 Big Basin Way, Los Gatos CA 95033","37.2462272644043","-122.13957214355469"
"5","22400 Skyline Blvd Box 35","La Honda","CA","94020","22400 Skyline Blvd Box 35,La Honda,CA,94020","37.28997802734375","-122.18656158447266"
"6","11845 Skyline Blvd","Los Gatos","CA","95033","11845 Skyline Blvd,Los Gatos,CA,95033","37.27555465698242","-122.1490707397461"
"7","26160 Highway 9","Los Gatos","CA","95033","26160 Highway 9,Los Gatos,CA,95033","37.2505989074707","-122.13375091552734"
"10","23101 Highway 9","Boulder Creek","CA","95006","23101 Highway 9,Boulder Creek,CA,95006","37.22978210449219","-122.14895629882812"
"15","295 Snowcrest Road","Los Gatos","CA","95033","295 Snowcrest Road,Los Gatos,CA,95033","37.25581359863281","-122.12186431884766"
"16","20211 Beatty Ridge Road","Los Gatos","CA","95033","20211 Beatty Ridge Road,Los Gatos,CA,95033","37.20353698730469","-122.07523345947266"
"17","13520 Indian Trail Road","Los Gatos","CA","95033","13520 Indian Trail Road,Los Gatos,CA,95033","37.24345016479492","-122.12454986572266"
"1620","17287 Skyline Blvd. Box 102","Woodside","CA","94062","17287 Skyline Blvd. Box 102,Woodside,CA,94062","37.29869079589844","-122.17615509033203"
"1621","200 Oak Ridge Road","Los Gatos","CA","95033","200 Oak Ridge Road,Los Gatos,CA,95033","37.20759582519531","-122.05712127685547"
"1622","18584 Favre Ridge Road","Los Gatos","CA","95033","18584 Favre Ridge Road,Los Gatos,CA,95033","37.19740676879883","-122.0666732788086"
"1634","366 Woodland Ridge","Los Gatos","CA","95033","366 Woodland Ridge,Los Gatos,CA,95033","37.198394775390625","-122.04586029052734"
"1638","45 Big Tree Road","Woodside","CA","94062","45 Big Tree Road,Woodside,CA,94062","37.37629699707031","-122.25694274902344"
"1639","7691 Alpine Road","La Honda","CA","94020","7691 Alpine Road,La Honda,CA,94020","37.28936767578125","-122.22113037109375"
"1640","16456 Sanborn Road, Saratoga CA 95070",NULL,NULL,NULL,"16456 Sanborn Road, Saratoga CA 95070","37.22869873046875","-122.0571517944336"
"1646","75 Stadler Drive","Woodside","CA","94062","75 Stadler Drive,Woodside,CA,94062","37.390647888183594","-122.26724243164062"
"1649","1 Blakewood Way","Woodside","CA","94062","1 Blakewood Way,Woodside,CA,94062","37.387176513671875","-122.26751708984375"
"1653","15799 Skyline Blvd, Los Gatos CA 95033",NULL,NULL,NULL,"15799 Skyline Blvd, Los Gatos CA 95033","37.22272872924805","-122.09197235107422"
"1659","19050 Skyline Boulevard","Los Gatos","CA","95033","19050 Skyline Boulevard,Los Gatos,CA,95033","37.19314193725586","-122.03570556640625"
"1661","16035 Redwood Lodge Road","Los Gatos","CA","95033","16035 Redwood Lodge Road,Los Gatos,CA,95033","37.110347747802734","-121.9502182006836"
"1668","3500 Portola Heights Road","La Honda","CA","94020","3500 Portola Heights Road,La Honda,CA,94020","37.28810119628906","-122.16039276123047"
"1674","18034 Idalyn Drive, Los Gatos CA 95033",NULL,NULL,NULL,"18034 Idalyn Drive, Los Gatos CA 95033","37.15727996826172","-121.98714447021484"
"1675","18680 Favre Ridge Road","Los Gatos","CA","95033","18680 Favre Ridge Road,Los Gatos,CA,95033","37.19402313232422","-122.06301879882812"
"1677","12 Medway Road","Woodside","CA","94062","12 Medway Road,Woodside,CA,94062","37.37759017944336","-122.25531005859375"
"1679","105 Sunrise Drive","Woodside","CA","94062","105 Sunrise Drive,Woodside,CA,94062","37.39419174194336","-122.26201629638672"
"1680","18200 Skyline Blvd.","Woodside","CA","94062","18200 Skyline Blvd.,Woodside,CA,94062","37.372859954833984","-122.2579345703125"
"1682","18474 Grizzly Rock Road","Los Gatos","CA","95033","18474 Grizzly Rock Road,Los Gatos,CA,95033","37.200408935546875","-122.06729888916016"
"1687","RR2 Box 435","La Honda","CA","94020","131 Rapley Ranch Rd.La Honda,CA,94020","37.33277893066406","-122.23212432861328"

I went back to the original usage.py and started switching what was needed for my dataset and aside from picking up the local CSV file from the environment it seems that the only changes needed to go from working, sorting subplots to non-sorting subplots were the two changes marked in the code below with -<SWITCH>-.

I am posting the code in its non-working state below. To make it work with a local copy of the gapminder csv you only need to limit the dataset to 2007(first -<SWITCH>-) and swap out the subplot specs (second -<SWITCH>-).

Note that I also tried setting the id column in my data to userid in case there was some sort of collision somewhere, but that had no effect. I also tried changing the labels in my data to “country”, “pop”, and so on – no effect.

I have tried dropping nulls with .dropna(), converting ints to strings and strings to ints, etc. etc.

I placed a print() in the update_figure() callback method to confirm that this method is receiving the data sorted per the table clicks. Somehow the subplots are rendered sorted by the x-axis values regardless of how the input is sorted. The example usage.py from github

One last note, in the subplots I am using “latitude” twice, because my longitude values are negative and setting “yaxis3” to “log” doesn’t make sense for that data. This is, again, in the spirit of changing as little as possible from the working example as I narrow/repro the problem. Any help in this would be greatly appreciated.

import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table_experiments as dt
import json
import pandas as pd
import numpy as np
import plotly
import os
ENV = os.environ

DEBUG_HOST = ENV['HOSTNAME']
print(f"Debug Host: {DEBUG_HOST}")

if DEBUG_HOST:  # Setup for remote debugger
    from dbgp.client import brkOnExcept, brk  # Ignore pylint complaint about "brk"
    brkOnExcept(host=DEBUG_HOST, port=9000)
    '''
    Insert the following line anywhere in code to invoke remote debugger:
    brk(host=DEBUG_HOST, port=9000)
    '''

app = dash.Dash()

app.scripts.config.serve_locally = True
# app.css.config.serve_locally = True

#DF_WALMART = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/1962_2006_walmart_store_openings.csv')

# DF_MEMBERSHIP = pd.read_csv(
#     'https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv'
# )

DF_MEMBERSHIP = pd.read_csv(ENV['INPUT_DATA'])
#-<SWITCH>-DF_MEMBERSHIP = DF_MEMBERSHIP[DF_MEMBERSHIP['year'] == 2007]
DF_MEMBERSHIP = DF_MEMBERSHIP.loc[20:70]
#DF_MEMBERSHIP = DF_MEMBERSHIP[["id","home-street","home-city","home-state","home-zip","latitude","longitude"]]
print(DF_MEMBERSHIP.head())


app.layout = html.Div([
    html.H4('Membership DataTable'),
    dt.DataTable(
        rows=DF_MEMBERSHIP.to_dict('records'),

        # optional - sets the order of columns
        #columns=sorted(DF_MEMBERSHIP.columns),

        row_selectable=True,
        filterable=True,
        sortable=True,
        selected_row_indices=[],
        id='datatable-membership'
    ),
    html.Div(id='selected-indexes'),
    dcc.Graph(
        id='graph-membership'
    ),
], className="container")

@app.callback(
    Output('datatable-membership', 'selected_row_indices'),
    [Input('graph-membership', 'clickData')],
    [State('datatable-membership', 'selected_row_indices')])
def update_selected_row_indices(clickData, selected_row_indices):
    if clickData:
        for point in clickData['points']:
            if point['pointNumber'] in selected_row_indices:
                selected_row_indices.remove(point['pointNumber'])
            else:
                selected_row_indices.append(point['pointNumber'])
    return selected_row_indices


@app.callback(
    Output('graph-membership', 'figure'),
    [Input('datatable-membership', 'rows'),
     Input('datatable-membership', 'selected_row_indices')])
def update_figure(rows, selected_row_indices):
    dff = pd.DataFrame(rows)
    print(f'figure:\n{dff.head()}')
    fig = plotly.tools.make_subplots(
        rows=3, cols=1,
        subplot_titles=('Life Expectancy', 'GDP Per Capita', 'Population',),
#        subplot_titles=('Home Zip', 'Latitude', 'Longitude',),
        shared_xaxes=True)
    marker = {'color': ['#0074D9']*len(dff)}
    for i in (selected_row_indices or []):
        marker['color'][i] = '#FF851B'
    fig.append_trace({
        'x': dff['id'],
        'y': dff['home-zip'],
        'type': 'bar',
        'marker': marker
    }, 1, 1)
    fig.append_trace({
        'x': dff['id'],
        'y': dff['latitude'],
        'type': 'bar',
        'marker': marker
    }, 2, 1)
    fig.append_trace({
        'x': dff['id'],
        'y': dff['latitude'],
        'type': 'bar',
        'marker': marker
    }, 3, 1)
    #-<SWITCH>-
    # fig.append_trace({
    #     'x': dff['country'],
    #     'y': dff['lifeExp'],
    #     'type': 'bar',
    #     'marker': marker
    # }, 1, 1)
    # fig.append_trace({
    #     'x': dff['country'],
    #     'y': dff['gdpPercap'],
    #     'type': 'bar',
    #     'marker': marker
    # }, 2, 1)
    # fig.append_trace({
    #     'x': dff['country'],
    #     'y': dff['pop'],
    #     'type': 'bar',
    #     'marker': marker
    # }, 3, 1)
    fig['layout']['showlegend'] = False
    fig['layout']['height'] = 800
    fig['layout']['margin'] = {
        'l': 40,
        'r': 10,
        't': 60,
        'b': 200
    }
#    fig['layout']['yaxis3']['type'] = 'log'
    #brk(host=DEBUG_HOST, port=9000)
    return fig


app.css.append_css({
    'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'
})

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

OK, so here is the root of the problem: I was feeding a number to the shared x axis for the subplots.

Seems like a reasonable thing to do. :frowning:

Even when I changed it to a string with DF_MEMBERSHIP.id.apply(str) (as well as a couple of other approaches) I was getting the same behavior. My “aha” moment came when I let the problem lie for a while, then when I came back to it I decided to force the matter in a more aggressive way… I prepended the ‘a’ character to all of the ids and now it works perfectly:

"id","home-street","home-city","home-state","home-zip","original address","latitude","longitude"
"a4","24424 Big Basin Way, Los Gatos CA 95033",NULL,NULL,NULL,"24424 Big Basin Way, Los Gatos CA 95033","37.2462272644043","-122.13957214355469"
"a5","22400 Skyline Blvd Box 35","La Honda","CA","94020","22400 Skyline Blvd Box 35,La Honda,CA,94020","37.28997802734375","-122.18656158447266"
"a6","11845 Skyline Blvd","Los Gatos","CA","95033","11845 Skyline Blvd,Los Gatos,CA,95033","37.27555465698242","-122.1490707397461"
"a7","26160 Highway 9","Los Gatos","CA","95033","26160 Highway 9,Los Gatos,CA,95033","37.2505989074707","-122.13375091552734"
"a10","23101 Highway 9","Boulder Creek","CA","95006","23101 Highway 9,Boulder Creek,CA,95006","37.22978210449219","-122.14895629882812"
"a15","295 Snowcrest Road","Los Gatos","CA","95033","295 Snowcrest Road,Los Gatos,CA,95033","37.25581359863281","-122.12186431884766"

I think this is a bug, or at least bears explaining in some example or doc why an axis can’t be a number. I’ll open an issue in the dash-table-experiments repo in the next day or so, will update this post with the issue ID when I do.

Opened issue #54 in dash-table-experients repo: https://github.com/plotly/dash-table-experiments/issues/54