Created
November 21, 2022 15:57
-
-
Save clane9/9b58f778c39560cf49a87cbb971795bc to your computer and use it in GitHub Desktop.
Fixed generic crossfilter recipe for Plotly Dash
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from dash import Dash, dcc, html | |
import numpy as np | |
import pandas as pd | |
from dash.dependencies import Input, Output | |
from dash.exceptions import PreventUpdate | |
import plotly.express as px | |
def get_figure(df, x_col, y_col, selectedpoints=None, selectedpoints_local=None, pad=0.04): | |
if selectedpoints is None: | |
selectedpoints = df.index | |
if selectedpoints_local and selectedpoints_local.get("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.loc[selectedpoints, x_col]) - pad, | |
'x1': np.max(df.loc[selectedpoints, x_col]) + pad, | |
'y0': np.min(df.loc[selectedpoints, y_col]) - pad, | |
'y1': np.max(df.loc[selectedpoints, y_col]) + pad, | |
} | |
# set which points are selected with the `selectedpoints` property | |
# and style those points with the `selected` and `unselected` | |
# attribute. see | |
# https://medium.com/@plotlygraphs/notes-from-the-latest-plotly-js-release-b035a5b43e21 | |
# for an explanation | |
fig = px.scatter(df, x=df[x_col], y=df[y_col], text=df.index) | |
fig.update_traces(selectedpoints=selectedpoints, | |
customdata=df.index, | |
mode='markers+text', marker={ 'color': 'rgba(0, 116, 217, 0.7)', 'size': 20 }, unselected={'marker': { 'opacity': 0.3 }, 'textfont': { 'color': 'rgba(0, 0, 0, 0)' }}) | |
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 | |
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] | |
app = Dash(__name__, external_stylesheets=external_stylesheets) | |
# make a sample data frame with 6 columns | |
np.random.seed(0) # no-display | |
df = pd.DataFrame({"Col " + str(i+1): np.random.rand(30) for i in range(6)}) | |
app.layout = html.Div([ | |
html.Div( | |
dcc.Graph( | |
id='g1', | |
figure=get_figure(df, "Col 1", "Col 2"), | |
config={'displayModeBar': False} | |
), | |
className='four columns' | |
), | |
html.Div( | |
dcc.Graph( | |
id='g2', | |
figure=get_figure(df, "Col 3", "Col 4"), | |
config={'displayModeBar': False} | |
), | |
className='four columns' | |
), | |
html.Div( | |
dcc.Graph( | |
id='g3', | |
figure=get_figure(df, "Col 5", "Col 6"), | |
config={'displayModeBar': False} | |
), | |
className='four columns' | |
) | |
], className='row') | |
# this callback defines 3 figures | |
# as a function of the intersection of their 3 selections | |
@app.callback( | |
Output('g1', 'figure'), | |
Output('g2', 'figure'), | |
Output('g3', 'figure'), | |
Input('g1', 'selectedData'), | |
Input('g2', 'selectedData'), | |
Input('g3', 'selectedData') | |
) | |
def callback(selection1, selection2, selection3): | |
selectedpoints = df.index | |
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']]) | |
break | |
else: | |
raise PreventUpdate | |
return [get_figure(df, "Col 1", "Col 2", selectedpoints, selection1), | |
get_figure(df, "Col 3", "Col 4", selectedpoints, selection2), | |
get_figure(df, "Col 5", "Col 6", selectedpoints, selection3)] | |
if __name__ == '__main__': | |
app.run_server(debug=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment