You are on page 1of 23

Read more stories this month when you create a free Medium account.

Sign in
Get started

How to Build a Reporting Dashboard


Top highlight
Towards Data
Science

using Dash and Plotly


A Medium
publication sharing
concepts, ideas, and
codes.

Follow
David Comfort
Follow
Mar 11, 2019 · 24 min read

1.99K

In this blog post, I will provide a step-by-step tutorial on how to build a


reporting dashboard using Dash, a Python framework for building
analytical web applications. Rather than go over the basics of building a
Dash app, I provide a detailed guide to building a multi-page dashboard
with data tables and graphs.

I built the reporting dashboard as a multi-page app in order to break up the


dashboard into di erent pages so it less overwhelming and to present data
in an organized fashion. On each dashboard page, there are two data
tables, a date range selector, a data download link, as well as a set of graphs
below the two dashboards. I ran into several technical challenges while
building the dashboard and I describe in detail how I overcame these
challenges.

The completed Dashboard can be viewed at https://davidcomfort-dash-


app1.herokuapp.com/cc-travel-report/paid-search/ and the code is
provided in Github. (The data used in the app is random data and the
names for the products are “dummy” names.)

. . .

Figure 1: Dashboard built using Dash


Figure 2: Second Part of Dashboard

Table of Contents
1. Introduction

2. What am I Trying to Achieve with a Dashboard?

3. Challenges Building the Dashboard

4. Creating a New Environment and Installing Dash

5. Getting Started with Dash

6. Building a Multi-page Application

Building the Index Page

Customizing the Page Title and Favicon

Overview of the Page Layout

Local Testing of the App

7. Building the Date Selector Element

Adding a correction to CSS so that the Date Picker is not hidden behind the
data tables

8. Building the First Data Table

Changing the Dates Presented in the Data Table based upon the Date
Selection

Calculating Changes in the Metrics, as well as the calculating cost-per-


session, conversion rate, and cost-per-acquisition.

A method to select either a condensed data table or the complete data table.

Conditionally Color-Code Di erent Data Table cells

Conditional Formatting of Cells using Doppelganger Columns


9. Building the Download Data link

10. Building the Second Data Table

11. Updating Graphs by Selecting Rows in a Dash Data table

Step 1

Step 2

Step 3

12. Updating the Graphs and Calculating metrics on the y

13. Plotly Graph Tools

14. Deploying the Dashboard

Resolving an Issue with Custom.css File and Authentication

Dash Deployment Server

15. Wrapping up

16. Resources for Dash

. . .

1. Introduction
In January of this year, I went to my rst PyData conference, PyData Miami,
and sat in on a great presentation by Chelsea Douglas about Dash.

Dash: data exploration web apps in pure Python - Chels…

Embedded Video 1: Dash: data exploration web apps in pure Python — Chelsea Douglas

It quickly became apparent how powerful Dash was and how I easily I could
build web apps and dashboards using Python.

From my perspective, there was a real need in my company to automate


reporting, replace Microsoft Excel pivot tables and spreadsheets, and
provide an alternative to Business Intelligence tools. Even though various
stakeholders in my company have relied upon Excel spreadsheets for
regular reporting, their usage becomes unwieldy, they are prone to error,
they are not platform independent, and they do not lend themselves to
automation.

Therefore, I endeavored to build a multi-page web application using Dash.


This article goes into the nitty, gritty details of my e orts and how I
overcame several technical challenges. I should note that I come from a
data scientist perspective and make no claims to be a software developer.
Hence, there certainly is room for improvement in my code and I would
welcome the reader’s suggestions. At the same time, I hope the reader can
bene t from my e orts if they need to build complex dashboards and data
tables.

. . .
2. What am I Trying to Achieve with a Dashboard?
At my present company, a lot of periodic reporting is done either with Excel
spreadsheets and pivot tables, or using business intelligence tools such as
Birst or Qlikview. Hence, I wanted to build a reporting dashboard as a
proof-of-concept that could replace and enhance our reporting. Speci cally,
I wanted to build a reporting web application for one of brands, which
could report out on di erent marketing channel metrics, enable automation
and provide ease of access.

The requirements for the E-commerce Marketing Dashboard included:

Break out the di erent marketing channels into di erent pages so that
the amount of data presented in the dashboard would not be
overwhelming.

A date selector so a user can select a date range to lter the data.

A data table to present basic metrics (spend, sessions, number of


transactions, and revenue) for each digital marketing channel or
product type for the date range selected, as well as for the same period
last year and a corresponding period prior to the date range selected.*

Another data table to present calculated metrics for the date range
selected (as well as for the same period last year and prior period).
These metrics include cost-per-session (CPS), conversion rate (CVR),
and CPA (cost-per-acquisition).

A link to download the data (in Excel format) that is displayed in each
data table.

Graphs of the metrics on a weekly basis are to be depicted below the


data tables.

An example of a corresponding prior period would be if a user selected week


numbers 5 and 6 of 2019, then the corresponding prior period would be
week numbers 3 and 4 of 2019.

. . .

3. Challenges Building the Dashboard


There were multiple challenges that cropped up when I was building the
dashboard app. Some of the main challenges included:

Figuring out a method to display either a condensed data table or a


complete data table.

Having the ability to color code cells in the data tables, based upon the
value in that cell.

Having the ability to select multiple products to include in a graphical


representation of the data.

Having the ability to update the metrics in the data tables on-the- y,
depending upon the date range a user selects.

Having the ability to download the data presented in the data tables
without any added formatting.

Depicting year-to-year changes in metrics on the same x-axis as a given


metric.

Being able to zoom in on all graphs at the same time.

. . .

4. Creating a New Environment and Installing Dash


There is a Dash User Guide, which provides a fairly thorough introduction
to Dash and I encourage the reader to go through the user guide and build
some simple Dash apps prior to tackling a full edged dashboard. In
addition, there is a Dash Community Forum, a show-and-tell section of the
forum highlighting work by the Dash community, a gallery of Dash
projects, a curated list of awesome Dash resources, and an introductory
essay about Dash:

Dash is a Open Source Python library for creating reactive, Web-based


applications. Dash started as a public proof-of-concept on GitHub 2 years ago.
We kept this prototype online, but subsequent work on Dash occurred behind
closed doors. We used feedback from private trials at banks, labs, and data
science teams to guide the product forward. Today, we’re excited to
announce the rst public release of Dash that is both enterprise-ready
and a rst-class member of Plotly’s open-source tools. Dash can be
downloaded today from Python’s package manager with pip install dash —
it’s entirely open-source and MIT licensed. You’ll nd a getting started guide
here and the Dash code on GitHub here.

Dash is a user interface library for creating analytical web applications. Those
who use Python for data analysis, data exploration, visualization, modelling,
instrument control, and reporting will nd immediate use for Dash.

Dash makes it dead-simple to build a GUI around your data analysis code.

Prior to installing Dash, as is the usual practice, I created a new


environment using conda: conda create --name dash and then activated
that environment, conda activate dash . I then simply followed the Dash

installation protocol provided in the user guide:

Code Block 1: Pip installing Dash and its components

I should note that the versions for Dash and its components will change
from above and you should refer to the User Guide.

. . .

5. Getting Started with Dash


There are already quite a few tutorials for Dash, so I will focus on how to
build a multi-page dashboard with data tables and graphs in this tutorial,
rather than go over the basics of building a Dash app.

If you are just getting started in Dash, I would encourage the reader to go
through at least the rst three sections of the excellent Dash User Guide.
There is also a section on the Dash Data Table. There are several tutorials to
get you started*:

Introducing Plotly Dash — A high level introduction to Dash by Chris


Parmer, the author of Dash. This essay was released as part of Dash’s
o cial launch (June 21, 2017).

Plotly’s tutorials — Part 1: App Layout

Plotly’s tutorials — Part 2: Interactivity

Plotly’s tutorials — Part 3: Interactive Graphing

Plotly’s tutorials — Part 4: Callbacks With State

Interactive Web-Based Dashboards in Python — How the MVC model


pertains to Dash and a walkthrough of building an app.

Using Plotly’s Dash to deliver public sector decision support dashboards


— Building a complex dashboard step-by-step.

OPS CodeDay: Dash Plotly Map + Graph — How to use Jupyter


notebooks in tandem with Dash to create mapping viz.

