Last active
January 31, 2024 15:13
-
-
Save nite/aff146e2b161c19f6d553dc0a4ce3622 to your computer and use it in GitHub Desktop.
Plotly Dash Crossfilter
This file contains hidden or 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
import dash | |
import dash_core_components as dcc | |
import dash_html_components as html | |
import pandas as pd | |
import plotly.graph_objs as go | |
from dash.dependencies import Output, Input, State | |
import dash_table_experiments as dt | |
from plotly.graph_objs.layout import Margin | |
app = dash.Dash(static_folder='static') | |
app.css.append_css({'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'}) | |
app.css.append_css({'external_url': '/static/styles.css'}) | |
server = app.server | |
try: | |
df = pd.read_pickle('/var/tmp/flights.pkl') | |
except: | |
df = pd.read_csv( | |
'http://square.github.io/crossfilter/flights-3m.json', | |
parse_dates=['date'], | |
date_parser=lambda s: pd.datetime.strptime(s, '%m%d%H%M')) | |
df.to_pickle('/var/tmp/flights.pkl') | |
df['time'] = df.date.dt.time | |
df = df.sample(frac=0.01) | |
def hist(df, filtered_df, col, selected_points, quantile=None, size=None): | |
filtered_serie = get_serie(col, filtered_df, quantile) if quantile else filtered_df[col] | |
serie = get_serie(col, df, quantile) if quantile else df[col] | |
min_x, max_x = serie.min(), serie.max() | |
if not size: | |
size = (max_x - min_x) / 25 | |
xbins = dict(start=min_x, end=max_x, size=size) | |
if selected_points: | |
selected_points = selected_points[int(len(selected_points) / 2)] | |
data = [ | |
go.Histogram( | |
x=serie, | |
# autobinx=True, | |
xbins=xbins, | |
marker=dict( | |
color='#FFD7E9', | |
), | |
selectedpoints=selected_points | |
), | |
go.Histogram( | |
x=filtered_serie, | |
xbins=xbins | |
), | |
] | |
layout = go.Layout( | |
margin=Margin( | |
l=5, | |
r=5, | |
b=50, | |
t=50, | |
pad=4 | |
), | |
bargap=0.1, | |
barmode='overlay', | |
dragmode='select', | |
selectdirection='h', | |
title=col, | |
showlegend=False | |
) | |
return dcc.Graph( | |
id=col + '-graph', | |
figure={ | |
'data': data, | |
'layout': layout | |
} | |
) | |
def get_serie(col, filtered_df, quantile): | |
mask = filtered_df[col].between( | |
filtered_df[col].quantile(quantile), | |
filtered_df[col].quantile(1 - quantile)) | |
return filtered_df[mask][col] | |
def get_plots(df, filtered_df, delay_select=None, distance_select=None, date_selection=None): | |
return html.Div(id='stuff', children=[ | |
html.Div(id='plots', className='plots', children=[ | |
hist(df, filtered_df, 'delay', delay_select, 0.05), | |
hist(df, filtered_df, 'distance', distance_select, 0.05), | |
# todo: this should be a normal bar chart, not histogram | |
hist(df, filtered_df, 'date', date_selection), | |
# hist(df, filtered_df, 'time', distance_select, size=5000), | |
]), | |
html.Div('%s / %s' % (filtered_df.shape[0], df.shape[0])), | |
html.Div(id='delay-selection', children=delay_select), | |
html.Div(id='distance-selection', children=distance_select), | |
html.Div(id='date-selection', children=date_selection), | |
dt.DataTable( | |
rows=filtered_df.to_dict('records'), | |
row_selectable=True, | |
filterable=True, | |
sortable=True, | |
selected_row_indices=[], | |
id='datatable' | |
) | |
]) | |
app.layout = html.Div(children=[ | |
html.H1(children='Plotly Crossfilter'), | |
html.Div(children=[ | |
"Plotly Dash reproduction of ", | |
html.A( | |
"http://square.github.io/crossfilter", | |
href="http://square.github.io/crossfilter/") | |
]), | |
html.Div(id='data-viz', children=get_plots(df, df)) | |
]) | |
@app.callback( | |
Output('data-viz', 'children'), | |
[ | |
Input('delay-graph', 'selectedData'), | |
Input('distance-graph', 'selectedData'), | |
Input('date-graph', 'selectedData'), | |
], | |
[ | |
State('delay-selection', 'children'), | |
State('distance-selection', 'children'), | |
State('date-selection', 'children'), | |
] | |
) | |
def plots(delay_select, distance_select, date_select, delay_selection, distance_selection, date_selection): | |
if delay_select or distance_select or date_select: | |
filtered_df = df.copy() | |
delay_selection = delay_select['range']['x'] if delay_select else delay_selection | |
distance_selection = distance_select['range']['x'] if distance_select else distance_selection | |
date_selection = date_select['range']['x'] if date_select else date_selection | |
if delay_selection: | |
filtered_df = filtered_df[filtered_df['delay'].between(*delay_selection)] | |
if distance_selection: | |
filtered_df = filtered_df[filtered_df['distance'].between(*distance_selection)] | |
if date_selection: | |
filtered_df = filtered_df[filtered_df['date'].between(*date_selection)] | |
else: | |
filtered_df = df | |
delay_selection = distance_selection = None | |
return get_plots(df, filtered_df, delay_selection, distance_selection, date_selection) | |
if __name__ == '__main__': | |
app.run_server(debug=True, port=1234) |
This file contains hidden or 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
pandas | |
dash | |
dash_renderer | |
dash_core_components | |
dash_html_components | |
dash_table_experiments | |
gunicorn |
This file contains hidden or 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
html, body { | |
height: 100%; | |
} | |
.content, .stuff { | |
height: 100%; | |
} | |
.plots { | |
height: 100%; | |
flex: auto; | |
display: flex; | |
border-top: 1px solid slategray; | |
} | |
.plots > div { | |
flex: 1; | |
height: 250px; | |
margin: 0; | |
} | |
/* Loading from https://codepen.io/mikesmith1611/pen/QOKgpG */ | |
@-webkit-keyframes spin { | |
0% { | |
-webkit-transform: rotate(0deg); | |
} | |
100% { | |
-webkit-transform: rotate(360deg); | |
} | |
} | |
@keyframes spin { | |
0% { | |
transform: rotate(0deg); | |
} | |
100% { | |
transform: rotate(360deg); | |
} | |
} | |
@keyframes fadein { | |
0% { | |
opacity: 0; | |
} | |
100% { | |
opacity: 1; | |
} | |
} | |
@-webkit-keyframes fadein { | |
0% { | |
opacity: 0; | |
} | |
100% { | |
opacity: 1; | |
} | |
} | |
._dash-loading-callback { | |
border: 16px solid #f3f3f3; | |
border-radius: 50%; | |
border-top: 16px solid #3498db; | |
width: 120px; | |
height: 120px; | |
margin-top: -60px; | |
margin-bottom: -60px; | |
display: inline-block; | |
position: fixed; | |
top: 50%; | |
left: 50%; | |
opacity: 0; | |
background: #ffffff; | |
-webkit-animation: fadein 1s linear forwards, spin 2s infinite linear; /* Safari and Chrome */ | |
animation: fadein 1s linear forwards, spin 2s infinite linear; | |
animation-delay: 1s; | |
-webkit-animation-delay: 1s; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment