Plotly Dash — Everything You Need To Know
By: Stephen Kilcommins
Tags: Data Science, Plotly, Visualization, Python, Data Analysis

Plotly Dash — Everything You Need To Know

Table of Contents

Introduction

Over the past four months I have been exploring and critically examining the leading frameworks in the Python dashboarding ecosystem. The current industry leaders in this space are Streamlit, Plotly Dash, Voilà, and Panel. As such, I opted to focus my research entirely on these four dashboarding frameworks, as I wanted to focus in-depth on the industry leaders, as opposed to the breadth of dashboarding frameworks available.

As no solidified criteria exist for reviewing and comparing dashboarding frameworks, I had to create my own comparison criteria which made sense in the context of choosing one particular dashboarding framework over another. These criteria can be found in the table of contents above, and each of the four frameworks mentioned will be examined under these criteria.

For my comparison to be more insightful and realistic I have exposed myself to each of the frameworks over a period of months, and have created a shared example dashboard application utilising each of the four frameworks. This allowed me to get a feel for each of the technologies and experience their advantages and disadvantages first-hand.

Over the next 2 weeks I will be releasing an in-depth critical review of Voilà and Panel. This will be followed by a comparison article in which I will compare the 4 frameworks directly. Last week, I released my first review in this series on Streamlit.

The goal with these articles is not to make you proficient in using each of these frameworks — but rather to detail their strengths, weaknesses, best use cases, and all of the information that you should consider before choosing to utilise one of these frameworks in your own projects.

Who Are Dash & What Is the Framework’s Primary Objective?

Plotly Dash is a reasonably new framework for building machine learning and data science applications. Dash was created by parent company Plotly, who are already well established within the world of data science, due to their ‘plotly.py’ and ‘plotly.js’ Python and JavaScript graphing libraries. Founded in 2013, by just 2015 Plotly’s Python and R graphing libraries had become the most downloaded in the world. In 2017 Plotly launched the initial beta version of Dash, with the framework’s initial release coming in early 2019. In 2019 Dash for R was created, and in 2020 Dash for Julia was created, leading to a language-agnostic framework built upon the most common data science programming languages. ’Dash Enterprise’ was also launched on Microsoft Azure marketplace during the same year. Through Dash, Plotly aim to “close the gap between Data Science and the rest of the organization”.

With Fernando Perez — creator of IPython and Project Jupyter — as an advisor, you can be assured that Dash will work nicely within the IPython/Jupyter environment. Dash claims to be the ‘most downloaded and trusted framework for building ML & data science webapps’.

There are 2 different versions of the Dash framework available on the market today — ‘Dash Open Source’ and ‘Dash Enterprise’. It must be noted here that for the remainder of this assessment only the Dash Open Source solution will be discussed and utilised.

Dash is written on top of Flask, Plotly.js, and React.js. It enables users to create interactive dashboards using regular Python, without having to worry about back-end code, routes and requests. Dash is rendered in the browser, making it a cross-platform solution.

Programming Language Support

As mentioned above, Dash is a language-agnostic framework — with support for the Python, R, and Julia programming languages.

Python Graphing Libraries Supported

The ‘Graph’ component used to create graphs in Dash takes a Plotly figure as input, meaning Dash is primarily built for use with the ‘plotly.py’ Python graphing library. There exists external libraries for displaying alternative Python plotting libraries — namely Seaborn/Matplotlib, Altair/Vega-Lite, and Bokeh — within Dash, however these libraries are not very robust, and the level of interaction with the outputted graphs is not at the same level as Plotly-produced graphs.

The main Python library for working with other plotting libraries within Dash is called ‘dash-alternative-viz’. If you choose to use Dash it is recommended to work predominantly with Plotly graphs, as the framework was built with them in mind.

Multi-Page Application Support

Dash explicitly supports multi-page applications. Dash supports multi-page applications in a more performant way than all of the other Python dashboarding frameworks I have encountered. It also contains the ability to alter URLs without refreshing the actual webpage itself — leading to incredibly quick navigation between pages with highly reduced page-load times. The Dash user guide itself is an example of multi-page Dash application.

Although Dash handles multi-page applications in a performant way, one major caveat with using Dash for multi-page applications is that the Dash framework is stateless, limiting how a multi-page app can be implemented, since state can’t be shared between separate pages at the server side. Currently, sharing information between application pages is non-trivial, and is mostly done using hacks, some of which can be found at the links below:

Open Source vs. Proprietary

Dash has both an open source framework, as well as an enterprise version with many additional features — starting at $50,000/year.

