Hello,
I have a piece of code that selects a feature from a dropdown menu and then produces figure with n_columns * n_rows subplots.
Where n_columns is always 3 and n_rows is the number of unique classes for each feature and it is therefore variable.
This is working fine.
However, as I am not able to produce separate callbacks from subplots, I am trying to change the code so that it produces one figure (with three columns/subplots) for unique class for the selected feature.
Here is my code:
### CALLBACKS ###
@app.callback(
[Output('month-selection', 'figure')],
#[Output('nf', 'integer')],
[Output(str(f), 'figure') for f in range(nf+1)],
[Output('table-daily', 'data'),
Output('table-daily', 'columns')],
[Output('selected-date', 'children')],
[Input('month-dropdown', 'value')],
[Input('feat-dropdown', 'value')],
[Input('datepicker', 'value')]
)
def update_output(month_year, feature, cdate):
global df
#global nf
###################
### MONTHY DATA ###
###################
df2 = df.loc[df['trade_date'] == month_year]
df_plot = df2.assign(lh_count=df['lh']).groupby(['lh']).agg({'quote_amount_eur_abs':'sum', 'lh_count':'count'}).reset_index()
df_plot.columns = ['Status', 'Value Traded €', 'Nbr of Trades']
df_plot['% Value Traded'] = round(100 * df_plot['Value Traded €'] / df_plot['Value Traded €'].sum(), 3)
df_plot['% Trades'] = round(100 * df_plot['Nbr of Trades'] / df_plot['Nbr of Trades'].sum(), 3)
df_plot['Value Traded €'] = df_plot['Value Traded €'].round(2)
df_plot = df_plot[['Status', '% Trades', 'Nbr of Trades', '% Value Traded', 'Value Traded €']]
df_plot['colour'] = ''
df_plot.loc[df_plot['Status'] == 'A. Better', 'colour'] = 'darkorange'
df_plot.loc[df_plot['Status'] == 'B. Equal', 'colour'] = 'forestgreen'
df_plot.loc[df_plot['Status'] == 'C. Success', 'colour'] = 'darkslateblue'
df_plot.loc[df_plot['Status'] == 'D. Worse', 'colour'] = 'crimson'
df_plot['Nbr of Trades'] = df_plot['Nbr of Trades'].apply('{:,}'.format)
df_plot['Value Traded €'] = df_plot['Value Traded €'].apply('{0:,.2f}'.format)
outcome = ['A. Better', 'B. Equal', 'C. Success', 'D. Worse']
subplot_titles = ['Performance Summary — ' + month_year + '<br> <br>', '% Number of Trades — ' + month_year + '<br> <br>', '% Value Traded — ' + month_year + '<br> <br>']
specs = [[{'type': 'table'}, {'type': 'pie'}, {'type': 'pie'}]]
fig = make_subplots(rows=1, cols=3, specs=specs,
subplot_titles=subplot_titles)
fig.add_trace(
go.Table(
header=dict(values=['Status', '% Trades', 'Trades', '% Value', 'Value €'],
fill_color='darkorange',
align='left'),
cells=dict(values=[df_plot['Status'], df_plot['% Trades'], df_plot['Nbr of Trades'], df_plot['% Value Traded'], df_plot['Value Traded €']],
fill_color='white',
align='left')),
row=1, col=1
)
fig.add_trace(
go.Pie(labels=df_plot['Status'], values=df_plot['% Trades'], sort=False, pull=[0.1, 0.1, 0, 0], hole=.6, marker=dict(colors=df_plot['colour']), legendgroup = '1'),
row=1, col=2
)
fig.add_trace(
go.Pie(labels=df_plot['Status'], values=df_plot['% Value Traded'], sort=False, pull=[0.1, 0.1, 0, 0], hole=.6, marker=dict(colors=df_plot['colour']), legendgroup = '1'),
row=1, col=3
)
###
feats = df2[feature].unique().tolist()
nf = len(feats)
# nrows = len(feats)
nrows = 1
row = 1
specs = [[{'type': 'table'}, {'type': 'pie'}, {'type': 'pie'}]]
#subplot_titles = []
#for feat in feats:
# subplot_titles.extend(['Performance Summary ' + feature + ' ' + feat + ' <br> <br>', '% Number of Trades ' + feature + ' ' + feat + ' <br> <br>', '% Value Traded ' + feature + ' ' + feat + ' <br> <br>'])
#row = 1
fe = 0
figures = [{} for _ in range(nf+1)]
for feat in feats:
subplot_titles = ['Performance Summary ' + feature + ' ' + feat + ' <br> <br>', '% Number of Trades ' + feature + ' ' + feat + ' <br> <br>', '% Value Traded ' + feature + ' ' + feat + ' <br> <br>']
#figures[fe] = make_subplots(rows=nrows, cols=3, specs=specs, subplot_titles=subplot_titles)
figures[fe] = make_subplots(rows=nrows, cols=3, specs=specs, subplot_titles=subplot_titles)
df_temp = df2[df2[feature] == feat]
df_plotd = df_temp.assign(lh_count=df_temp['lh']).groupby(['lh']).agg({'quote_amount_eur_abs':'sum', 'lh_count':'count'}).reset_index()
df_plotd.columns = ['Status', 'Value Traded €', 'Nbr of Trades']
df_plotd['% Value Traded'] = round(100 * df_plotd['Value Traded €'] / df_plotd['Value Traded €'].sum(), 3)
df_plotd['% Trades'] = round(100 * df_plotd['Nbr of Trades'] / df_plotd['Nbr of Trades'].sum(), 3)
df_plotd = df_plotd[['Status', '% Trades', 'Nbr of Trades', '% Value Traded', 'Value Traded €']]
missing = list(set(outcome) - set(df_plotd['Status'].unique().tolist()))
for i in missing:
df_plotd.loc[df_plotd.shape[0]] = [i, 0, 0, 0, 0]
df_plotd = df_plotd.sort_values(by=['Status'], ascending=True)
df_plotd['colour'] = ''
df_plotd.loc[df_plotd['Status'] == 'A. Better', 'colour'] = 'darkorange'
df_plotd.loc[df_plotd['Status'] == 'B. Equal', 'colour'] = 'forestgreen'
df_plotd.loc[df_plotd['Status'] == 'C. Success', 'colour'] = 'darkslateblue'
df_plotd.loc[df_plotd['Status'] == 'D. Worse', 'colour'] = 'crimson'
df_plotd['Nbr of Trades'] = df_plotd['Nbr of Trades'].apply('{:,}'.format)
df_plotd['Value Traded €'] = df_plotd['Value Traded €'].apply('{0:,.2f}'.format)
figures[fe].add_trace(
go.Table(
header=dict(values=['Status', '% Trades', 'Trades', '% Value', 'Value €'],
fill_color='darkorange',
align='left'),
cells=dict(values=[df_plotd['Status'], df_plotd['% Trades'], df_plotd['Nbr of Trades'], df_plotd['% Value Traded'], df_plotd['Value Traded €']],
fill_color='lavender',
align='left')),
row=row, col=1
)
figures[fe].add_trace(
go.Pie(labels=df_plotd['Status'], values=df_plotd['% Trades'], sort=False, pull=[0.1, 0.1, 0, 0], hole=.6, marker=dict(colors=df_plotd['colour']), legendgroup = str(row)),
row=row, col=2
)
figures[fe].add_trace(
go.Pie(labels=df_plotd['Status'], values=df_plotd['% Value Traded'], sort=False, pull=[0.1, 0.1, 0, 0], hole=.6, marker=dict(colors=df_plotd['colour']), legendgroup = str(row)),
row=row, col=3
)
figures[fe].update_layout(title_x=0, title_y=1, height=300)
fe = fe + 1
#row = row + 1
#fig_det.update_layout(title_x=0, title_y=1, height=nrows*300, legend_tracegroupgap = 224)
###
df3 = df[df['trade_date_original'] == cdate].copy()
df3 = df3[['operation_id', 'input_time', 'broker_timestamp', 'booking_time', 'created_time', 'operation', 'broker', 'symbol', 'price',
'lowest_platform', 'lowest', 'highest_platform', 'highest', 'lh']]
columns = [{'name': col, 'id': col} for col in df3.columns]
data = df3.to_dict(orient='records')
#return fig, fig_det, data, columns, cdate.split('T')[0]
return fig, *figures, data, columns, cdate.split('T')[0]
calendar = [dmc.MantineProvider(
theme={
'fontFamily': 'Arial, Helvetica, sans-serif',
'primaryColor': 'orange',
},
inherit=True,
withGlobalStyles=True,
withNormalizeCSS=True,
children=[dmc.DatePicker(
id='datepicker',
label='Date Selection',
minDate=df['trade_date_original'].min(),
maxDate=df['trade_date_original'].max(),
value=df['trade_date_original'].max(),
clearable=False,
style={'width': 160})])]
##############
### LAYOUT ###
##############
button_style = {'background-color': 'darkorange', 'color': 'grey', 'fontWeight': 'bold', 'fontFamily': 'Arial, Helvetica, sans-serif', 'fontSize': 12, 'border': '0px', 'border-radius': '5%'}
app.layout = html.Div([
html.Div([html.Label('CRYPTO EXECUTION PERFORMANCE', style={'fontWeight': 'bold', 'fontFamily': 'Arial, Helvetica, sans-serif', 'color': 'darkorange', 'fontSize': 18})]),
html.Br(),
html.Div([html.Label('MONTHLY EVOLUTION', style={'fontWeight': 'bold', 'fontFamily': 'Arial, Helvetica, sans-serif', 'color': 'darkorange', 'fontSize': 16})]),
html.Div([
dcc.Graph(id='time-ntrans',
figure=fig1, responsive=True)], style={'display': 'inline-block'}),
html.Div([
dcc.Graph(id='time-volume',
figure=fig2, responsive=True)], style={'display': 'inline-block'}),
html.Div([html.Label('MONTHLY PERFORMANCE', style={'fontWeight': 'bold', 'fontFamily': 'Arial, Helvetica, sans-serif', 'color': 'darkorange', 'fontSize': 16})]),
html.Div([html.Label('Month Selection', style={'fontFamily': 'Arial, Helvetica, sans-serif', 'color': '#000039', 'fontSize': 14}),
html.Br(),
dcc.Dropdown(month_list, month_list[-1], id='month-dropdown', searchable=False, clearable=False, style=button_style)],
style={'width':'120px', 'margin-left': '20px', 'margin-right': '60px', 'margin-top': '10px', 'verticalAlign': 'top'}),
html.Div([dcc.Graph(id='month-selection', className='user-select')]),
html.Div([html.Label('DETAILED PERFORMANCE', style={'fontWeight': 'bold', 'fontFamily': 'Arial, Helvetica, sans-serif', 'color': 'darkorange', 'fontSize': 16})]),
html.Div([html.Label('Feature Selection', style={'fontFamily': 'Arial, Helvetica, sans-serif', 'color': '#000039', 'fontSize': 14}),
html.Br(),
dcc.Dropdown(columns, columns[3], id='feat-dropdown', searchable=False, clearable=False, style=button_style)],
style={'width':'120px', 'margin-left': '20px', 'margin-top': '10px', 'verticalAlign': 'top'}),
#html.Div([dcc.Graph(id='feat-selection', className='user-select')]),
#html.Div([html.Label(id='nf')]),
html.Div(*[dcc.Graph(id=str(f), figure={}) for f in range(nf + 1)]),
html.Div([html.Label('DAILY EVOLUTION', style={'fontWeight': 'bold', 'fontFamily': 'Arial, Helvetica, sans-serif', 'color': 'darkorange', 'fontSize': 16})]),
html.Div([
dcc.Graph(id='time-ntransd',
figure=fig3, responsive=True)], style={'display': 'inline-block'}),
html.Div([
dcc.Graph(id='time-volumed',
figure=fig4, responsive=True)], style={'display': 'inline-block'}),
html.Br(), html.Br(),
html.Div([html.Label('DAILY TABLE', style={'fontWeight': 'bold', 'fontFamily': 'Arial, Helvetica, sans-serif', 'color': 'darkorange', 'fontSize': 16}),
dmc.Text(id='selected-date', style={'fontFamily': 'Arial, Helvetica, sans-serif', 'color': 'white', 'fontSize': 14})]),
html.Div(calendar,
style={'margin-top': '-10px', 'margin-left': '20px', 'verticalAlign': 'top'}),
html.Br(),
html.Div([
dash_table.DataTable(id='table-daily',
style_header={'backgroundColor': 'darkorange',
'color': 'white',
'fontWeight': 'bold', 'fontFamily': 'Arial, Helvetica, sans-serif', 'fontSize': 12
},
style_data={'backgroundColor': 'white',
'color': 'slategray',
'fontFamily': 'Arial, Helvetica, sans-serif', 'fontSize': 12
},
style_filter = {
'color': 'LightGray',
'fontFamily': 'Arial, Helvetica, sans-serif', 'fontSize': 12
},
fill_width=False,
editable=True,
filter_action='native',
filter_options={'case': 'insensitive'},
sort_action='native',
sort_mode='multi',
#column_selectable='single',
row_selectable='single',
#selected_columns=[],
selected_rows=[],
selected_row_ids=[],
#fixed_rows={'headers': True},
page_action='none',
style_table={'height': '700px', 'width': '1600px', 'overflowY': 'auto'},
export_format='csv',
export_headers='display',
#page_current=0,
#page_size=10
)]),
])
The key issue is how to export a variable number of *figures at once. Specially given than nf is a local variable, which is not recognised either in the callback or by in the layout.