Skip to content

Instantly share code, notes, and snippets.

@nite
Last active January 31, 2024 15:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nite/aff146e2b161c19f6d553dc0a4ce3622 to your computer and use it in GitHub Desktop.
Save nite/aff146e2b161c19f6d553dc0a4ce3622 to your computer and use it in GitHub Desktop.
Plotly Dash Crossfilter
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)
pandas
dash
dash_renderer
dash_core_components
dash_html_components
dash_table_experiments
gunicorn
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