Astonishingly, 10% of Fortune 500 companies use Dash Enterprise, meaning the framework is already tried and tested on a large scale within the industry. Dash Enterprise offers many valuable solutions wrapped within one framework, shown in the figure below.

Differences Between Dash Open Source & Dash Enterprise

Installation & Getting Started

Useful link:

Dash is easy to install — just run the following command in your terminal — ‘pip install dash ’or ‘pip install dash==1.19.0’ to install a specific version. After this you will need to import the library at the top of your Python script using the following import statement — ‘import dash’. Alongside the importation of the main dash library, you will need to import 2 more libraries — the ‘dash_html_components’ and ‘dash_core_components’ libraries. Both of these libraries are imported to create components which will act as the building blocks of your Dash application.

The process to run a Python script file with Dash is the same as running any other Python script, using the following command — ‘python filename.py’. Navigate to http://127.0.0.1:8050/ to view your Dash app if it does not pop up after running the command.

You can kill the app at any time by typing Ctrl + C in the terminal window.

To install JupyterDash the process is practically the same. Run the ‘pip install jupyter-dash’ command. Then, in your notebook (.ipynb) file instead of importing the dash library, include the following import — ‘from jupyter_dash import JupyterDash’. Obviously, this time around you will not need to run your file from the terminal. Instead, you can run your Jupyter Notebook as you typically would.

Prerequisites

An IDE/ Text Editor

Python 2 or 3

PIP (or Anaconda)

Not required but recommended: An environment management tool such as pipenv, venv, virtualend, conda

On the Dash installation page of the user guide it is recommended to install and upgrade your Dash packages frequently as the framework is under active development.

How Easy Is It to Get Started?

Although the Dash installation process is very straightforward, it gets a little tricky from there for beginners. Dash includes a 6-part tutorial to provide a run-down on some key features of the framework, including the layout, basic callbacks, interactive graphing, and sharing data between callbacks in an application.

Unless you are proficient with web development methodologies a quick scan of this tutorial documentation will likely not be sufficient.

There is a minor learning curve in starting to develop Dash applications, and this tutorial guide should be studied, rather than quickly glanced over, to really find your footing with Dash. I would recommend having a thorough read of this tutorial guide alongside the Dash Core Components Overview and the Dash HTML Components Overview before sitting down to create your first Dash application.

Development — Advantages + Disadvantages

Each Dash app is composed of 2 parts — the application layout, and application callbacks which permit interactivity. As previously mentioned, there are 2 main libraries which make up the contents of your application, the ‘dash_core_components’ and ‘dash_html_components’ libraries. Both of these libraries contain Python classes so it is possible for programmers with no web development experience to program using Dash, however, HTML, CSS, and even JavaScript code can also be used in optional parameters within these Python classes. This makes controlling the layout and look of your application easy if you have any prior experience with these programming methodologies. You can also create your own Dash components using JavaScript and React.js if you need a component which does not already exist in the aforementioned Dash libraries — although, in the vast majority of cases I doubt that this will be necessary, as the core and html component libraries are extremely comprehensive.

For now, I will not include any Dash code examples, as they are very well explained in the Dash documentation, but I will demonstrate an example soccer analytics dashboard at the end of this article to give you a sense of the structuring of a Dash application.

In essence the layout is composed of a tree of components. The ‘dash_html_components’ library has a component for every HTML tag you should need, and the ‘dash_core_components’ library has developer-built components that are made of a mixture of HTML, CSS, JavaScript, and React.js.

Dash uses “Hot-reloading” which enables developers to see their code changes automatically in browser. Note that you must include the following line of code in your application ‘app.run_server(debug=True)’ to access this hot-reloading feature. One annoyance I have encountered with this feature is that if there is an error in your Python code your application will shut down, kicking you out of the browser. You then need to fix the code issue before re-running your Dash application to view its output in the browser again. In saying that, there are workarounds available to avoid this if it is an irksome issue for you. One such workaround uses the Flask ‘flask_failsafe’ module to overcome the issue.

It is possible to change the HTML style property of different components in your application by supplying a Python dictionary (in lieu of the usual CSS syntax which has lists ending with semi-colons). You should note that the keys in the style dictionary are camelCased — i.e. the text-align CSS property will be textAlign within Dash.

For writing blocks of text, you can use HTML components from the ‘dash_html_components’ library or alternatively you can use the ‘Markdown’ component from the ‘dash_core_components’ library. It is common when using the Dash framework to find many ways of achieving the same ends, certainly aiding in the development process, but perhaps leading to hinderances in code maintenance — as differing approaches may be used in different sections of the application.

In the Dash user guide it is said that Dash permits ‘declarative UIs that are customizable through reactive and functional Python callbacks.’ Through callbacks, users can update and manipulate attributes of an application component through interaction with the application user interface. I will not deal with Dash callbacks in-depth here, as the Dash callbacks documentation is extremely comprehensive.

One slight disadvantage in working with Dash is that prior knowledge of HTML and CSS is more or less essential to attain full control over your dashboard look and feel. You can get away with just knowing Python as Dash contains ‘Pythonic’ abstractions of HTML and CSS attributes, however, the styling of your application will certainly falter without having at least a handle on HTML and CSS.

Prerequisite Skills Needed (Excluding Python)

In the Dash user guide it states — “If you’re not comfortable with HTML, don’t worry! You can get 95% of the way there with just a few elements and attributes”. For the most part this is true, as the ‘dash_html_components’ library provides users with what are essentially Python wrappers for HTML attributes. However, I do not personally believe you would be able to create Dash applications beyond very basic dashboards without at least a beginner’s knowledge of HTML. For me, the issue arises in knowing what HTML attributes to actually use, and in what way to structure these within your application code. Taking a quick crash course in HTML would definitely suffice to rid yourself of these worries though, so I wouldn’t have not knowing HTML as a reason to not leverage the framework, but do be forewarned — you will almost certainly need to brush up on your HTML knowledge to make full use of the framework.

Likewise, knowledge of CSS is not required or essential to work with Dash, but again, if you want to have greater control in the design decisions of your Dash application, I recommend at least familiarising yourself with the basics of CSS.

W3Schools provide incredible, simplistic tutorials for learning HTML & CSS, as well as reference guides for all the possible attributes you could use in your Dash applications. I have attached some helpful links below. Simply utilising the reference guides should be enough help to greatly enhance your Dash applications.

W3Schools HTML Tutorial:

W3Schools HTML Reference Guide:

W3Schools CSS Tutorial:

W3Schools CSS Reference Guide:

Ease of Use

Dash is easy to use in a basic fashion to create dashboard applications, but it is slightly more difficult to create complex application layouts. It is certainly nowhere near as easy to use as Streamlit, however, it trades off ease of use for more flexibility and an improved end-product, both in terms of control and speed. If at any stage you run into issues, the Dash documentation is extensive and you can call the ‘help’ function in your Python console to obtain details on components and their possible arguments — for example ‘help(dcc.Dropdown)’.

Example of the Dash ‘help’ function. Source

Dash’s API can be slightly overwhelming at first due to its size, however, the documentation is extremely comprehensive and is laid out in a clear and logical manner, making it easy to find the what you are looking for.

Design Flexibility

In my opinion Dash’s main strength is its design flexibility.

A lot of this design flexibility can be attributed to the use of HTML and CSS code alongside your Python code however. Users can create templates (using HTML, CSS, & Jinja) and utilise HTML and CSS directly to change the layout and flow of their applications. Much like the ‘plotly.py’ Python library, the Dash API is all-encompassing, providing programmers with incredible flexibility in design and interactivity, albeit with a slight learning curve.

Dash also provide a library called ‘Dash Bootstrap Components’ which ‘is a library of Bootstrap components for Plotly Dash, that makes it easier to build consistently styled apps with complex, responsive layouts.’

If spending a bit more time on the design side of things is possible within your organisation then Dash is a great option, as it allows almost perfect control of application layout and appearance.

Jupyter/IPython Support

The main Dash framework does not work directly with Jupyter notebooks, but with Python script files. There is some good news though; in May of 2020 Plotly released ’JupyterDash’ for working with Dash in the Jupyter/IPython environment. You can find the JupyterDash GitHub repository here:

I have found that using JupyterDash greatly reduces development time when creating Dash applications. The underlying application code is the same in both Dash and JupyterDash, and so developing in the Jupyter notebook environment allows more rapid prototyping of dashboard applications as you can ensure your Python code is working as expected in other code cells within the notebook.

Dash Application Examples

You can find some examples of Dash applications here, with the corresponding app source code here.

Deployment — Options, Advantages + Disadvantages

Useful link:

There are many different avenues to deploy your Dash application. Dash Enterprise comes with a Dash deployment server offered out of the box, making it very easy to deploy your application to a live production environment.

Dash apps can be deployed on any servers which support Flask app deployment. The Dash deployment documentation contains the necessary steps on how to deploy public Dash applications on Heroku cloud platform. Further information on deploying general Python applications on Heroku can be found on Herkou’s website. An in-depth example of deploying a Dash application on Heroku can be found here, if you run into issues following the Dash deployment documentation.

