Create a Multipage Dash Application
source link: https://www.tuicool.com/articles/6ZBnqe7
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Making a dashboardis a great way to communicate the value of data science projects to coders and non-coders alike!
For those of us that work in data science, often times our skills with data come with a lack of skill in front end development. Dash, a library built by Plotly, offers simple boiler plate code for developing interactive web applications in Python.
For inspiration, check out the Dash Gallery !
In this article, I walk through the steps of building a basic, multi-page, dash application. The code and data for this project can be found at this Github repo .
Why a multipage app?
Learning to build a multi-page app is useful for a number of reasons. Here are a few:
- Allows adding a separate page for methodology or background information.
- Breaking up a complex data project between multiple pages can make for a smooth user experience.
- Can be used to build an online portfolio!
- Understanding the workflow can serve as a gentle baby step into the world of web development.
Application structure:
Project Directory:
index.py is ultimately the file the will run the entire application. Consider it like the table of contents for the project. Each page will be built in separate files and then imported into index.py. This workflow is used to keep files concise and readable, and to keep the structure of the project organized and flexible.
D.R.Y Code
This workflow helps avoid repetitive code. For instance, each page of this application requires a navigation bar. Instead of writing the same code in every file, the component is simply imported.
Create Directory and Environment
First we create a folder to hold all of our project files:
NOTE: I personally prefer to work in either Visual Studio Code or Pycharm, but for the sake of keeping this walkthrough accessible we will stick to building this application in terminal.
In terminal:
mkdir dash_app cd dash_app
Next thing we do is create a virtual environment to build the application. No, this is not technically a requirement, but it removes the risk of having dependency errors, and generally is best practice when building web applications.
pip install virtualenv virtualenv venv source venv/bin/activate
(venv) should now appear before the $ sign in your terminal. This means that you are now using your virtual environment.
With the virtual environment activated, we need to install the libraries we will be using for this project.
pip install pandas pip install dash pip install dash-bootstrap-components### These are dependences and are likely already installedpip install dash-core-components pip install dash-html-components pip install plotly
Building the Navbar:
NOTE: The code for the navbar and the rest of the homepage was copied directly from the dash bootstrap components website .
Dash bootstrap componentsdivides the webpage into a grid, and is a useful tool for organizing the layout of a web application. For more information on positioning app components (anything that appears on the webpage) follow this link and examine the use of dbc.Row and dbc.Column.
Open vim text editor in terminal.
vim navbar.py s
s enables edit mode so we can begin writing our code!
In navbar.py:
import dash_bootstrap_components as dbcdef Navbar(): navbar = dbc.NavbarSimple( children=[ dbc.NavItem(dbc.NavLink("Time-Series", href="/time-series")), dbc.DropdownMenu( nav=True, in_navbar=True, label="Menu", children=[ dbc.DropdownMenuItem("Entry 1"), dbc.DropdownMenuItem("Entry 2"), dbc.DropdownMenuItem(divider=True), dbc.DropdownMenuItem("Entry 3"), ], ), ], brand="Home", brand_href="/home", sticky="top", )return navbar
In the sixth line above, the href parameter tells the application which page to return to the user (if you place https:// at the beginning you can navigate to outside websites!)
In this line of code, we set the href to “/time-series”. This will be the name of the link for our time-series graph.
Save and exit the file.
Esc :wq [ENTER]
Homepage:
vim homepage.py ## s to enable edit mode
In homepage.py first we need to import the libraries needed.
import dash import dash_bootstrap_components as dbc import dash_core_components as dcc import dash_html_components as html
We also want to import the Navbar function from navbar.py and create a Navbar object.
from navbar import Navbarnav = Navbar()
Build the homepage body:
The layout of this page will be a grid of 2 grid spaces. 1 rows, 2 columns.
Column 1:
- Header
- Paragraph
- Button
Column 2:
- Header
- Graph
Extra credit: Change the graph to an image.
body = dbc.Container( [ dbc.Row( [ dbc.Col( [ html.H2("Heading"), html.P( """\ Donec id elit non mi porta gravida at eget metus.Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentumnibh, ut fermentum massa justo sit amet risus. Etiam porta semmalesuada magna mollis euismod. Donec sed odio dui. Donec id elit nonmi porta gravida at eget metus. Fusce dapibus, tellus ac cursuscommodo, tortor mauris condimentum nibh, ut fermentum massa justo sitamet risus. Etiam porta sem malesuada magna mollis euismod. Donec sedodio dui.""" ), dbc.Button("View details", color="secondary"), ], md=4, ), dbc.Col( [ html.H2("Graph"), dcc.Graph( figure={"data": [{"x": [1, 2, 3], "y": [1, 4, 9]}]} ), ] ), ] ) ], className="mt-4", )
The next bit of code is important .
Because we are building a multipage application, we need to be able to import the layout into other files. To do this, we will build a Homepage function that returns the entire layout for the page.
Note: Layouts must always be a dash html component. The standard is to wrap the layout inside a div.
def Homepage(): layout = html.Div([ nav, body ])return layout
Before moving on, let’s make sure the application is working properly. To do this, add the following code to the bottom of the page.
app = dash.Dash(__name__, external_stylesheets = [dbc.themes.UNITED])app.layout = Homepage()if __name__ == "__main__": app.run_server()
Save and exit vim.
Esc :wq [ENTER]
Great! Now let’s run the application to make sure the code works!
python homepage.py
If the application fails, an error message will print out. Get used to this, it is very much the work flow of application building. For me, 90% of the time it is a simple syntax error. Read the error message and debug the problem. (If there is an issue with my code, let me know in the comments!)
If the application runs, several lines will print out. The important line will look something like this:
Running on http://127.0.0.1:8050/
Copy the http url and paste it into an internet browser.
Presto! An application is born!
After you’ve admired your creation for a suitable amount of time, shut down the web app.
[CTR] C ##### It will look like this in terminal: ^C
App
Now let’s build our interactive graph page!
First we create an app.py file.
vim app.py ## s to enable edit mode
Next, import the libraries for the application, our Navbar function, and also the Illinois population data.
### Data
import pandas as pd
import pickle### Graphing
import plotly.graph_objects as go### Dash
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Output, Input## Navbar
from navbar import Navbardf = pd.read_csv('https://raw.githubusercontent.com/jayohelee/dash-tutorial/master/data/population_il_cities.csv')df.set_index(df.iloc[:,0], drop = True, inplace = True)df = df.iloc[:,1:]
The last two lines of code are simply setting the date column as the index and then removing a duplicate of the column from the dataset.
Ok now let’s build some components!
Navbar:
nav = Navbar()
Header:
We will use the header to provide some instruction to user.
header = html.H3( 'Select the name of an Illinois city to see its population!' )
Dropdown:
For this application we will be building a dropdown menu. In the background, dropdown menus are formatted as a list of dictionaries. This dictionary has two keys. “label” and “value”. label = what the user will see in the dropdown menu, and value = what you are returning to the application to query the data.
Since each column is an individual city in Illinois, we will be using the column names of this dataset for querying.
Every column is formatted as “City, Illinois”. Having the “, Illinois” included for every label is a bit overkill since we are only looking cities from Illinois. In the code below we will remove this extra bit of text for the label of each dictionary:
options = [{'label':x.replace(', Illinois', ''), 'value': x} for x in df.columns]
Now, we plug the list of dictionaries directly into a dropdown object
IMPORTANT: Any component that is interactive must have an id name. You’ll see why soon. Additionally, components cannot share id-names. If an id is used more than once, an error will be thrown and the application will break.
The parameters in the dcc.Dropdown function are.
- id: A unique identifier for a component. Formatted as a string.
- options: The list of dictionaries with “label” and “value” keys
- value: A default value that the dropdown is set to when the application loads. (I chose a random city from the dataset for this default.)
dropdown = html.Div(dcc.Dropdown( id = 'pop_dropdown', options = options, value = 'Abingdon city, Illinois' ))
Dropdown complete!
Output space:
This is where we will output our graph.
output = html.Div(id = 'output', children = [], )
That’s all of the components!
Now, exactly like we did for our homepage, we will make an layout function called “App”.
def App(): layout = html.Div([ nav, header, dropdown, output ])return layout
Alright, now let’s make our graph. We’ll create a build_graph function that will accept a city from the dropdown menu and return a Plotly graph showing that city’s population trend.
def build_graph(city):data = [go.Scatter(x = df.index, y = df[city], marker = {'color': 'orange'})]graph = dcc.Graph( figure = { 'data': data,'layout': go.Layout( title = '{} Population Change'.format(city), yaxis = {'title': 'Population'}, hovermode = 'closest' ) } )return graph
Ok! That’s it for this file. Make sure to save and exit.
Esc :wq [ENTER]
Index
Now let’s bring it all together with an index.py file!
vim index.py ## s to enable edit mode
Import all of the libraries and functions.
import dash import dash_core_components as dcc import dash_html_components as html from dash.dependencies import Input, Output import dash_bootstrap_components as dbcfrom app import App, build_graphfrom homepage import Homepage
Next, we declare a dash app object. To make things nice and pretty we will use a theme by passing in a dbc.themes external stylesheet. The name of the theme is always upper case and is wrapped inside a list. Feel free to peruse the available themes here .
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.UNITED])
IMPORTANT: In order to make our time series graph interactive, we have to create a callback function for the dropdown menu and output space. However, dash doesn’t allow callbacks for components that don’t exist in the layout. Because there is no dropdown menu or output space in the homepage layout, we must change the configurations of our app.
app.config.suppress_callback_exceptions = True
Next we create a dcc.Location object that is not visible in the application, but monitors the url. This will tell our application to return a page based on what link the user clicks.
Now, create a content space where we return either the homepage or our time-series page.
app.layout = html.Div([ dcc.Location(id = 'url', refresh = False), html.Div(id = 'page-content') ])
Callbacks
Alright! Now let’s make our app interactive so things actually happen when the user clicks on stuff.
We will need two callbacks. One to return different pages of the app, and another to update the graph.
In order to do this, we declare @app.callback
Inside app.callback we pass in the Output function we imported at the top of the file. The parameters for Output are:
- The id for the component that is being updated.
- The parameter of the component that is being updated. (90% of the time, you will be updating a div and this parameter will be set to “children”)
Note: A callback can update multiple output spaces. Simply wrap all of the Output functions inside a list.
Next we pass the Input function into app.callback.
- **Input is always wrapped inside a list. Even if there is only one input**
- The parameters for Input are the same as Output
In this case we are outputting to our ‘page-content’ div, and inputting from our ‘url’ location.
@app.callback(Output('page-content', 'children'),
[Input('url', 'pathname')])
def display_page(pathname):
if pathname == '/time-series':
return App()else:
return Homepage()
Now we create the callback for our time-series graph.
Because we made a build_graph function, this callback is super simple.
@app.callback(
Output('output', 'children'),
[Input('pop_dropdown', 'value')]
)
def update_graph(city):graph = build_graph(city)return graph
Lastly, to make the application run.
if __name__ == '__main__': app.run_server(debug=True)
Notice I have set debug=True. When deploying your app, you would set this to false, but we are in the development phase, and setting debug to True makes debugging our application a bit easier.
Save and exit:
Esc :wq [ENTER]
And run the app!
python index.py
Conclusion
This is a simple example, but these fundamentals can be used to build much more complex and powerful applications!
Drop a comment and let me know if you have any questions.
P.S. To exit your virtual environment.
deactivate
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK