Creating an Interactive Data app using Plotly’s Dash | by Kyle | Towards Data Science

Let’s make an app to automatically plot data files. This is a walkthrough of creating a web app in python using plotly’s Dash, with an example we created at Kyso — the live version can be found here:
dash-app-dx9g2r0la6-8000.cloud.kyso.io
It’s an app for uploading any file with xy, xyyy, etc. coordinates and then automatically generating a plot.
Dash is a Python framework for building web applications. It is built on top of Flask, Plotly.js, React and React Js., enabling you to build dashboards & applications using pure Python. In this tutorial, I will introduce you to the fundamentals and will assume that you have prior experience with Plotly. If you are new to plotly, check out these beginner guides:
https://kyso.io/KyleOS/plotly-intro
https://kyso.io/KyleOS/cufflinks-intro
In order to start using Dash, you’ll need to install several packages.
The core dash backend.Dash front-endDash HTML componentsDash core componentsPlotly
The core dash backend.
Dash front-end
Dash HTML components
Dash core components
Plotly
Note that all of these are already pre-installed on Kyso’s workspaces if you would like to launch your app from Kyso!
pip install dash==0.21.1  pip install dash-renderer==0.13.0  pip install dash-html-components==0.11.0pip install dash-core-components==0.23.0  pip install plotly --upgrade
Dash apps are composed of two parts. The first part is the “layout” of the app and it describes what the application looks like. Dash provides Python classes for all of the visual components of the application, i.e to generate HTML content with Python. To use these classes, let’s import the dash_core_components and the dash_html_components libraries. Create a file named app.py with the following code:
import dashimport dash_core_components as dccimport dash_html_components as html
Like with Flask we can initialize Dash by calling the Dash class of dash. Once that is done we can create the layout for our application, using the Div class from the dash_html_components to create HTML Divs. Note that dash_html_componentsincludes all HTML tags for you to style the layout of your app.
In order to create a graph within our layout, we use the Graph class from dash_core_components. Graph renders interactive data visualizations using plotly.js. The Graph class expects a figure object with the data to be plotted and the layout details. Dash also allows you to do stylings such as changing the background color and text color. You can change the background by using the style attribute and passing an object with your specific color.
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']app = dash.Dash(__name__, external_stylesheets=external_stylesheets)colors = {    "graphBackground": "#F5F5F5",    "background": "#ffffff",    "text": "#000000"}app.layout = html.Div([    dcc.Upload(        id='upload-data',        children=html.Div([            'Drag and Drop or ',            html.A('Select Files')        ]),        style={            'width': '100%',            'height': '60px',            'lineHeight': '60px',            'borderWidth': '1px',            'borderStyle': 'dashed',            'borderRadius': '5px',            'textAlign': 'center',            'margin': '10px'        },        # Allow multiple files to be uploaded        multiple=True    ),    dcc.Graph(id='Mygraph'),    html.Div(id='output-data-upload')])
Take note that we’re setting ids for each component we’re creating, we’ll go through these in just a sec. The Dash upload component allows your app’s viewers to upload files. The app can access the contents of an uploaded file by listening to the content’s property of the dcc.Upload component, which is a base64 encoded string that contains the file’s contents, regardless of file-type.
So we’ve created the layout above; the upload box with some styling as well as the Graph that will be created when we drop in a file.
Ok so now there are a few things we need to do, we need to create functions for:
Reading in any file, whether it be csv, an excel file or tsv, and convert it into a Pandas DataFrame.Creating the graph when a new file is dropped.Printing the table of the data.
Reading in any file, whether it be csv, an excel file or tsv, and convert it into a Pandas DataFrame.
Creating the graph when a new file is dropped.
Printing the table of the data.
There are a few libraries we need for this:
import base64import datetimeimport ioimport plotly.graph_objs as goimport cufflinks as cfimport pandas as pdimport dash_table
So, to parse our targeted data file types:
def parse_data(contents, filename):    content_type, content_string = contents.split(',')    decoded = base64.b64decode(content_string)    try:        if 'csv' in filename:            # Assume that the user uploaded a CSV or TXT file            df = pd.read_csv(                io.StringIO(decoded.decode('utf-8')))        elif 'xls' in filename:            # Assume that the user uploaded an excel file            df = pd.read_excel(io.BytesIO(decoded))        elif 'txt' or 'tsv' in filename:            # Assume that the user upl, delimiter = r'\s+'oaded an excel file            df = pd.read_csv(                io.StringIO(decoded.decode('utf-8')), delimiter = r'\s+')    except Exception as e:        print(e)        return html.Div([            'There was an error processing this file.'        ])    return df
Using cufflinks, a wrapper for easing plotting with pandas and plotly, to plot our scalar graphs:
def update_graph(contents, filename):    fig = {        'layout': go.Layout(            plot_bgcolor=colors["graphBackground"],            paper_bgcolor=colors["graphBackground"])    }    if contents:        contents = contents[0]        filename = filename[0]        df = parse_data(contents, filename)        df = df.set_index(df.columns[0])        fig['data'] = df.iplot(asFigure=True, kind='scatter', mode='lines+markers', size=1)        table = html.Div([            html.H5(filename),            dash_table.DataTable(                data=df.to_dict('rows'),                columns=[{'name': i, 'id': i} for i in df.columns]            ),            html.Hr(),            html.Div('Raw Content'),            html.Pre(contents[0:200] + '...', style={                'whiteSpace': 'pre-wrap',                'wordBreak': 'break-all'            })        ])
And finally creating a table for each time a file is dropped in:
def update_table(contents, filename):    table = html.Div()    if contents:        contents = contents[0]        filename = filename[0]        df = parse_data(contents, filename)        table = html.Div([            html.H5(filename),            dash_table.DataTable(                data=df.to_dict('rows'),                columns=[{'name': i, 'id': i} for i in df.columns]            ),            html.Hr(),            html.Div('Raw Content'),            html.Pre(contents[0:200] + '...', style={                'whiteSpace': 'pre-wrap',                'wordBreak': 'break-all'            })        ])    return table
The second part of a Dash application involves callbacks that define the application’s interactivity. In order to do this, we need to import Input and Output from dash.dependencies:
from dash.dependencies import Input, Output, State
In this application we have 2 callbacks:
@app.callback(Output('Mygraph', 'figure'),            [                Input('upload-data', 'contents'),                Input('upload-data', 'filename')            ])
So above, our input is the upload component we created at the beginning and have bound it to a callback such that whenever a file is selected, it updates Mygraph which is our Graph component in real time. Dash provides a decorator @app which makes it possible to bind a callback function to our components. You'll notice in the full app.py that we use the decorator before we declare the update_graph function.
And below is our second callback with the same input (i.e. the files that are uploaded to our Upload component, and this time the output is the HTML Div containing our printed table version of the file’s data. Again, the decorator is called before we declare the update_table function.
@app.callback(Output('output-data-upload', 'children'),            [                Input('upload-data', 'contents'),                Input('upload-data', 'filename')            ])
In order to view our visualization, we need to run our web server just like in Flask. Remember Dash is built on top of Flask. Dash includes “hot-reloading”, this features is activated by default when you run your app with app.run_server(debug=True). This means that Dash will automatically refresh your browser when you make a change in your code.
if __name__ == '__main__':    app.run_server(debug=True, host='0.0.0.0', port=8000)
Next, move to the terminal and start the server by typing the code below: python app.py - this will start a new web server at http://localhost:8000/.
Now, putting everything together:
import base64import datetimeimport ioimport plotly.graph_objs as goimport cufflinks as cfimport dashfrom dash.dependencies import Input, Output, Stateimport dash_core_components as dccimport dash_html_components as htmlimport dash_tableimport pandas as pdexternal_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']app = dash.Dash(__name__, external_stylesheets=external_stylesheets)server = app.servercolors = {    "graphBackground": "#F5F5F5",    "background": "#ffffff",    "text": "#000000"}app.layout = html.Div([    dcc.Upload(        id='upload-data',        children=html.Div([            'Drag and Drop or ',            html.A('Select Files')        ]),        style={            'width': '100%',            'height': '60px',            'lineHeight': '60px',            'borderWidth': '1px',            'borderStyle': 'dashed',            'borderRadius': '5px',            'textAlign': 'center',            'margin': '10px'        },        # Allow multiple files to be uploaded        multiple=True    ),    dcc.Graph(id='Mygraph'),    html.Div(id='output-data-upload')])def parse_data(contents, filename):    content_type, content_string = contents.split(',')    decoded = base64.b64decode(content_string)    try:        if 'csv' in filename:            # Assume that the user uploaded a CSV or TXT file            df = pd.read_csv(                io.StringIO(decoded.decode('utf-8')))        elif 'xls' in filename:            # Assume that the user uploaded an excel file            df = pd.read_excel(io.BytesIO(decoded))        elif 'txt' or 'tsv' in filename:            # Assume that the user upl, delimiter = r'\s+'oaded an excel file            df = pd.read_csv(                io.StringIO(decoded.decode('utf-8')), delimiter = r'\s+')    except Exception as e:        print(e)        return html.Div([            'There was an error processing this file.'        ])    return df@app.callback(Output('Mygraph', 'figure'),            [                Input('upload-data', 'contents'),                Input('upload-data', 'filename')            ])def update_graph(contents, filename):    fig = {        'layout': go.Layout(            plot_bgcolor=colors["graphBackground"],            paper_bgcolor=colors["graphBackground"])    }    if contents:        contents = contents[0]        filename = filename[0]        df = parse_data(contents, filename)        df = df.set_index(df.columns[0])        fig['data'] = df.iplot(asFigure=True, kind='scatter', mode='lines+markers', size=1)    return fig@app.callback(Output('output-data-upload', 'children'),            [                Input('upload-data', 'contents'),                Input('upload-data', 'filename')            ])def update_table(contents, filename):    table = html.Div()    if contents:        contents = contents[0]        filename = filename[0]        df = parse_data(contents, filename)        table = html.Div([            html.H5(filename),            dash_table.DataTable(                data=df.to_dict('rows'),                columns=[{'name': i, 'id': i} for i in df.columns]            ),            html.Hr(),            html.Div('Raw Content'),            html.Pre(contents[0:200] + '...', style={                'whiteSpace': 'pre-wrap',                'wordBreak': 'break-all'            })        ])    return tableif __name__ == '__main__':    app.run_server(debug=True)
To run the app yourself, simply copy & paste the code above into your app.py file and from the terminal run
python app.py
Go to http://localhost:8000/
...
Launch a workspace (see guide here) and enter Jupyterlab. You can either run it from the notebook (simply execute the cell) or create an app.py file and run from the terminal.
To view your running app, copy and paste your workspace’s URL into a separate tab. Remove the lab? and append -8000 (the public port on which we've launched the app above) to the workspace id number, before .cloud.kyso.io. For example, if my workspace is running at https://live-aymycm9bst.cloud.kyso.io/lab?
In a separate tab, I can go to https://live-aymycm9bst-8000.cloud.kyso.io/, where my app will be running.