For the dash tables the sorting and filtering is done backend. I then use this filtered\sorted data for plotting charts etc.
It working in most scenarios except when i make subplots and try to sort the data the callbacks seems to not update the plots.
The specific callback from my scripts is as
@app.callback(Output(‘graph-69’, “figure”), [Input(‘table-paging-with-graph’, “data”)])
def plot_graphs_2(data):
The whole scripts is as follows:
app = dash.Dash(name)
app.config[‘suppress_callback_exceptions’]=True
styles = {
‘pre’: {
‘border’: ‘thin lightgrey solid’,
‘height’ : 700,
‘overflowX’: ‘scroll’,
‘overflowX’: ‘scroll’
}
}
df = pd.read_excel(‘Y:/Best-Execution/Users/Ankur/Z.13_Ergebnistool_v11.xlsm’, sheet_name= 1)
df[‘alloc_id’] = df[‘allocation_id’].astype(str) + ‘_abc’
df[‘trade_duration’] = df.end_date - df.start_date
df.loc[df.trade_duration >= datetime.timedelta(days = 2) , ‘trade_duration’] = ‘two_ormore’
df.loc[df.trade_duration == datetime.timedelta(days = 0) , ‘trade_duration’] = ‘sameday’
df.loc[df.trade_duration == datetime.timedelta(days = 1) , ‘trade_duration’] = ‘oneday’
df.loc[df.trade_duration == datetime.timedelta(days = 2) , ‘trade_duration’] = ‘twodays’
mi_choices = [‘MI Arrival’, ‘MI VWAP TD’, ‘MI Open’, ‘MI Close’, ‘MI DVWAP’]
groupby_choices = [‘ISIN’, ‘Country’, ‘Currrency Name’, ‘Relative Order Size’, ‘Participation Rate Average’, ‘Trade Duration’]
xaxis_choices = [‘Start Date’ , ‘End Date’, ‘Allocation Id’, ‘Broker’ , ‘Country’ , ‘ISIN’ , ‘Market Capitalization’, ‘Brokerage’]
PAGE_SIZE = 5
app.layout = html.Div(
className=“row”,
children=[
html.Div(html.H1(‘Visualization Tool: Equity(le Aktion)’, style={“textAlign”: “center”}), className = ‘twelve columns’),
html.Div(
dash_table.DataTable(
id=‘table-paging-with-graph’,
columns=[
{“name”: i, “id”: i} for i in (df.columns)
],
# page_current=0,
# page_size=20,
# page_action=‘custom’,
style_cell={
# all three widths are needed
‘minWidth’: ‘80px’, ‘width’: ‘80px’, ‘maxWidth’: ‘180px’,
‘whiteSpace’: ‘no-wrap’,
‘overflow’: ‘hidden’,
‘textOverflow’: ‘ellipsis’},
filter_action='custom',
filter_query='',
fixed_rows={ 'headers': True, 'data': 0 },
fixed_columns={ 'headers': True, 'data': 0 },
sort_action='custom',
sort_mode='multi',
style_data_conditional=[{'width': '80px'}],
virtualization = True,
page_action = 'none',
sort_by=[]
),
style={'height': 550, 'overflowY': 'scroll'},
className='six columns'
),
html.Div(
id='table-paging-with-graph-container',
className="six columns"
),
html.Div(html.P('MI Type:'),
className="three columns"
),
html.Div(dcc.Dropdown(id='mi-type', options=[{'label': i, 'value': i} for i in mi_choices],
value="MI DVWAP"),
className="three columns"
),
html.Div(html.P('Groupby Type:'),
className="three columns"
),
html.Div(dcc.Dropdown(id='groupby-type', options=[{'label': i, 'value': i} for i in groupby_choices],
value="Currrency Name"),
className="three columns"
),
html.Div(html.P('X Axis:'),
className="three columns"
),
html.Div(dcc.Dropdown(id='xaxis-type', options=[{'label': i, 'value': i} for i in xaxis_choices],
value="Start Date"),
className="three columns"
),
html.Div((dcc.Graph(id = 'graph-1')),
className="six columns"
),
html.Div(html.Pre(id='table-1', style=styles['pre']),
className="six columns"
),
html.Div((dcc.Graph(id = 'graph-69')),
className="six columns"
),
]
)
operators = [['ge ', ‘>=’],
['le ', ‘<=’],
['lt ', ‘<’],
['gt ', ‘>’],
['ne ', ‘!=’],
['eq ', ‘=’],
['contains '],
['datestartswith ']]
def split_filter_part(filter_part):
for operator_type in operators:
for operator in operator_type:
if operator in filter_part:
name_part, value_part = filter_part.split(operator, 1)
name = name_part[name_part.find(’{’) + 1: name_part.rfind(’}’)]
value_part = value_part.strip()
v0 = value_part[0]
if (v0 == value_part[-1] and v0 in ("'", '"', '`')):
value = value_part[1: -1].replace('\\' + v0, v0)
else:
try:
value = float(value_part)
except ValueError:
value = value_part
# word operators need spaces after them in the filter string,
# but we don't want these later
return name, operator_type[0].strip(), value
return [None] * 3
def generate_table(dataframe, max_rows=100000):
return html.Table(
# Header
[html.Tr([html.Th(col) for col in dataframe.columns])] +
# Body
[html.Tr([
html.Td(dataframe.iloc[i][col]) for col in dataframe.columns
]) for i in range(min(len(dataframe), max_rows))]
)
@app.callback(Output(‘table-paging-with-graph’, “data”),
[Input(‘table-paging-with-graph’, “sort_by”),
Input(‘table-paging-with-graph’, “filter_query”)])
def update_table(sort_by, filter):
filtering_expressions = filter.split(’ && ')
dff = df
for filter_part in filtering_expressions:
col_name, operator, filter_value = split_filter_part(filter_part)
if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'):
# these operators match pandas series operator method names
dff = dff.loc[getattr(dff[col_name], operator)(filter_value)]
elif operator == 'contains':
dff = dff.loc[dff[col_name].str.contains(filter_value)]
elif operator == 'datestartswith':
# this is a simplification of the front-end filtering logic,
# only works with complete fields in standard format
dff = dff.loc[dff[col_name].str.startswith(filter_value)]
if len(sort_by):
dff = dff.sort_values(
[col['column_id'] for col in sort_by],
ascending=[
col['direction'] == 'asc'
for col in sort_by
],
inplace=False
)
# fig = make_subplots(rows=6, cols=1, shared_xaxes=True, vertical_spacing=0.02, subplot_titles=("Volume","MI Arrival", "MI Close", "MI Open", "MI VWAP TD", "MI DVWAP"))
# fig.add_trace(go.Bar(x=dff["alloc_id"], y=dff['trading_volume_eur'], name = 'Volume €' , marker_color ='rgb(0, 24, 245)'),
# row=1, col=1)
# fig.add_trace(go.Bar(x=dff["alloc_id"], y=dff['mi_arrival'], name = 'MI mi_arrival' , marker_color ='rgb(0, 24, 245)'),
# row=2, col=1)
# fig.add_trace(go.Bar(x=dff["alloc_id"], y=dff['mi_close'], name = 'MI Close' , marker_color ='rgb(0, 24, 245)'),
# row=3, col=1)
# fig.add_trace(go.Bar(x=dff["alloc_id"], y=dff['mi_open'], name = 'MI Open' , marker_color ='rgb(0, 24, 245)'),
# row=4, col=1)
# fig.add_trace(go.Bar(x=dff["alloc_id"], y=dff['mi_vwap_td'], name = 'MI VWAP TD' , marker_color ='rgb(0, 24, 245)'),
# row=5, col=1)
# fig.add_trace(go.Bar(x=dff["alloc_id"], y=dff['mi_dvwap'], name = 'MI DVWAP' , marker_color ='rgb(0, 24, 245)'),
# row=6, col=1)
# fig.update_layout(height=1200, title_text="Stacked MI Subplots with Shared X-Axes", showlegend=False,)
return dff.to_dict('records')
@app.callback(Output(‘graph-69’, “figure”), [Input(‘table-paging-with-graph’, “data”)])
def plot_graphs_2(data):
dff = pd.DataFrame(data)
fig_1 = make_subplots(rows=6, cols=1, shared_xaxes=True, vertical_spacing=0.02, subplot_titles=(“Volume”,“MI Arrival”, “MI Close”, “MI Open”, “MI VWAP TD”, “MI DVWAP”))
fig_1.append_trace(go.Bar(x=dff["alloc_id"], y=dff['trading_volume_eur'], name = 'Volume €' , marker_color ='rgb(0, 24, 245)'),
row=1, col=1)
fig_1.append_trace(go.Bar(x=dff["alloc_id"], y=dff['mi_arrival'], name = 'MI mi_arrival' , marker_color ='rgb(0, 24, 245)'),
row=2, col=1)
fig_1.append_trace(go.Bar(x=dff["alloc_id"], y=dff['mi_close'], name = 'MI Close' , marker_color ='rgb(0, 24, 245)'),
row=3, col=1)
fig_1.append_trace(go.Bar(x=dff["alloc_id"], y=dff['mi_open'], name = 'MI Open' , marker_color ='rgb(0, 24, 245)'),
row=4, col=1)
fig_1.append_trace(go.Bar(x=dff["alloc_id"], y=dff['mi_vwap_td'], name = 'MI VWAP TD' , marker_color ='rgb(0, 24, 245)'),
row=5, col=1)
fig_1.append_trace(go.Bar(x=dff["alloc_id"], y=dff['mi_dvwap'], name = 'MI DVWAP' , marker_color ='rgb(0, 24, 245)'),
row=6, col=1)
fig_1.update_layout(height=1200, title_text="Stacked MI Subplots with Shared X-Axes", showlegend=False,)
return fig_1
@app.callback(Output(‘table-paging-with-graph-container’, “children”), [Input(‘table-paging-with-graph’, “data”)])
def plot_graphs(data):
dff = pd.DataFrame(data)
return html.Div([
dcc.Graph(
id=column,
figure={
"data": [
{
"x": dff["alloc_id"],
"y": dff[column] if column in dff else [],
"type": "bar",
"anchor": 'x',
"marker": {"color": "#0074D9"},
}
],
"layout": {
"xaxis": {"automargin": True},
"yaxis": {"automargin": True, 'title' : column},
"anchor": 'x',
"height": 300,
"margin": {"t": 15, "l": 10, "r": 10},
},
},
)
for column in ['trading_volume_eur', 'mi_arrival', 'mi_vwap_td', 'mi_open', 'mi_close', 'mi_dvwap']
]
)
@app.callback(Output(“graph-1”, “figure”), [Input(‘mi-type’, ‘value’), Input(‘xaxis-type’, ‘value’), Input(‘groupby-type’, ‘value’), Input(‘table-paging-with-graph’, “data”)])
def make_figure(mi_type, xaxis, groupby, data):
df = pd.DataFrame(data)
active_column = mi_type
if active_column == 'MI Arrival':
active_column = 'mi_arrival'
if active_column =='MI VWAP TD':
active_column = 'mi_vwap_td'
if active_column == 'MI Open':
active_column = 'mi_open'
if active_column == 'MI Close':
active_column = 'mi_close'
if active_column == 'MI DVWAP':
active_column = 'mi_dvwap'
xaxis_column = xaxis
if xaxis_column == 'Start Date':
xaxis_column = 'start_date'
elif xaxis_column =='End Date':
xaxis_column = 'end_date'
elif xaxis_column == 'Allocation Id':
xaxis_column = 'alloc_id'
elif xaxis_column == 'Broker':
xaxis_column = 'execution_broker'
elif xaxis_column == 'Country':
xaxis_column = 'country'
elif xaxis_column == 'ISIN':
xaxis_column = 'isin'
elif xaxis_column == 'Currency':
xaxis_column = 'currency_name'
elif xaxis_column == 'Market Capitalization':
xaxis_column = 'market_cap in EUR'
elif xaxis_column == 'Brokerage':
xaxis_column = 'brokerage'
groupby_column = groupby
if groupby_column == 'ISIN':
groupby_column = 'isin'
elif groupby_column =='Country':
groupby_column = 'country'
elif groupby_column == 'Currrency Name':
groupby_column = 'currency_name'
elif groupby_column == 'Relative Order Size':
groupby_column = 'rel_order_size'
elif groupby_column == 'Participation Rate Average':
groupby_column = 'part_rate_average'
elif groupby_column == 'Participation Rate Average':
groupby_column = 'part_rate_average'
elif groupby_column == 'Trade Duration':
groupby_column = 'trade_duration'
return px.scatter(
df,
x=xaxis_column,
y= active_column,
color=groupby_column,
size = 'trading_volume_eur',
hover_name = 'isin',
hover_data = ['allocation_id' , 'end_date'],
height=700,
)
@app.callback(Output(‘table-1’, ‘children’), [Input(‘graph-1’, “selectedData”)])
def table_output(selectedData):
return json.dumps(selectedData, sort_keys=True, allow_nan=True, indent=4)
if name == ‘main’:
app.run_server(debug=True)