I am trying to adapt the final plotly dash example on this page to my dataset and to include a dropdown filter. The idea is that I can select data on any of the three graphs and it will be highlighted on the others, with the dropdown providing a general filter for the dataasets.
Dataset structure:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 38124 entries, 0 to 38123
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Col1 38124 non-null float64
1 Col2 38124 non-null float64
2 Col3 38124 non-null float64
3 Col4 38124 non-null float64
4 Col5 38124 non-null float64
5 Col6 38124 non-null float64
6 Col7 38124 non-null float64
7 Col8 38124 non-null int64
8 Col9 38124 non-null float64
dtypes: float64(8), int64(1)
memory usage: 2.6 MB
When I just substituted my dataset into the example I could get the output I expected. However, when I start to include filtering based on Col2 in a dropdown, the behaviour of the output becomes quite strange. All of the data displays like it is unselected, then when I try to select data nothing happens.
My aim is that I will be able to filter the dataset on Col2 using the dropdown, and then have the behaviour demonstrated in the example. Any thoughts would be welcome.
Code below:
# Build App
app = JupyterDash(__name__)
app.layout = html.Div([
html.Div([
dcc.Dropdown(
id='dropdown', clearable=False, multi=True,
value=df['Col2'].unique(), options=[
{'label': f'{int(c)} bin', 'value': c}
for c in np.sort(df['Col2'].unique())
])
], style={'width': '30%',}),
html.Div([
dcc.Graph(id='graph1')
], style={'width': '33%', 'display': 'inline-block',}),
html.Div([
dcc.Graph(id='graph2')
], style={'width': '33%', 'display': 'inline-block',}),
html.Div([
dcc.Graph(id='graph3')
], style={'width': '33%', 'display': 'inline-block',}),
], style={'background-color': 'white'})
# Define plotting function
def get_figure(df, xcol, ycol, selectedpoints, selectedpoints_local):
if selectedpoints_local and selectedpoints_local['range']:
ranges = selectedpoints_local['range']
selection_bounds = {'x0': ranges['x'][0], 'x1': ranges['x'][1],
'y0': ranges['y'][0], 'y1': ranges['y'][1]}
else:
selection_bounds = {'x0': np.min(df[xcol]), 'x1': np.max(df[xcol]),
'y0': np.min(df[ycol]), 'y1': np.max(df[ycol])}
fig = px.scatter(df, x=df[xcol], y=df[ycol])
fig.update_traces(selectedpoints=selectedpoints,
customdata=df.index,
mode='markers',
unselected={'marker': { 'opacity': 0.2 }})
fig.update_layout(margin={'l': 20, 'r': 0, 'b': 15, 't': 5}, dragmode='select', hovermode=False)
fig.add_shape(dict({'type': 'rect',
'line': { 'width': 1, 'dash': 'dot', 'color': 'darkgrey' } },
**selection_bounds))
return fig
# Define callback
@app.callback(
Output('graph1', 'figure'),
Output('graph2', 'figure'),
Output('graph3', 'figure'),
[Input('dropdown', 'value'), Input('graph1', 'selectedData'),],
[Input('dropdown', 'value'), Input('graph2', 'selectedData'),],
[Input('dropdown', 'value'), Input('graph3', 'selectedData'),],
)
def callback(dd1, selection1, dd2, selection2, dd3, selection3,):
df_graph = df[(df['Col2'].isin(dd1)) & (df['Col1'] >= 40) & (df['Col1'] <= 65)]
selectedpoints = df_graph.index
trigger = callback_context.triggered[0]['prop_id'].split('.')[0]
if trigger == 'dropdown': # introduced this to 'reset' the selection everytime a new dropdown selection is made
selection1 = None
selection2 = None
selection3 = None
for selected_data in [selection1, selection2, selection3]:
if selected_data and selected_data['points']:
selectedpoints = np.intersect1d(selectedpoints,
[p['customdata'] for p in selected_data['points']])
return [get_figure(df_graph, "Col1", "Col3", selectedpoints, selection1),
get_figure(df_graph, "Col4", "Col3", selectedpoints, selection2),
get_figure(df_graph, "Col5", "Col3", selectedpoints, selection3,)]
app.run_server(mode='external')