Search code examples
pythoncallbackdashboardplotly-dash

How to select dataset column from dropdown in Dash by Plotly in Python?


I'm hoping for some guidance on creating a dashboard using Dash by Plotly in Python. Although there is a lot of documentation and forums posts available online, I feel that they assume some knowledge that I may be missing, particularly in correctly using callbacks to implement interactivity.

What I am trying to achieve is a dashboard that will allow me to:

  1. Upload an Excel file
  2. Select a column from the Excel file
  3. Run a function to process data from that column
  4. Display resulting output data in a table

I have tried to create a minimal working example but have struggled. Here's what I've managed so far:

import base64
import io
import pandas as pd
import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State

## 1. Define overall dashboard layout

Dashboard = dash.Dash(__name__, external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css'])
Dashboard.layout = html.Div([
                             html.H1('Dashboard Prototype', style={'width':'100%', 'textAlign': 'center'}),
                             dcc.Upload(
                                       id       = 'Dashboard_Input',
                                       children = html.A('Select Files'),
                                       style    = {'width': '100%', 'height': '60px', 'lineHeight': '60px', 'borderWidth': '1px',
                                                   'borderStyle': 'dashed', 'borderRadius': '5px', 'textAlign': 'center', 'display': 'inline-block'},
                                       multiple = True
                                       ),
                             html.Br(),
                             html.Div(id='Dashboard_Dropdown'),
                             html.Br(),
                             html.Div(id='Dashboard_Output')
                           ])

## 2. Define functions to upload, process and present data

def selectData(contents, filename, date):

    content_type, content_string = contents.split(',')
    decoded = base64.b64decode(content_string)

    try:
        Raw_Data = pd.read_excel(io.BytesIO(decoded))
    except Exception as e:
        return html.Div(['There was an error processing this file.'])

    Dropdown = html.Div([                      
                         html.H4 (('File Name: ', filename), style={'width':'100%', 'textAlign': 'center'}),
                         html.Br(),
                         dcc.Dropdown(
                                      id          = 'Dataset_and_Column',
                                      options     = [{'label': i, 'value': i} for i in Raw_Data.columns],
                                      style       = {'width': '100%', 'height': '60px', 'lineHeight': '60px', 'borderWidth': '1px',
                                                     'borderStyle': 'dashed', 'borderRadius': '5px', 'textAlign': 'center', 'display': 'inline-block'},
                                      placeholder =  'Select Dataset Column'
                                     ),
                         html.Br(),
                        ])

    return Dropdown

def processData(Raw_Data, Column_Label):

    # DO SOME PROCESSING THAT RESULTS IN 'TABLE':
    Table      = Table
    HTML_Table = html.Div([ 
                           dash_table.DataTable(
                                                data         = Table.to_dict('records'),
                                                columns      = [{'name': i, 'id': i} for i in Table.columns],
                                                style_cell   = {'textAlign': 'center', 'width': '100px'}, 
                                                editable     = False,
                                                style_header = {'fontWeight': 'bold', 'textAlign': 'center'},
                                                style_table  = {'maxHeight': '50ex', 'width': '100%'}
                                               ),
                           html.Hr()
                         ])
    return HTML_Table

## 4. Define callback functions

Dashboard.config.suppress_callback_exceptions = True

@Dashboard.callback(
                    Output('Dashboard_Dropdown', 'children'),
                    [Input('Dashboard_Input'   , 'contents')],
                    [State('Dashboard_Input'   , 'filename'), State('Dashboard_Input', 'last_modified')]
                   )
def createDropdown(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
       Dropdown = [selectData(c, n, d) for c, n, d in zip(list_of_contents, list_of_names, list_of_dates)]
       return Dropdown

################################################################################
################################################################################

# THIS BIT DOESN'T WORK #
# Need to make this callback function take an input of Raw_Date and Column_Label (selected from the dropdown) to run the processData() function.

@Dashboard.callback(
                    Output('Dashboard_Output'  , 'children'),
                    [Input('Dataset_and_Column', 'contents'), Input('Dataset_and_Column', 'value')],
                   )
def processSelectedColumn(contents, value):

    # HTML_Table = processData(contents, value)

    return value#HTML_Table

    # ^^^^^^ Note that this allow 'value' to be printed on the dashboard but 
    # when the commented 'HTML_Table' bits are uncommented it fails

################################################################################
################################################################################

## 6. Run dashboard

if __name__ == '__main__':
    Dashboard.run_server(debug=True)

This allows me to select an Excel file and then print the selected column of the Pandas data frame created from it but I am unsure exactly how to connect it all together to run my processData() function on the selected column.

Any ideas on how I've not correctly used or connected up the callbacks, functions, etc.?


Solution

  • You'll need the processSelectedColumn callback to accept a value from the dropdown as an Input as well. It might also be possible/good to take the data from the upload as State instead of Input. That should give you the values you need to perform your desired calculation.