Deployment of Dash apps on JupyterHub using the ‘ContainDS Dashboards’ extension is also possible. This is an easy route for people with no prior experience deploying applications. From the ContainDS documentation, ‘ContainDS Dashboards is an extension for JupyterHub that allows users to instantly publish notebooks and other source code files as user-friendly interactive dashboards to share with non-technical colleagues’. You can find out more about deployment using this route here. Another viable option for users who are looking to deploy with minimum stress is PythonAnywhere. An article describing the process taken to upload a Dash application to PythonAnywhere can be found here.

Nearly all of the major cloud providers support deployment of Flask applications, and consequently Dash applications. It is possible to deploy your Dash application to AWS, Azure, Google Cloud Platform, and many other cloud providers. I have included links to deploying on all of the major cloud platforms below:

AWS deployment guide:

Azure deployment guide:

Google Cloud Platform deployment guide:

Google App Engine deployment guide:

Lastly, Dash apps can also be deployed on an Apache webserver using the ‘mod_swgi’ Apache HTTP Server module, and also on an Nginx webserver.

Authentication Mechanisms

Dash OpenSource provides users with basic authentication measures to include in their Dash applications. It must be installed separately to dash using the command ‘pip install dash-auth’, and then imported at the top of your Python script using ‘import dash-auth’. The package uses HTTP Basic Authentication, whereby users require a username and password to access the Dash application.

As well as the basic authentication provided by Dash you can also set up authentication measures on your live production server where your Dash application is deployed.

Maintenance — Advantages + Disadvantages

In order to test out how easy it is to maintain a Dash application I created a basic dashboard using Dash a few months back. On returning to my application nothing had broken due to deprecation, and the dashboard still functioned and looked as it did when it was first created.

Having not looked at any Dash code in months it took re-reading the initial 6-part tutorial in the documentation to refamiliarize myself with the code structure before being able to continue developing my application. Overall, it was relatively easy, but did require around 30–60 minutes to remember how to add new features. Dash maintain a clear changelog, and so on returning to my application, I simply had to read the release notes that had been uploaded during my absence to find any new features that had been added, or if any features had deprecation warnings.

Support & Online Prevalence

Overall Level of Support

No chat bot or support email is included with Dash OpenSource. In saying that, the Dash community forum is active, so obtaining support from a Dash team member or a Dash community member is relatively easy to achieve on the forum. Dash creator and Plotly co-founder Chris Parmer also frequents the forum, along with other developers of the Dash framework, so a lot of issues can be easily and directly resolved here.

The Dash Github Community

Star History of Streamlit, Dash, Panel, & Voilà. Image Courtesy of Star History.

Below I will summarise some of the key statistics from Dash’s GitHub page. These numbers can give an idea of how active the community is, as well as its popularity. All stats provided are as they were in late January 2021.

Stars: 13.8k

Forks: 1.4k

Issues: 392 Open, 558 Closed

Contributors: 61

Jupyter-Dash:

Stars: ~410

Forks: ~85

Issues: 18 Open, 13 Closed

Contributors: 3

Dash’s Online Prevalence

Another thing that is worth considering when looking at potentially using a newer framework is its online prevalence and general popularity. Choosing a framework which has a good presence online is essential for when you run into any issues when utilising the framework. If the online presence of the framework is low, then attempting to find a solution to your issue will be more difficult. Three good places to search to get an inclination of a framework’s online presence are Google, Stack Overflow, and the ‘Issues’ section of the official framework repository.

Dash:

A Google search for ‘plotly dash’ returns 905,000 results.

A Stack Overflow search for ‘plotly dash’ returns 500 results.

The Dash GitHub repository’s Issues section has 558 closed issues and 392 open issues.

JupyterDash:

A Google search for ‘jupyterdash’ returns 9,380 results.

A Stack Overflow search for ‘jupyter dash’ returns 149 results.

The JupyterDash GitHub repository’s Issues section has 13 closed issues and 18 open issues.

These searches do not provide much information in of themselves, however, taking them into account whilst also taking the direct Dash support forum into consideration will give you an idea of the support that you will receive if you do come across an issue you can’t resolve by yourself.

Helpful Resources

Awesome Dash: https://github.com/ucg8j/awesome-dash#cheat-sheets

Note that Awesome Dash also have a brilliant list of Dash resources.

GitHub: https://github.com/plotly/dash

Docs: https://dash.plotly.com/

Dash Community: https://community.plotly.com/c/dash/16

Dash Inception Article: https://medium.com/plotly/introducing-dash-5ecf7191b503

JupyterDash Inception Article: https://medium.com/plotly/introducing-jupyterdash-811f1f57c02e

