Reputation: 968
I have a Python dashboard built using dash
, that I want to filter on either the Investor
or the Fund
column.
Investor Fund Period Date Symbol Shares Value
0 Rick Fund 3 2019-06-30 AVLR 3 9
1 Faye Fund 2 2015-03-31 MEG 11 80
2 Rick Fund 3 2018-12-31 BAC 10 200
3 Dre Fund 4 2020-06-30 PLOW 2 10
4 Faye Fund 2 2015-03-31 DNOW 10 100
5 Mike Fund 1 2015-03-31 JNJ 1 10
6 Mike Fund 1 2018-12-31 QSR 4 20
7 Mike Fund 1 2018-12-31 LBTYA 3 12
In other words, the user should be able to input one or more investors, and/or one or more Funds in the same filter field, and the dashboard will update accordingly. So I think I need to change:
options=[{'label': i, 'value': i} for i in df['Investor'].unique()]
to something like groupby
but am not positive? Here is my code:
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
data = {'Investor': {0: 'Rick', 1: 'Faye', 2: 'Rick', 3: 'Dre', 4: 'Faye', 5: 'Mike', 6: 'Mike', 7: 'Mike'},
'Fund': {0: 'Fund 3', 1: 'Fund 2', 2: 'Fund 3', 3: 'Fund 4', 4: 'Fund 2', 5: 'Fund 1', 6: 'Fund 1', 7: 'Fund 1'},
'Period Date': {0: '2019-06-30', 1: '2015-03-31', 2: '2018-12-31', 3: '2020-06-30', 4: '2015-03-31', 5: '2015-03-31', 6: '2018-12-31', 7: '2018-12-31'},
'Symbol': {0: 'AVLR', 1: 'MEG', 2: 'BAC', 3: 'PLOW', 4: 'DNOW', 5: 'JNJ', 6: 'QSR', 7: 'LBTYA'},
'Shares': {0: 3, 1: 11, 2: 10, 3: 2, 4: 10, 5: 1, 6: 4, 7: 3},
'Value': {0: 9, 1: 80, 2: 200, 3: 10, 4: 100, 5: 10, 6: 20, 7: 12}}
df = pd.DataFrame.from_dict(data)
def generate_table(dataframe, max_rows=100):
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 = dash.Dash()
app.layout = html.Div(
children=[html.H4(children='Investor Portfolio'),
dcc.Dropdown(
id='dropdown',
options=[{'label': i, 'value': i} for i in df['Investor'].unique()],
multi=True, placeholder='Filter by Investor or Fund...'),
html.Div(id='table-container')
])
@app.callback(dash.dependencies.Output('table-container', 'children'),
[dash.dependencies.Input('dropdown', 'value')])
def display_table(dropdown_value):
if dropdown_value is None:
return generate_table(df)
dff = df[df.Investor.str.contains('|'.join(dropdown_value))]
dff = dff[['Investor', 'Period Date', 'Symbol','Shares', 'Value']]
return generate_table(dff)
if __name__ == '__main__':
app.run_server(debug=True)
Upvotes: 2
Views: 4896
Reputation: 19545
I am admittedly pretty new to Dash, but from what I can tell, you can achieve what you want by extending your options list, and then using an or
condition in the dff
that you are displaying in the Dash App to include the Fund
column.
This is a bit brute force, and a nicer solution would be for Dash to know which columns your selected options are coming from. However, this would only be an issue if entries from different columns contained the same string (and here the unique values for Investor
and Fund
aren't ever the same).
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
data = {'Investor': {0: 'Rick', 1: 'Faye', 2: 'Rick', 3: 'Dre', 4: 'Faye', 5: 'Mike', 6: 'Mike', 7: 'Mike'},
'Fund': {0: 'Fund 3', 1: 'Fund 2', 2: 'Fund 3', 3: 'Fund 4', 4: 'Fund 2', 5: 'Fund 1', 6: 'Fund 1', 7: 'Fund 1'},
'Period Date': {0: '2019-06-30', 1: '2015-03-31', 2: '2018-12-31', 3: '2020-06-30', 4: '2015-03-31', 5: '2015-03-31', 6: '2018-12-31', 7: '2018-12-31'},
'Symbol': {0: 'AVLR', 1: 'MEG', 2: 'BAC', 3: 'PLOW', 4: 'DNOW', 5: 'JNJ', 6: 'QSR', 7: 'LBTYA'},
'Shares': {0: 3, 1: 11, 2: 10, 3: 2, 4: 10, 5: 1, 6: 4, 7: 3},
'Value': {0: 9, 1: 80, 2: 200, 3: 10, 4: 100, 5: 10, 6: 20, 7: 12}}
df = pd.DataFrame.from_dict(data)
def generate_table(dataframe, max_rows=100):
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 = dash.Dash()
app.layout = html.Div(
children=[html.H4(children='Investor Portfolio'),
dcc.Dropdown(
id='dropdown',
## extend the options to consider unique Fund values as well
options=[{'label': i, 'value': i} for i in df['Investor'].unique()] + [{'label': i, 'value': i} for i in df['Fund'].unique()],
multi=True, placeholder='Filter by Investor or Fund...'),
html.Div(id='table-container')
])
@app.callback(dash.dependencies.Output('table-container', 'children'),
[dash.dependencies.Input('dropdown', 'value')])
def display_table(dropdown_value):
if dropdown_value is None:
return generate_table(df)
## add an 'or' condition for the other column you want to use to slice the df
## and update the columns that are displayed
dff = df[df.Investor.str.contains('|'.join(dropdown_value)) | df.Fund.str.contains('|'.join(dropdown_value))]
dff = dff[['Investor', 'Fund', 'Period Date', 'Symbol','Shares', 'Value']]
return generate_table(dff)
if __name__ == '__main__':
app.run_server(debug=True)
Upvotes: 3