Creating Interactive Visualizations with Plotly’s Dash Framework —


High level overview of how to get started with Dash.

Finding Bigfoot with Dash, Part 1 — Walkthrough of building a


dashboard of Bigfoot sightings. Part 2, Part 3.

Visualize Earthquakes with Plotly Dash — Environmental scan of


alternatives to Dash followed with a tutorial.

ARGO Labs — Plotly Dash Tutorial (Video) — Detailed introduction to


creating interactive dashboards.

Data Visualization GUIs with Dash and Python (Video playlist) — Five-
part series exploring Dash features.

*From the Awesome Dash Github page.

Essentially, Dash apps are composed of two parts: (1) the “layout” of the
app that describes the look and feel of the app, and (2) the “callbacks” that
enable the apps to be interactive. A simple Dash App Layout is presented in
the user guide and reproduced below:

Code Block 2: Simple Dash App Layout

The dashboard which I describe in this tutorial splits up the Dash app into
di erent les and enables one to build a multi-page app.

There is an “index” page which renders di erent pages, or layouts,


depending the URL. Each individual layout consists of several di erent
Dash components, including a date range picker, data tables, a download
link, and several graphs. Each of these components is related to one or more
“callbacks” that enable the dashboard to be interactive.

A user can interact with one of the Dash components (e.g., change the date
range) and the other components re ect this change (e.g., cause the data
presented in the data tables to change).

A schematic of this approach is in the following diagram:

Figure 3: Schematic of Dashboard les

. . .

6. Building a Multi-page Application


The structure of multi-page Dash application, based upon the Dash
documentation at https://dash.plot.ly/urls, with slight tweaks, is presented
below:

Code Block 3: File Structure of a Multi-page Dash App

The le app.py simply has the following:

Code Block 4: app.py

Note that I am using the dash authentication module


(https://dash.plot.ly/authentication). This has some implications which I
will go into below.

Building the Index Page


The index.py le essentially de nes the URL page structure of the app by
de ning a function display_page , which determines what page layout

should be rendered depending upon the app URL.

Code Block 5: index.py

This is very similar to the index.py page in the Dash Vanguard Report at
https://github.com/plotly/dash-vanguard-report, with a demo at
(https://dash-gallery.plotly.host/dash-vanguard-report/portfolio-
management).

However, I had to include the line from app import server , in order to
overcome an issue when I deployed the app on Heroku (see
https://community.plot.ly/t/nolayoutexception-on-deployment-of-multi-
page-dash-app-example-code/12463 for details).

Customizing the Page Title and Favicon


In addition, I wanted to customize the app’s HTML index template,
speci cally, the page title and the favicon. In order to do so, I added the
following index_string: to index.py :

Code Block 6: index_string in index.py File

In order to order to customize the favicon, the favicon.ico image can be


placed in the assets directory.

Overview of the Page Layout


The callback in the index.py le takes as input the app URL and outputs
di erent layouts depending upon the layout:

Code Block 7: Callback in index.py File

The layouts.py le contains the following:

Normal python import statements

Statement to read in the data CSV le into pandas data frame

De nition of the data table columns needed in the di erent variations of


the layouts

De nition of each page layout section

De nition of the noPage layout

The beginning of the layouts.py le is as follows:

Code Block 8: Beginning of layouts.py File


Each page layout section contains the following elements:

Call to Header() which is imported, so one can have common elements


(logo, brand name, etc.) across multiple apps.

Data Range element

Header Bar

Radio Button to select condensed vs. complete data frame

First data table

Download button

Second data table

Graphs

A simpli ed code block for one layout section in the layouts.py is below:

Code Block 9: Section of layouts.py to render Paid Search Page

The above code does not include conditional formatting, which I will go
into below.

Local Testing of the App


Following the user guide, after one has created the index.py and app.py

les, one invokes the app with

$ python app.py
...Running on http://127.0.0.1:8050/ (Press CTRL+C to quit)

and visit http:127.0.0.1:8050/cc-travel-report/paid-search/ in your web


browser.

. . .

7. Building the Date Selector Element

Figure 4: Date Selector Element

The date selector element provided a means to update the data presented
in both data tables, as well as the downloaded data link. I also wanted the
date selector to provide feedback on the dates selected by the user.

I simply chose January 1, 2018 as the minimum date allowed


( min_date_allowed ); The maximum date allowed was based upon the
maximum date in the CSV data le ( max_date_allowed ); the initial month
presented in the date selected was based upon the maximum date in the
CSV data le ( initial_visible_month ); the initial start date was the
maximum date minus 6 days ( start_date ); and the initial end date was
the maximum date.

The following code is located in the layouts.py le and needs to be


repeated for each “page” of the Dashboard app.

Code Block 10: Html Element for Date Picker in layouts.py File

I used a callback and a function, update_output to provide feedback to the


user. Speci cally, I wanted to provide text feedback on the dates selected,
the number of days selected, as well as the dates in the corresponding
previous period. For instance, if a user had selected week numbers 5 and 6
of 2019 using the date range selector, then the corresponding past period
would be week numbers 3 and 4 of 2019. The update_output function
below provides this functionality.

Code Block 11: Callback and Function for Date Picker in layouts.py le

The date picker element provides input to the callbacks for the rst data
table, the second data table, the download link, as well as the set of graphs
below the data tables. I will go into detail how each of these callbacks work,
with their associated outputs.

Adding a correction to CSS so that the Date Picker is not hidden


behind the data tables
One issue I had to correct was that the data tables obscured the date picker
element, so I had to correct the CSS accordingly.

Code Block 12: CSS for change z-index of data table in custom.css le

. . .

8. Building the First Data Table

Figure 5: First Data Table with a Condensed View

The rst data table in the dashboard presents metrics such as spend (the
cost associated with a given advertising product), website sessions,
bookings (transactions), and revenue. These metrics are aggregated
depending upon the dates selected in the date selected and typically present
data for the current year for the date range selected, data for the previous
corresponding period, data for last year, as well as percent and absolute
di erences between these periods.
Changing the Dates Presented in the Data Table based upon the
Date Selection
The main functionality I wanted for the rst data table is making it
interactive, whereby the data presented changes according in the date
selected. To start with, the data table element needs to be included in the
layouts.py le as in the (simpli ed) code below:

Code Block 13: Data Table Component in layouts.py File

The di erent parameters for the data table include:

id : identi er so that the data table can be referenced by the callbacks.

columns : the columns presented in the table; deletable gives the


ability of the user to delete the column while using the app.

editable=True gives the user the ability to change the data in the table
while using the app.

n_fixed_columns=2 freezes the rst two columns (the checkboxes and


the placement type in our case), so when a user scrolls across the
complete data table, the user can still view the check boxes and the
placement type.

style_table allows CSS styles to be applied to the table.

row_selectable='multi' allows the user to select multiple rows via the


checkboxes. This will enable updating of the graphs below based upon
the selection.

selected_rows=0 contains the index of the rows that are selected


initially (and presented in the graphs below).

style_cell allows CSS styles to be applied to the table cells.

I will add additional parameters in a subsequent step so we can have


conditional style formatting. Number formatting such as dollar signs,
commas, and percent signs are added in pandas and de ned as a series of
formatters.

I should note that I do not specify the data parameter for the data table in
the layouts.py le. Rather, the data parameter for the data table will
come from the callback:

Code Block 14: Callback for First Data Table in layouts.py File

Calculating Changes in the Metrics, as well as the calculating cost-


per-session, conversion rate, and cost-per-acquisition.
Since we need to calculate changes in the metrics, as well as CPA, CPS, and
conversion rate depending upon the dates selected “on-the- y,” I push these
calculations into a separate le, functions.py . I can then re-use these

functions for the di erent Dashboard pages. I also de ne formatting


functions in this le:

Code Block 15: Data Formatters in functions.py le

The functions for both the rst data table and the second data table are
similar so I only present one here:

Code Block 16: update_ rst_datatable Function in functions.py

The ow between the data table element, the callback and the function can
be portrayed as:
Figure 6: Flow for the rst data table

A method to select either a condensed data table or the complete


data table.
One of the features that I wanted for the data table was the ability to show
a “condensed” version of the table as well as the complete data table.
Therefore, I included a radio button in the layouts.py le to select which
version of the table to present:

Code Block 17: Radio Button in layouts.py

The callback for this functionality takes input from the radio button and
outputs the columns to render in the data table:

Code Block 18: Callback for Radio Button in layouts.py File

This callback is a little bit more complicated since I am adding columns for
conditional formatting (which I will go into below). Essentially, just as the
callback below is changing the data presented in the data table based upon
the dates selected using the callback statement, Output('datatable-paid-
search', 'data' , this callback is changing the columns presented in the

data table based upon the radio button selection using the callback
statement, Output('datatable-paid-search', 'columns' .

Conditionally Color-Code Di erent Data Table cells


One of the features which the stakeholders wanted for the data table was
the ability to have certain numbers or cells in the data table to be
highlighted based upon a metric’s value; red for negative numbers for
instance. However, conditional formatting of data table cells has three main
issues.

There is lack of formatting functionality in Dash Data Tables at this


time.

If a number is formatted prior to inclusion in a Dash Data Table (in


pandas for instance), then data table functionality such as sorting and
ltering does not work properly.

There is a bug in the Dash data table code in which conditional


formatting does not work properly.
I ended up formatting the numbers in the data table in pandas despite the
above limitations. I discovered that conditional formatting in Dash does not
work properly for formatted numbers (numbers with commas, dollar signs,
percent signs, etc.). Indeed, I found out that there is a bug with the method
described in the Conditional Formatting — Highlighting Cells section of the
Dash Data Table User Guide:

Code Block 19: Conditional Formatting — Highlighting Cells

The cell for New York City temperature shows up as green even though the
value is less than 3.9.* I’ve tested this in other scenarios and it seems like
the conditional formatting for numbers only uses the integer part of the
condition (“3” but not “3.9”). The lter for Temperature used for
conditional formatting somehow truncates the signi cant digits and only
considers the integer part of a number. I posted to the Dash community
forum about this bug, and it has since been xed in a recent version of
Dash.

*This has since been corrected in the Dash Documentation.

Conditional Formatting of Cells using Doppelganger Columns


Due to the above limitations with conditional formatting of cells, I came up
with an alternative method in which I add “doppelganger” columns to both
the pandas data frame and Dash data table. These doppelganger columns
had either the value of the original column, or the value of the original
column multiplied by 100 (to overcome the bug when the decimal portion
of a value is not considered by conditional ltering). Then, the
doppelganger columns can be added to the data table but are hidden from
view with the following statements:

Code Block 20: Adding Doppelganger Columns

Then, the conditional cell formatting can be implemented using the


following syntax:

Code Block 21: Conditional Cell Formatting

Essentially, the lter is applied on the “doppelganger” column,


Revenue_YoY_percent_conditional ( ltering cells in which the value is less
than 0). However, the formatting is applied on the corresponding “real”
column, Revenue YoY (%) . One can imagine other usages for this method

of conditional formatting; for instance, highlighting outlier values.

The complete statement for the data table is below (with conditional
formatting for odd and even rows, as well highlighting cells that are above
a certain threshold using the doppelganger method):

Code Block 22: Data Table with Conditional Formatting

I describe the method to update the graphs using the selected rows in the
data table below.

. . .

9. Building the Download Data link


Figure 7: Download data link

One of the features I wanted for the dashboard was the ability to download
the data that was presented in the data tables. Speci cally, I wanted the
downloaded data to be updated according to the dates selected (and
presented in the data tables). In addition, since the dates are not displayed,
it was necessary to add the dates to the downloaded le. Moreover, even
though the data is formatted in the data tables (with $, % , and thousand
comma separators, I wanted the downloaded data to be free of formatting.

There were two critical Plotly community threads which helped me to build
this functionality at Allow users to dowload [sic] an Excel in a click and
Allowing users to download CSV on click.

The download data functionality was implemented using the following:

There is simply a placeholder for the download button in the


layouts.py le.

Code Block 23: Placeholder for Download Button

You will need the following modules for the download link to work
properly:

Code Block 24: Import Statements to Enable Download Functionality

The callback for the excel download is placed in the callbacks.py le.
The callback is taking the start_date and the end_date from the date
picker element and outputting a le reference to the download link.

The update_link function is updating the URL link to serve, based upon
the start and end dates.

The function download_excel_1 takes the URL value and splits it up


back into start_date and end_date so that I can name the filename

based upon the start and end dates, as well as the current date.

Code Block 25: Callback and function for excel download data link

The function download_excel_1 also calls another function,


update_first_download , which is very similar to the

update_first_download function presented above, except that the data


is not formatted.

The downloaded Excel le is named according to the product name and


the start and end dates selected, as well as the current date. Columns for
the start and end dates and other appropriate dates are added to the
downloaded data le.

Figure 8: Snap shot of the downloaded data.

. . .
10. Building the Second Data Table

Figure 9: Data Table with Calculated Metrics

The second data table featured metrics such as cost-per-session, conversion


rate and and cost-per-session which are calculated “on-the- y,” depending
upon the dates selected in the date range selected.

The second data table is created is a similar manner to the rst data table so
the details are left out of this tutorial, but the complete les are on Github.

. . .

11. Updating Graphs by Selecting Rows in a Dash


Data table

Figure 10: Digital Marketing Metric Graphs


The graphs below the data tables display metrics aggregated by week, and
include the current year’s data, last year’s data, as well as the percentage
changes between the two.

Figure 11: Diagram of callback and function to update graphs

The steps to update the graphs are:

1. Set placeholder in the layouts.py le.

2. Create callback for each set of graphs, for each page of the Dashboard in
the callbacks.py le.

3. Build a function which lters the complete list of products by the list of
products selected in the Dash data table, and subsequently creates a
pandas data frame based upon this selection.

4. The ltered data frame is then passed to another function,


update_graph that (a) calculates metrics such as cost-per-session,
conversion rate and cost-per session, and (b) produces the output for the
graphs.

These steps are detailed below. Step 4 is detailed in Section 12, “Updating the
Graphs and Calculating metrics on the y.”

Step 1
In the layouts.py le, there is simply a placeholder for the graphs:

Code Block 26: Placeholder for Graphs

Step 2
There is a callback for each set of graphs on each page in the callbacks.py

le:
Code Block 27: Callback for Graphs

The callback takes input from the rst data table, as well as input from the
date picker, and outputs to the dcc.Graph element with the id , paid-

search .

Step 3
One of the challenges I had had was to gure out a way of updating the
graphs depending upon the products selected in the data table.

In the code for the rst data table, it was necessary to set the parameters for
row_selectable to multi and selected_rows=[0] . The former parameter

enables checkboxes next to each row in the data table, whereas the latter
parameter ensures that the selected rows has an initial value. The
parameter, n_fixed_columns freezes the rst two rows in the data table so
that they are visible when a user enables the complete data frame, (thus
exposing all of the available columns).

Here is a simpli ed code block for the rst data table (which is in the
layouts.py le):

Code Block 28: Simpli ed Data Table De nition

Hence, the callback for the graphs acquire the selected_rows . In the
function to update the graphs, update_paid_search , a list of products is

built by ltering the original data frame by the dashboard page category
(Paid Search in this case) and getting a complete list of unique placement
types. Then, a for loop lters this list by the list of products selected in the
data table. Subsequently, a ltered data frame, filtered_df is created by
ltering the original data frame by the list of selected products and
performing a pandas groupby and summing the spend, sessions, bookings
and revenue columns. The callback and function, update_paid_search , for
the graphs is presented below:

Code Block 29: Callback and Update Function for Graphs

. . .

12. Updating the Graphs and Calculating metrics on


the y
The graphs for each metric are aggregated by week and by the products
that are selected in the data table (as detailed above).

The features I wanted for the graphs and for the update function included:

Since the graphs can depict metrics on more than one product at a time,
Read more stories this month when you create a free Medium account.
the update function needs to calculate the metrics on-the- y.

The graph for each metric should include the value for this year, the
value for last year, as well as the year-to-year percentage
di erence.These should be overlaid on the same graph, with the values
Towards Data
being line graphs, and the year-to-year change being bar graphs.
Science
A Medium
I wanted the zoom function to work across all of the graphs
publication sharing
simultaneously,
concepts, ideas, and such that zooming in on one graph, zooms in the other
codes.
graphs at the same zoom level.
Follow

The complete update_graph function (which resides in the functions.py

le is below:

1.99K
Code Block 30: update_graph Function in functions.py File
In order to “group” the graphs together, I utilized the subplot capability of
Plotly detailed at https://plot.ly/python/subplots/. The update_graph

function has the following main elements:

On-the- y calculation of the metrics.

Each graph “trace” is de ned using the graph_objs method in the Plotly
library (remember to import this method via import plotly.graph_objs

as go ). Speci cally, each graph is de ned with the go.Scatter

method:

Code Block 31: Trace Assignment for each Graph element

The main graph gure and its parameters are de ned when we call
tools.make_subplots . During app development, I found that potential

sources of error are not remembering to change the number of rows


when you add additional traces ( rows=6 ) , as well as ensuring that you

have the same number of titles as the number of graphs. Both of these
might cause the app to fail.

Code Block 32: make_subplots To Set Parameters for Subplots

The di erent subplot “traces” are appended to the main graph gure
with the statements such as fig.append_trace(sessions_ty, 1, 1) ,

where sessions_ty is the trace name de ned above; the rst number, 1

is the graph number and the last number is the column number (which
is 1 in every case since we only have one column). I have included
commented-out numbers for each trace in order to assist me in the next
step.

Code Block 33: Appending Trace to the Figure

In order to overlay the year-to-year changes, I had to have several update


statements, as well as adding new y-axes for each overlaid graph (the year-
to-year change graphs). The syntax required for these overlaid graphs was a
little tricky.

Code Block 34: Code to Overlay Year-to-Year Change Graphs

For instance, in the rst update statement, the index number 2 in the
statement fig['data'][2] refers to the third trace, sessions_yoy (since
Python is zero-indexed).

The following gure hopefully assists in visualizing the indexing of the


various elements of the combined graph.
Figure 12: Schematic of the Di erent Axis Indexes

The y-axis name, y7 , in the statement yaxis='y7' needs to be 7 since


there are already 6 y-axes (one for each metric: sessions, spend,
bookings, cpa, cps, and cr. For each metric, this year’s and last year’s
data shares the same left y-axis). Hence, we need start numbering the
right-side y axes at 7.

We need to assign the parameter for each of the additional y-axes using
the fig['layout']['yaxis'] statements. Speci cally, the rst right-side
axis for the year-to-year changes in sessions overlaps with the other
session traces in the rst graph. Hence, we need to assign the
parameters accordingly: overlaying='y1', anchor='x1' and, of course,
we need to assign it to the right side by setting side='right' . In
addition, I update the title of the set of the graphs with the following:

Code Block 35: Update Title of Graphs

Finally, I need to update the overall gure layout with the following
statement. I should note that there are several parameters below which are
commented out since I am still experimenting with di erent parameters.

Code Block 36: Update Overall Figure Layout

. . .

13. Plotly Graph Tools


I should note that there are several helpful tools available in Dash to
manipulate the graphs. If you hover over the top right-side of the graph
gure, you can view these tools.
Figure 13: Plotly Graph Tools

With these tools you can:

Drag to zoom in and double-click to return to the original graph.

Drag the corners of a graph to zoom along one axis.

Double-click to autoscale a single axis.

Change the hover mode to compare data or investigate a single data


point.

Certainly, one of the nicest tools is the ability to download a pic of the
graph:
Figure 14: Downloaded image of the graphs

. . .

14. Deploying the Dashboard


I essentially followed the Deploying Dash Apps guide in the Dash User
Guide, with a few changes. Speci cally, I deployed the Dashboard on
Heroku using the following steps:

Step 1: I had already created a folder for the my dashboard code in a


previous step.

Code Block 37: Mkdir and cd Code

Step 2: Initialize the folder with git . Rather than use venv , I had
previously set up a conda environment for Dash.

Code Block 38: Init Git and Active Dash Conda Environment

And I had either installed the app’s dependencies using either conda or
pip :

Code Block 39: Pip Installation of Dash and other Modules

You will also need a new dependency, gunicorn , for deploying the app
(line 10 in the above code segment).

Step 3: Initialize folder with an app ( app.py ), a .gitignore le, a


requirements.txt le, and a Procfile le for deployment. We only
need a minimal app.py since we are de ning our app using multiple
les. You need to create the following les in your project folder.

app.py

Code Block 40: app.py le (previously created)

.gitignore

Code Block 41: .gitignore le

And a Procfile le:

Code Block 42: Proc le le

You also need to generate a requirements.txt le. You can do this with:

Code Block 43: Create new requirements.txt le

Step 4: Initialize Heroku, add les to Git, and Deploy. Prior to


initializing Heroku, you need to install the Heroku Command Line
Interface (CLI) either with their installer available at
https://devcenter.heroku.com/articles/heroku-cli or, on a Mac, using
homebrew: brew tap heroku/brew && brew install heroku .

Code Block 44: Initialize Heroku and Deploy


You should be able to view your app at https://my-dash-

app.herokuapp.com (changing my-dash-app to the name of your app).

Step 5: Update the code and redeploy. When you modify any element of
your app, you will need to add the changes to git and push those
changes to Heroku. Also, whenever I change the underlying CSV data
le, you need to push the new data set to Heroku.

Code Block 45: Pushing changes to Heroku via Git

I should note that if you have issues deploying to Heroku, try deploying a
simply app rst and test whether you are doing each step correctly. For
instance, I found that I needed to add the following to the index.py le in
order for the Heroku deployment to work: from app import server .

Resolving an Issue with Custom.css File and Authentication


In addition, I found that when I added authentication to my app, the app
could no longer access my custom.css le, so I needed to create a css le on
codepen.io and include it in the list of external css les (I am assuming that
this is a bug in Dash).

Dash Deployment Server


Plotly also o ers a Dash Deployment Server…

for easy app deployment in commercial IT environments, get started with


support plans and workshops for mission critical projects, or contract our
world-class engineering team for custom feature development or proof of
concept app development.

I should note that I have no a liation with Plotly.

. . .

15. Wrapping up
From start to nish, the project took about two and half weeks. I had not
previously used Dash and had limited experience using Plotly. Some of the
methods which I employed to build the dashboard included:

Building minimal parts of the dashboard rst. Basically, I do not try to


tackle everything at once. I built element at a time, and often by itself,
before adding it to the entire dashboard.

To ensure that I got my python syntax correct, I used a Jupyter notebook


with the original data frame and applied each change in a piecewise
fashion.

I would make small incremental changes to the app and then test. Rinse
and repeat.

I developed one section rst and got it working and to my liking. Then I
would copy and paste this section to the other sections and make
appropriate changes.

I saved deployment for the last step. I tested everything locally.

Some things on my wish list to update the Dashboard include:

Add weeks to each graph axis, most likely using annotations.

Have the graph x-axis cross over years, rather than just depict either
2018 or 2019.

Add a “print PDF” button for the report. I tried to implement one but
haven’t been able to get it working yet.
Add a footer with metadata (data sources, date data was pulled, where
it was pulled from, etc.)

Thanks for reading this far. I know it is a little overwhelming, but I


essentially broke things down into manageable pieces.

. . .

16. Resources for Dash


Here are some resources to help you further with Dash:

Dash User Guide

Udemy Course (I have not taken this online course so I cannot comment
on it though.

Dash Community Forum

Awesome Dash Resource Guide on Github

Dash Dashboard Data Science Data Visualization Towards Data Science

1.99K claps

WRITTEN BY

David Comfort
Follow

David has a passionate curiosity and drive for solving


challenging problems through data and science.
https://www.linkedin.com/in/davidmichaelcomfort/

Towards Data Science


Follow

A Medium publication sharing concepts, ideas, and codes.

See responses (11)

More From Medium

More from Towards Data Science

What’s happened to the data science


job market in the past month
Edouard Harris in Towards Data Science
Apr 13 · 4 min read

2.8K

More from Towards Data Science

Say Goodbye to Excel? A Simple


Evaluation of Python Grid Studio
Using COVID-19 Data
Christopher Tao in Towards Data Science
Apr 15 · 6 min read

960
More from Towards Data Science

Dashboards are Dead


Taylor Brownlow in Towards Data Science
Apr 9 · 7 min read

5.3K

Discover Medium
Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Make Medium yours
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Become a member
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

AboutHelpLegal

You might also like