How to Build a Reporting Dashboard using Dash and Plotly: https://towardsdatascience.com/how-to-build-a-complex-reporting-dashboard-using-dash-and-plotl-4f4257c18a7f

Dash Recipes: https://github.com/plotly/dash-recipes

Charming Data YouTube Channel: https://www.youtube.com/c/CharmingData/featured

Communities

Twitter: https://twitter.com/plotlygraphs

LinkedIn: https://www.linkedin.com/company/plotly/

Github: https://github.com/plotly/dash

JupyterDash Github: https://github.com/plotly/jupyter-dash

Medium: https://medium.com/plotly

Facebook: https://www.facebook.com/Plotly/

Dash Community: https://community.plotly.com/c/dash/16

Conclusion & Optimal Use Case

In the end, having explored Dash, it is my new favourite framework in the Python dashboarding landscape. As Streamlit, Dash, Panel, and Voilà currently stand I feel that Dash is the superior of the frameworks, however this isn’t an entirely fair comparison as it has been around for the longest timeframe. I do not think a pure Python background is sufficient to get by with Dash as I would consider a basic knowledge of HTML essential in structuring your application layout and getting the best from the framework. Furthermore, a basic understanding of CSS is also useful to enhance your application appearance. There is an initial minor learning curve to get started with Dash, but after spending a few hours getting to grips with the application structure it has been smooth sailing.

One caveat of Dash is that you are more-or-less cornered into using Plotly to produce your graphs. You can use other plotting libraries to be fair though, but I would love to see more integration with these plotting libraries in the future to make a more graphing library agnostic solution. There is a great online presence and community surrounding Dash, and it is tried and tested in the industry. The Dash API is robust, and the ‘Pythonic’ abstraction of web development methodologies increases the frameworks power substantially in terms of design flexibility.

If you’re looking for a one-framework-fits-all solution, or if you have a basic understanding of web development, Dash is the way to go.

Bonus: Example Soccer Analytics Dashboard Using Dash

The goal for this section was to draw a sketch of a dashboard layout I wanted to make and then use each of Streamlit, Dash, Voilà, and Panel to create a dashboard application as close as possible to this rough sketch.

My Artistic Rendition of The Desired Dashboard Layout. Not For Sale.

Again, the goal here is not to explain the intricacies of programming with Dash, but rather to demonstrate the framework’s strengths and weaknesses through a real example.

I have split the coding sections up into ‘Baseline Code’, ‘Dash Code’, and ‘Dash Code After typography.css Touch-Ups’.

The ‘Baseline Code’ represents the bare bones code that was written in standard Python to create each element of the dashboard — namely the library imports, the functions needed to produce the plots, and the code to read in and clean the data.

The ‘Dash Code’ section demonstrates how to create a dashboard from the original base Python script. This code section will give you an idea of how Dash applications are structured with ‘layout’ and ‘callbacks’ code segments. This dashboard is already most of the way there, but some more measures can be applied to achieve a nicer looking dashboard closer to the sketch above.

Lastly, the ‘Dash Code After typography.css Touch-Ups’ section aims to amend the Dash Code section until it is more-or-less the exact layout as specified in the original dashboard sketch. This section demonstrates how local CSS files can be used to override your Dash application’s appearance, including the corresponding CSS code.

Baseline Code:

#library imports

import numpy as np

import pandas as pd

import plotly.graph_objects as go

import plotly.express as px

from PIL import Image, ImageOps

import requests

from IPython.display import display, clear_output def get_and_display_player_image(df, player_name):

image_url = df[df['player_name'] == player_name] ['player_image_path'].values[0]

image = ImageOps.expand(Image.open(requests.get(image_url, stream=True).raw),border=5,fill='black')

image = image.resize((300, 300))



display(image) def apply_consistent_style(fig):

fig.update_yaxes(showgrid=False)

fig.update_xaxes(showgrid=False)

fig.update_yaxes(title_font=dict(size=16, color='white'), tickfont=dict(family='Courier', color='white', size=14))

fig.update_xaxes(title_font=dict(size=16, color='white'), tickfont=dict(family='Courier', color='white', size=14)) fig.update_layout(title_font=dict(size=18, family='Arial'), title_x=0.5, title_font_color='white', margin=dict(l=5,r=5,b=10,t=50,pad=0))



fig.update_layout({'plot_bgcolor': 'rgba(0, 0, 0, 0.5)', 'paper_bgcolor': 'rgba(0, 0, 0, 0.3)'})



return fig def plot_horizontal_bar(df, player):



df = df.groupby(['player_name'])['stats.goals.scored'].sum().sort_values(ascending = True)

df = df.reset_index()



x = df['stats.goals.scored'].tolist()

y = df['player_name'].tolist()



fig = px.bar(df,

x=x,

y=y,

orientation='h',

title='Top Goal Scorers - EPL 2020/2021 - Up to Game Week ' + str(df.shape[0]),

height=400,

text=x,

labels={'x':'Goals', 'y':'Player Name'}) fig.update_layout(xaxis_title="Goals Scored", yaxis_title="Player", plot_bgcolor="#FAFAFA")



fig.update_traces(textposition='outside', textfont={'color': 'white'}) colours = ['blue',] * df.shape[0]

colours[df[df['player_name'] == player].index[0]] = 'crimson'

fig.update_traces(marker_color=colours)





fig = apply_consistent_style(fig)



fig.update_yaxes(title_font=dict(size=16, color='white'), tickfont=dict(family='Courier', color='white', size=12))

fig.update_xaxes(title_font=dict(size=16, color='white'), tickfont=dict(family='Courier', color='white', size=14))



fig.show(config={"displayModeBar": False, "showTips": False});





def plot_line_plot(df, player):

df = df[df['stats.shots.shots_total'].notna()]

x = df[df['player_name'] == player][['stats.shots.shots_total', 'fixture_string']]['fixture_string']

y = df[df['player_name'] == player][['stats.shots.shots_total', 'fixture_string']]['stats.shots.shots_total']

y1 = df[df['player_name'] == player][['stats.shots.shots_on_goal', 'fixture_string']]['stats.shots.shots_on_goal'] fig = go.Figure()



fig.add_trace(go.Scatter(x=x, y=y,

mode='lines+markers', name="Taken"))



fig.add_trace(go.Scatter(x=x, y=y1,

mode='lines+markers', name="On Target"))



fig.update_layout(title= player + " Shots Per Game - 2020/2021",height=400, xaxis_title="Fixture Date", yaxis_title="Shots Taken/On Target") fig.update_xaxes(tickangle=-60,

tickmode = 'array',

tickvals = x,

ticktext= [y[-11:-1] for y in x])





fig.update_layout(hovermode="x unified")

fig.update_layout(hoverlabel=dict(bgcolor="white",font_size=12,font_ family="Rockwell")) fig.update_layout(showlegend=False)



fig = apply_consistent_style(fig)

fig.show(config={"displayModeBar": False, "showTips": False});







def plot_scatter_plot(df, player):

df = df[df['stats.passing.accurate_passes'].notna()]

x = df[df['player_name'] == player]['stats.passing.accurate_passes']

y = df[df['player_name'] == player]['stats.passing.passes']



x = np.array(x, dtype=float)

y = np.array(y, dtype=float)



fig = px.scatter(df,

x=x,

y=y,

title= player + " Attempted Passes vs. Accurate Passes - 2020/2021",

height=400,

labels={'x':'Completed', 'y':'Attempted'}, trendline='ols', trendline_color_override="red") fig.update_layout(xaxis_title="Accurate Passes", yaxis_title="Attempted Passes") fig.update_traces(marker_color='blue') fig = apply_consistent_style(fig)

fig.update_xaxes(showgrid=True)

fig.update_yaxes(showgrid=True)



fig.show(config={"displayModeBar": False, "showTips": False}); #start of computation

epl_strikers_df = pd.read_csv("data/epl_top_strikers_2020_2021.csv") #convert columns to integers

columns_to_convert_to_ints = ['stats.shots.shots_total','stats.shots.shots_on_goal','stats.goals.scored','stats.passing.total_crosses','stats.passing.crosses_accuracy','stats.passing.passes','stats.passing.accurate_passes','stats.passing.passes_accuracy','stats.passing.key_passes','stats.dribbles.attempts','stats.dribbles.success','stats.dribbles.dribbled_past','stats.other.aerials_won','stats.other.offsides','stats.other.hit_woodwork','stats.other.minutes_played']



epl_strikers_df[columns_to_convert_to_ints] = epl_strikers_df[columns_to_convert_to_ints].astype('Int64')

Dash Code:

#library imports

import numpy as np

import pandas as pd

import plotly.graph_objects as go

import plotly.express as px def apply_consistent_style(fig):

fig.update_yaxes(showgrid=False)

fig.update_xaxes(showgrid=False)

fig.update_yaxes(title_font=dict(size=16, color='white'), tickfont=dict(family='Courier', color='white', size=14))

fig.update_xaxes(title_font=dict(size=16, color='white'), tickfont=dict(family='Courier', color='white', size=14)) fig.update_layout(title_font=dict(size=18, family='Arial'), title_x=0.5, title_font_color='white', margin=dict(l=5,r=5,b=10,t=50,pad=0))



fig.update_layout({'plot_bgcolor': 'rgba(0, 0, 0, 0.5)', 'paper_bgcolor': 'rgba(0, 0, 0, 0.3)'})



return fig

def get_and_display_player_image(df, player_name):

image_url = df[df['player_name'] == player_name]['player_image_path'].values[0]



return image_url def plot_horizontal_bar(df, player):



df = df.groupby(['player_name'])['stats.goals.scored'].sum().sort_values(ascending = True)

df = df.reset_index()



x = df['stats.goals.scored'].tolist()

y = df['player_name'].tolist()



fig = px.bar(df,

x=x,

y=y,

orientation='h',

title='Top Goal Scorers - EPL 2020/2021 - Up to Game Week ' + str(df.shape[0]),

height=400,

text=x,

labels={'x':'Goals', 'y':'Player Name'}) fig.update_traces(hovertemplate=None, hoverinfo='skip') fig.update_layout(xaxis_title="Goals Scored", yaxis_title="Player", plot_bgcolor="#FAFAFA")



fig.update_traces(textposition='outside', textfont={'color': 'white'}) colours = ['blue',] * df.shape[0]

colours[df[df['player_name'] == player].index[0]] = 'crimson'

fig.update_traces(marker_color=colours)





fig = apply_consistent_style(fig)



fig.update_yaxes(title_font=dict(size=16, color='white'), tickfont=dict(family='Courier', color='white', size=12))

fig.update_xaxes(title_font=dict(size=16, color='white'), tickfont=dict(family='Courier', color='white', size=14))



#DASH CHANGES

# fig.show(config={"displayModeBar": False, "showTips": False});

return fig





def plot_line_plot(df, player):

df = df[df['stats.shots.shots_total'].notna()]

x = df[df['player_name'] == player][['stats.shots.shots_total', 'fixture_string']]['fixture_string']

y = df[df['player_name'] == player][['stats.shots.shots_total', 'fixture_string']]['stats.shots.shots_total']

y1 = df[df['player_name'] == player][['stats.shots.shots_on_goal', 'fixture_string']]['stats.shots.shots_on_goal'] fig = go.Figure()



fig.add_trace(go.Scatter(x=x, y=y,

mode='lines+markers', name="Taken"))



fig.add_trace(go.Scatter(x=x, y=y1,

mode='lines+markers', name="On Target"))



fig.update_layout(title= player + " Shots Per Game - 2020/2021",height=400, xaxis_title="Fixture Date", yaxis_title="Shots Taken/On Target") fig.update_xaxes(tickangle=-60,

tickmode = 'array',

tickvals = x,

ticktext= [y[-11:-1] for y in x])





fig.update_layout(hovermode="x unified")

fig.update_layout(hoverlabel=dict(bgcolor="white",font_size=12,font_family="Rockwell")) fig.update_layout(showlegend=False)



fig = apply_consistent_style(fig)



#DASH CHANGES

#fig.show(config={"displayModeBar": False, "showTips": False});

return fig





def plot_scatter_plot(df, player):

df = df[df['stats.passing.accurate_passes'].notna()]

x = df[df['player_name'] == player]['stats.passing.accurate_passes']

y = df[df['player_name'] == player]['stats.passing.passes']



x = np.array(x, dtype=float)

y = np.array(y, dtype=float)



fig = px.scatter(df,

x=x,

y=y,

title= player + " Attempted Passes vs. Accurate Passes - 2020/2021",

height=400,

labels={'x':'Completed', 'y':'Attempted'}, trendline='ols', trendline_color_override="red") fig.update_layout(xaxis_title="Accurate Passes", yaxis_title="Attempted Passes") fig.update_traces(marker_color='blue') fig = apply_consistent_style(fig)

fig.update_xaxes(showgrid=True)

fig.update_yaxes(showgrid=True)



#DASH CHANGES

#fig.show(config={"displayModeBar": False, "showTips": False});

return fig #read in data

epl_strikers_df = pd.read_csv("data/epl_top_strikers_2020_2021.csv") #convert columns to integers

columns_to_convert_to_ints = ['stats.shots.shots_total', 'stats.shots.shots_on_goal', 'stats.goals.scored',

'stats.passing.total_crosses', 'stats.passing.crosses_accuracy', 'stats.passing.passes',

'stats.passing.accurate_passes', 'stats.passing.passes_accuracy', 'stats.passing.key_passes',

'stats.dribbles.attempts', 'stats.dribbles.success', 'stats.dribbles.dribbled_past',

'stats.other.aerials_won', 'stats.other.offsides', 'stats.other.hit_woodwork',

'stats.other.minutes_played']



