How To Build A Reporting Dashboard Using Dash and Plotly
How To Build A Reporting Dashboard Using Dash and Plotly
Sign in
Get started
Follow
David Comfort
Follow
Mar 11, 2019 · 24 min read
1.99K
. . .
Table of Contents
1. Introduction
Adding a correction to CSS so that the Date Picker is not hidden behind the
data tables
Changing the Dates Presented in the Data Table based upon the Date
Selection
A method to select either a condensed data table or the complete data table.
Step 1
Step 2
Step 3
15. Wrapping up
. . .
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.
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.
. . .
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.
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.
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.
. . .
Having the ability to color code cells in the data tables, based upon the
value in that cell.
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.
. . .
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.
I should note that the versions for Dash and its components will change
from above and you should refer to the User Guide.
. . .
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*:
Data Visualization GUIs with Dash and Python (Video playlist) — Five-
part series exploring Dash features.
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:
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.
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).
. . .
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).
Header Bar
Download button
Graphs
A simpli ed code block for one layout section in the layouts.py is below:
The above code does not include conditional formatting, which I will go
into below.
$ python app.py
...Running on http://127.0.0.1:8050/ (Press CTRL+C to quit)
. . .
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.
Code Block 10: Html Element for Date Picker in layouts.py File
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.
Code Block 12: CSS for change z-index of data table in custom.css le
. . .
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:
editable=True gives the user the ability to change the data in the table
while using the app.
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
The functions for both the rst data table and the second data table are
similar so I only present one here:
The ow between the data table element, the callback and the function can
be portrayed as:
Figure 6: Flow for the rst data table
The callback for this functionality takes input from the radio button and
outputs the columns to render in the data table:
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' .
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.
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):
I describe the method to update the graphs using the selected rows in the
data table below.
. . .
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.
You will need the following modules for the download link to work
properly:
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.
based upon the start and end dates, as well as the current date.
Code Block 25: Callback and function for excel download data link
. . .
10. Building the Second Data Table
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.
. . .
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.
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:
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):
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:
. . .
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
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
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
method:
The main graph gure and its parameters are de ned when we call
tools.make_subplots . During app development, I found that potential
have the same number of titles as the number of graphs. Both of these
might cause the app to fail.
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.
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).
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:
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.
. . .
Certainly, one of the nicest tools is the ability to download a pic of the
graph:
Figure 14: Downloaded image of the graphs
. . .
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 :
You will also need a new dependency, gunicorn , for deploying the app
(line 10 in the above code segment).
app.py
.gitignore
You also need to generate a requirements.txt le. You can do this with:
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.
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 .
. . .
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:
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.
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.)
. . .
Udemy Course (I have not taken this online course so I cannot comment
on it though.
1.99K claps
WRITTEN BY
David Comfort
Follow
2.8K
960
More from Towards Data Science
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