Let's Learn Dash - A Data Visualization Framework based on Python: Part 2
Python Web Framework
In the previous blog, we made a website that showed us the data on avocados and was quite beautiful. In continuation with the previous blog Let's Learn Dash - A Data Visualization Framework based on Python: Part 1, we are going to add styling and user interactivity. So let's dive deep into coding.
Declaring variables
We are now going to extract the unique values of region and avocado_types and save them in variables in app.py
which will later be used in the drop-down menu creation
# app.py
# The preprocessed data is saved in a CSV and passed as a param in data which is attribute of Dash interface. Here it is sorted by the time and we will be working with the dates
data = (
pd.read_csv("avocado.csv")
.assign(Date=lambda data: pd.to_datetime(data["Date"], format="%Y-%m-%d"))
.sort_values(by="Date")
)
# We extract the datas and save them in variables
regions = data["region"].sort_values().unique()
avocado_types = data["type"].sort_values().unique()
Adding styling and new components
In the last blog, I explained how to add inline CSS. If you do not remember, well here's the code snippet(look for the style attribute)
html.Div(
children="Avocado Analytics",
className="Header",
style={"fontSize": "48px", "color": "red"},
)
But now we are going to use external CSS, so create a file in assets
folder and name it style.css
and add the below code.
/* style.css */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Lato", sans-serif;
margin: 0;
background-color: #F7F7F7;
}
.header {
background-color: #222222;
height: 288px;
padding: 16px 0 0 0;
}
.header-emoji {
font-size: 48px;
margin: 0 auto;
text-align: center;
}
.header-title {
color: #FFFFFF;
font-size: 48px;
font-weight: bold;
text-align: center;
margin: 0 auto;
}
.header-description {
color: #CFCFCF;
margin: 4px auto;
text-align: center;
max-width: 384px;
}
.wrapper {
margin-right: auto;
margin-left: auto;
max-width: 1024px;
padding-right: 10px;
padding-left: 10px;
margin-top: 32px;
}
.card {
margin-bottom: 24px;
box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.18);
}
.menu {
height: 112px;
width: 912px;
display: flex;
justify-content: space-evenly;
padding-top: 24px;
margin: -80px auto 0 auto;
background-color: #FFFFFF;
box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.18);
}
.Select-control {
width: 256px;
height: 48px;
}
.Select--single>.Select-control .Select-value,
.Select-placeholder {
line-height: 48px;
}
.Select--multi .Select-value-label {
line-height: 32px;
}
.menu-title {
margin-bottom: 6px;
font-weight: bold;
color: #079A82;
}
Now we have to link this CSS file to our project. Let's do that. Add the following code in app.py
# app.py
#...
# extrernal_stylesheets is used to use external files like CSS or JS Files
# rel:"stylesheet" will look for all stylesheet/ CSS files in the assets folder. For JS file you can use external_scripts.
external_stylesheets = [
{
"href": (
"https://fonts.googleapis.com/css2?"
"family=Lato:wght@400;700&display=swap"
),
"rel": "stylesheet",
},
]
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# ...
In the dash application, we send the external_stylesheets param as the external_stylesheets which was a dictionary in which we have explicitly mentioned the Google Font used and stylesheet. Now we will be adding the class names to the HTML components, so here will we modify our app.layout
code. The below code snippet primarily composes of three things: Header, Drop-Down Menu and Graph Visualization Dashboard
# app.py
app.title = "Avocado Analytics: Understand Your Avocados!"
# It is based on React Framework. So we will be building components of application.
app.layout = html.Div(
children=[
# Header
html.Div(
children=[
html.P(children="๐ฅ", className="header-emoji"),
html.H1(
children="Avocado Analytics", className="header-title"
),
html.P(
children=(
"Analyze the behavior of avocado prices and the number"
" of avocados sold in the US between 2015 and 2018"
),
className="header-description",
),
],
className="header",
),
# Drop-Down Menu
html.Div(
children=[
html.Div(
children=[
html.Div(children="Region", className="menu-title"),
dcc.Dropdown(
id="region-filter",
options=[
{"label": region, "value": region}
for region in regions
],
value="Albany",
clearable=False,
className="dropdown",
),
]
),
html.Div(
children=[
html.Div(children="Type", className="menu-title"),
dcc.Dropdown(
id="type-filter",
options=[
{
"label": avocado_type.title(),
"value": avocado_type,
}
for avocado_type in avocado_types
],
value="organic",
clearable=False,
searchable=False,
className="dropdown",
),
],
),
html.Div(
children=[
html.Div(
children="Date Range", className="menu-title"
),
dcc.DatePickerRange(
id="date-range",
min_date_allowed=data["Date"].min().date(),
max_date_allowed=data["Date"].max().date(),
start_date=data["Date"].min().date(),
end_date=data["Date"].max().date(),
),
]
),
],
className="menu",
),
# Graphs
html.Div(
children=[
html.Div(
children=dcc.Graph(
id="price-chart",
config={"displayModeBar": True},
),
className="card",
),
html.Div(
children=dcc.Graph(
id="volume-chart",
config={"displayModeBar": False},
),
className="card",
),
],
className="wrapper",
),
]
)
Let's understand the snippet of the code
dcc.Dropdown( id="region-filter", options=[ {"label": region, "value": region} for region in regions], value="Albany", clearable=False, className="dropdown", )
id
is the identifier of this element. Here, the value for the region is saved inregion-filter
. Later we are going to use these values in callback functions.options
indicates the options shown when the dropdown is selected. It expects a dictionary with labels and values.value
is the default value when the page loads.clearable
allows the user to leave this field empty if set toTrue
.className
is a CSS class selector used for applying styles.Notice for
for region in regions
,region
is the loop variable name andregions
is the variable in which we saved the values earlier in the blog.
Now as we have accumulated the values we will be now sending them in functions known as callback functions which uses @app.callback
decorator to be declared.
What are callback functions
In Dash, callbacks are functions that are triggered by user interaction. For example, a user might click on a button or select a value from a dropdown menu. When this happens, the callback function is called and it can update the app's layout in response. In the params of @app.callback we declare the input and output variables with their type and then write functions in normal Python syntax. Here is the code to be added in app.py
# This is Reactive Programming. Reactive Programming is a concept paradigm such that the website is always rendering based on the input.
# In the below code snippet we designate what will be the input and what will be the output.
@app.callback(
Output("price-chart", "figure"),
Output("volume-chart", "figure"),
Input("region-filter", "value"),
Input("type-filter", "value"),
Input("date-range", "start_date"),
Input("date-range", "end_date"),
)
# Callback functons are created in which the values are taken from the user and sent to the Dash function where the figures are rendered.
def update_charts(region, avocado_type, start_date, end_date):
filtered_data = data.query(
"region == @region and type == @avocado_type"
" and Date >= @start_date and Date <= @end_date"
)
price_chart_figure = {
"data": [
{
"x": filtered_data["Date"],
"y": filtered_data["AveragePrice"],
"type": "lines",
"hovertemplate": "$%{y:.2f}<extra></extra>",
},
],
"layout": {
"title": {
"text": "Average Price of Avocados",
"x": 0.05,
"xanchor": "left",
},
"xaxis": {"fixedrange": True},
"yaxis": {"tickprefix": "$", "fixedrange": True},
"colorway": ["#17B897"],
},
}
volume_chart_figure = {
"data": [
{
"x": filtered_data["Date"],
"y": filtered_data["Total Volume"],
"type": "lines",
},
],
"layout": {
"title": {"text": "Avocados Sold", "x": 0.05, "xanchor": "left"},
"xaxis": {"fixedrange": True},
"yaxis": {"fixedrange": True},
"colorway": ["#E12D39"],
},
}
return price_chart_figure, volume_chart_figure
What is reactive programming?
Not be mistaken with the famous React.JS framework, Reactive Programming is a conceptual paradigm such that the website is always rendered based on the input that is when a user interacts with an input component, such as a dropdown or a range slider, the output, such as a graph, immediately responds to the changes in the input.
Let's understand the above code
in @app.callback decorator we have declared what will be our input and output. In update_charts
function, we are going to write the code for the updation of the chart based on the user input.
filtered_data = data.query( "region == @region and type == @avocado_type" " and Date >= @start_date and Date <= @end_date" )
, In this code snippet, the data is filter region is the identifier and @region is the value which was taken as input and saved in region-filter
and passed as param region
in the callback function.
Note:
The inputs of the callback function should be in the SAME ORDER as that of the inputs taken from the drop-down menu, or else the website will crash. So in the HTML declaration, keep the input in order.
price_chart_figure = {
"data": [
{
"x": filtered_data["Date"],
"y": filtered_data["AveragePrice"],
"type": "lines",
"hovertemplate": "$%{y:.2f}<extra></extra>",
},
],
"layout": {
"title": {
"text": "Average Price of Avocados",
"x": 0.05,
"xanchor": "left",
},
"xaxis": {"fixedrange": True},
"yaxis": {"tickprefix": "$", "fixedrange": True},
"colorway": ["#17E897"],
},
}
In the above code snippet,
data
dictionary stores the values based on the pre-processed data saved in filtered_data
and desired attribute and saves them in x,y keys.
filtered_data
includes the data that was in the query (input from the user).
type
attribute is used to declare the type of graph we want (eg: line, bar, pie)
hovertemplate
attribute is responsible for showing the data at a particular point of the graph while hovering over it, along with the data extent(2 decimal points, 3 decimal points and so on).
layout
dictionary stores the title name, position of the title, legend, color of the graph and other layout details.
The final app.py code
# importing the libraries
import dash
import pandas as pd
from dash import Dash, Input, Output, dcc, html
# The preprocessed data is saved in a CSV and passed as a param in data which is attribute of Dash interface. Here it is sorted by the time and we will be working with the dates
data = (
pd.read_csv("avocado.csv")
.assign(Date=lambda data: pd.to_datetime(data["Date"], format="%Y-%m-%d"))
.sort_values(by="Date")
)
# We extract the datas and save them in variables
regions = data["region"].sort_values().unique()
avocado_types = data["type"].sort_values().unique()
# extrernal_stylesheets is used to use external files like CSS or JS Files
# rel:"stylesheet" will look for all stylesheet/ CSS files in the assets folder. For JS file you can use external_scripts.
external_stylesheets = [
{
"href": (
"https://fonts.googleapis.com/css2?"
"family=Lato:wght@400;700&display=swap"
),
"rel": "stylesheet",
},
]
app = Dash(__name__, external_stylesheets=external_stylesheets)
app.title = "Avocado Analytics: Understand Your Avocados!"
# It is based on React Framework. So we will be building components of application.
app.layout = html.Div(
children=[
html.Div(
children=[
html.P(children="๐ฅ", className="header-emoji"),
html.H1(
children="Avocado Analytics", className="header-title"
),
html.P(
children=(
"Analyze the behavior of avocado prices and the number"
" of avocados sold in the US between 2015 and 2018"
),
className="header-description",
),
],
className="header",
),
html.Div(
children=[
html.Div(
children=[
html.Div(children="Region", className="menu-title"),
dcc.Dropdown(
id="region-filter",
options=[
{"label": region, "value": region}
for region in regions
],
value="Albany",
clearable=False,
className="dropdown",
),
]
),
html.Div(
children=[
html.Div(children="Type", className="menu-title"),
dcc.Dropdown(
id="type-filter",
options=[
{
"label": avocado_type.title(),
"value": avocado_type,
}
for avocado_type in avocado_types
],
value="organic",
clearable=False,
searchable=False,
className="dropdown",
),
],
),
html.Div(
children=[
html.Div(
children="Date Range", className="menu-title"
),
dcc.DatePickerRange(
id="date-range",
min_date_allowed=data["Date"].min().date(),
max_date_allowed=data["Date"].max().date(),
start_date=data["Date"].min().date(),
end_date=data["Date"].max().date(),
),
]
),
],
className="menu",
),
html.Div(
children=[
html.Div(
children=dcc.Graph(
id="price-chart",
config={"displayModeBar": True},
),
className="card",
),
html.Div(
children=dcc.Graph(
id="volume-chart",
config={"displayModeBar": False},
),
className="card",
),
],
className="wrapper",
),
]
)
# This is Reactive Programming. Reactive Programming is a concept paradigm such that the website is always rendering based on the input.
# In the below code snippet we designate what will be the input and what will be the output.
@app.callback(
Output("price-chart", "figure"),
Output("volume-chart", "figure"),
Input("region-filter", "value"),
Input("type-filter", "value"),
Input("date-range", "start_date"),
Input("date-range", "end_date"),
)
# Callback functons are created in which the values are taken from the user and sent to the Dash function where the figures are rendered.
def update_charts(region, avocado_type, start_date, end_date):
filtered_data = data.query(
"region == @region and type == @avocado_type"
" and Date >= @start_date and Date <= @end_date"
)
price_chart_figure = {
"data": [
{
"x": filtered_data["Date"],
"y": filtered_data["AveragePrice"],
"type": "lines",
"hovertemplate": "$%{y:.2f}<extra></extra>",
},
],
"layout": {
"title": {
"text": "Average Price of Avocados",
"x": 0.5,
"xanchor": "left",
},
"xaxis": {"fixedrange": True},
"yaxis": {"tickprefix": "$", "fixedrange": True},
"colorway": ["#17B897"],
},
}
volume_chart_figure = {
"data": [
{
"x": filtered_data["Date"],
"y": filtered_data["Total Volume"],
"type": "lines",
},
],
"layout": {
"title": {"text": "Avocados Sold", "x": 0.05, "xanchor": "left"},
"xaxis": {"fixedrange": True},
"yaxis": {"fixedrange": True},
"colorway": ["#E12D39"],
},
}
return price_chart_figure, volume_chart_figure
if __name__ == "__main__":
app.run_server(debug=True)
The final ouput
Congratulations you have successfully built a data visualization website using Python, don't forget to simulate the code, upload it to GitHub and resume
If you want a longer and more detailed version of this blog, you can refer to this blog Develop Data Visualization Interfaces in Python With Dash โ Real Python
I hope this blog added a new skill set and you would explore more. We will again meet in the next blog.
Don't forget to give your opinion. It is highly valuable for my upcoming blogs
Follow me on
Subscribe to my newsletter to get notifications when I publish a new blog