epl_strikers_df[columns_to_convert_to_ints] = epl_strikers_df[columns_to_convert_to_ints].astype('Int64')

#Beginning of Dash-specific code

#dash imports

import dash

import dash_core_components as dcc

import dash_html_components as html

from dash.dependencies import Input, Output

app = dash.Dash(__name__, external_stylesheets=external_stylesheets, title="Dash Example Soccer Analytics Dashboard") external_stylesheets =[' https://codepen.io/chriddyp/pen/bWLwgP.css' app = dash.Dash(__name__, external_stylesheets=external_stylesheets, title="Dash Example Soccer Analytics Dashboard") #Application layout code

'background-size': 'cover'}, children=[

html.H1("Dash Example Soccer Analytics Dashboard", style={'text-align': 'center', 'color': 'white'}), app.layout = html.Div(style={'background-image': 'url(" https://www.lefthudson.com/wp-content/uploads/2019/11/soccer-field-wallpapers-lovely-backgrounds-real-madrid-2017-wallpaper-cave-this-week-of-soccer-field-wallpapers.jpg ")','background-size': 'cover'}, children=[html.H1("Dash Example Soccer Analytics Dashboard", style={'text-align': 'center', 'color': 'white'}), html.Div(style={'display':'flex'} ,children=[

html.H5("Choose a Player for Analysis:", style={'color': 'white', 'margin-right': '5em', 'white-space': 'nowrap'}), dcc.Dropdown(style={'background-color': 'rgba(0, 0, 0, 0.1)', 'width':'100%'},

id='player_choice',

options=[{'label': i, 'value': i} for i in list(epl_strikers_df['player_name'].unique())], value='Bruno Fernandes')

]), html.Br(), html.Div(children=[

html.Div(style={'display': 'inline-block', 'width':'33%', 'position': 'relative', 'top': '-48px'}, children= [

html.Img(id="player_image", style={'display': 'block', 'height': 300, 'width': 300, 'border': '0.5rem solid black', 'position': 'relative', 'margin': 'auto'}),

]),

dcc.Graph(id="horizontal_bar", style={'display': 'inline-block', 'width': '67%'})

]), html.Div(children=[

dcc.Graph(id="line_plot", style={'display': 'inline-block', 'width': '48%'}),

dcc.Graph(id="scatter_plot", style={'display': 'inline-block', 'width': '48%', 'position': 'absolute', 'right': 0})

])

]) #Callback code to permit application interactivity

Output(component_id='player_image', component_property='src'),

[Input(component_id='player_choice', component_property='value')]

)

def update_player_image(player_choice): @app .callback(Output(component_id='player_image', component_property='src'),[Input(component_id='player_choice', component_property='value')]def update_player_image(player_choice): image_src = get_and_display_player_image(epl_strikers_df, player_choice) return image_src

Output('horizontal_bar', 'figure'),

[Input('player_choice', 'value')]

)

def update_horizontal_bar(player_choice): @app .callback(Output('horizontal_bar', 'figure'),[Input('player_choice', 'value')]def update_horizontal_bar(player_choice): fig = plot_horizontal_bar(epl_strikers_df, player_choice) return fig

Output('line_plot', 'figure'),

[Input('player_choice', 'value')]

)

def update_line_plot(player_choice):



fig = plot_line_plot(epl_strikers_df, player_choice) @app .callback(Output('line_plot', 'figure'),[Input('player_choice', 'value')]def update_line_plot(player_choice):fig = plot_line_plot(epl_strikers_df, player_choice) return fig

Output('scatter_plot', 'figure'),

[Input('player_choice', 'value')]

)

def update_scatter_plot(player_choice):



fig = plot_scatter_plot(epl_strikers_df, player_choice)

return fig @app .callback(Output('scatter_plot', 'figure'),[Input('player_choice', 'value')]def update_scatter_plot(player_choice):fig = plot_scatter_plot(epl_strikers_df, player_choice)return fig if __name__ == '__main__':

app.run_server(debug=True)

From the above code we can see that it doesn’t require much Dash-specific code to create a relatively good-looking dashboard application. The code sample above demonstrates how Dash applications are laid out — with layout code to style the appearance of the application, and callback code to permit interactivity. As you can see, I’ve included component_id & component_property explicitly in the first callback before removing these parameter names on consecutive callbacks. This example shows how CSS code can be directly applied to your dashboard layout using the optional style parameter.

The dashboard so far: