Skip to content

Instantly share code, notes, and snippets.

@clane9
Created November 21, 2022 15:57
Show Gist options
  • Save clane9/9b58f778c39560cf49a87cbb971795bc to your computer and use it in GitHub Desktop.
Save clane9/9b58f778c39560cf49a87cbb971795bc to your computer and use it in GitHub Desktop.
Fixed generic crossfilter recipe for Plotly Dash
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