2

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)

1 Answer 1

3

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)

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks I think this probably works. Though would be nice to be able to do some type of universal search or be able to better delineate between the two fields visually in the dropdown.
Yeah I agree. If the column name could be passed in each of the dropdown options (e.g. Rick - Investor), then that would probably work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.