Combining Data Visuals: An Interactive Python Plotly Dashboard For Deeper Storytelling
A tutorial on Python Plotly dashboard code creation for a bubble map and bar chart
In my previous post here on Data at Depth, I gave a few examples of Python Plotly charts you can create to tell a story about global food security data.
The next step you can take is to put all of these data visualizations together in one dashboard interface using a web framework like Plotly dash or Streamlit.
Let’s take a deeper look today at the Python Plotly dash framework.
Python’s Plotly Dash is a powerful tool for creating interactive data visualizations. As a Comp Sci professor, I use it extensively for interactive dashboards.
The great advantage of Plotly dash is the ability of the developer to create web-based applications directly from Python code - no additional web development skills are needed.
Using the same dataset as my previous post (UN food security data), let me show you an example of how to create an interactive and interesting Python Plotly dashboard that contains:
A bubble chart: with variable-sized markers, a bubble chart can represent the severity of undernourishment within a given country. Each bubble visually represents the scale of undernourishment.
A horizontal bar chart: with its rigid organized structure, a horizontal bar chart provides a clear ordered list that emphasizes the 10 most undernourished countries by percentage of population.
A dropdown menu: by Year, allowing the user to see the changing story over a period of time.
The two data visualizations in this dashboard offer a dual perspective on the global picture of undernourishment: one that is geographically broad and another that is focused and comparative. We have a single interface to tell a deeper story
Let’s go step-by-step on how to do it!
The Dataset — UN Global Food Security
The UN food security dataset can be found HERE.
There are a few options to consider when downloading the dataset:
For the CSV file to download, I have chosen all of the countries for all of the years available (2000–2022), along with 2 important indicators:
Prevalence of undernourishment (%)
Number of people undernourished (million)
After downloading, I saved the file as UN_food_insecurity.csv.
For this exercise, we are interested in the prevalence of food insecurity in sheer numbers and percentage for each country The fields that we will need to work with are:
Area: The name of the geographical area, such as a country or region.
Item: The name or description of the item or indicator.
Year: The actual year or range of years as a string.
Unit: The unit of measurement for the data values.
Value: The data value.
On previewing the actual data, there are a few things to consider:
For the Item — this is the indicator field that we specified during the download. We know that there are 4 indicators. We can decide which indicator(s) we want to show with our data visuals.
For the Year — This one is a bit tricky. It is shown as a “range of years” with the middle year being the “actual” year. To keep it simple, let’s just use this value “as is”
For the Value — some of the data use a comparison operator (i.e.,>2.5). We need to have a way to handle this data. The simplest way is to just remove the operator.
Now that we know what data is available, we can work through creating the Python code step-by-step
Step-by-step Python Code Creation
Lets break down the Python code creation into five main sections: libraries needed, data frame creation, setting up the Dash interface, using callback functions, and updating the visualizations.
1. Libraries Needed
To get started, we first import the necessary libraries. Pandas is used for data manipulation, Dash for building the interactive dashboard, and Plotly Express for creating the visualizations.
import pandas as pd
import dash
from dash import dcc, html
from dash.dependencies import Input, Output
import plotly.express as px
Make sure you include these libraries as part of your Python installation, either through the CLI:
pip install pandas dash plotly
Or, if you are using PyCharm like me, using the built-in package manager:
Go to File > Settings (or PyCharm > Preferences on macOS).
Navigate to Project: YourProjectName > Python Interpreter.
Click on the + button to add a new package.
In the search bar, type the name of the package you want to install (e.g., pandas, dash, plotly).
Select the package from the list and click Install Package.
Once the packages are installed, you are good to go.
2. Data Frame Creation
Next, we load the dataset and prepare it for visualization. To do this, we need to access our CSV file and do a bit of fancy footwork with the pandas library:
# Load the dataset
df = pd.read_csv('UN_food_insecurity.csv')
# Filter the dataset for undernourishment data
undernourishment_item = "Prevalence of undernourishment (percent) (3-year average)"
df_filtered = df[df['Item'] == undernourishment_item]
# Further simplify the dataset to include only the relevant columns
df_simplified = df_filtered[['Area', 'Year', 'Value']].copy()
# Convert 'Value' to numeric, ensuring we can plot it
df_simplified['Value'] = pd.to_numeric(df_simplified['Value'], errors='coerce')
# Remove rows where 'Value' is NaN
df_simplified.dropna(subset=['Value'], inplace=True)
In this code snippet, we are filtering for the specific item related to undernourishment, simplifying the dataset to include only relevant columns, and ensuring numeric conversion and cleaning of the 'Value' column
.
3. Setting Up the Dash Interface
The Dash interface is set up with a layout that includes a dropdown for year selection and two Graph components for the map and bar chart visualizations.
# Initialize the Dash app
app = dash.Dash(__name__)
# App layout
app.layout = html.Div([
dcc.Dropdown(
id='year-dropdown',
options=[{'label': year, 'value': year} for year in df_simplified['Year'].unique()],
value=df_simplified['Year'].unique()[0], # Set an initial value
clearable=False,
),
dcc.Graph(id='nutrition-map'),
dcc.Graph(id='top-countries-bar')
])
This code snippet initializes a Dash app, sets up its layout with a dropdown for year selection, and places two graph components. The dropdown is populated with unique years from a DataFrame and sets a default value. The dcc.Graph
components are placeholders for a map and a bar chart that will be rendered with data.
This code is where Plotly dash shines - it eliminates the need for direct HTML, CSS, or JavaScript coding, abstracting away the complexity of web development. This allows developers to create an interactive web app using only Python code.
4. Using Callback Functions
Callback functions are crucial for adding interactivity to the dashboard. In this case, we use a callback to update both the map and the bar chart based on the year selected from the dropdown.
# Callback to update map and bar chart based on selected year
@app.callback(
[Output('nutrition-map', 'figure'),
Output('top-countries-bar', 'figure')],
[Input('year-dropdown', 'value')]
)
The @app.callback
is a decorator in Dash that defines a callback function for updating parts of the app in response to user input.
In the code above, it specifies that when the 'value'
of the 'year-dropdown'
changes, the function should update the figures for the 'nutrition-map'
and 'top-countries-bar'
.
A decorator is a function that modifies the behavior of another function, allowing for reusable code or adding functionality before and after the decorated function is called.
5. Updating the Visualizations
Within the callback function, we filter the dataset based on the selected year, update the map to display undernourishment prevalence with red bubbles, and create a horizontal bar chart showing the top 10 countries by undernourishment percentage, also in red.
def update_visualizations(selected_year):
filtered_df = df_simplified[df_simplified['Year'] == selected_year]
# Map
map_fig = px.scatter_geo(filtered_df,
locations="Area",
locationmode='country names',
size="Value",
hover_name="Area",
projection="natural earth",
color_discrete_sequence=["red"], # Make bubbles red
title=f'Prevalence of Undernourishment in {selected_year}')
# Bar chart for top 10 countries
top_countries = filtered_df.nlargest(10, 'Value')
bar_fig = px.bar(top_countries,
x="Value",
y="Area",
orientation='h',
color_discrete_sequence=["red"], # Make bars red
title="Top 10 Countries by Undernourishment Percentage")
bar_fig.update_layout(xaxis_title="Percentage", yaxis_title="Country")
return map_fig, bar_fig
With this code snippet, the function update_visualizations
takes a selected_year
, filters a DataFrame for that year, and then generates two visualizations: a geographic scatter plot (a map) showing the prevalence of undernourishment by country, and a horizontal bar chart ranking the top 10 countries by undernourishment percentage.
Both charts are styled with red elements and include titles specific to the selected year. The function returns the two Plotly figure objects for the map and bar chart.
Lastly, to make the dashboard available, we run the Dash app:
# Run the app
if __name__ == '__main__':
app.run_server(debug=True)
This structured approach not only simplifies the development process but also ensures our dashboard is both informative and interactive.
Saving and Running Our Dashboard Code
If you Copy/Paste this code in sequence into your Python editor (I like to use Pycharm) and click Run, the dash application will be loaded into your default web browser (at the default port of 8050):
The above image shows that the dash application created in the program ds_article01.py is running on my localhost (127.0.0.1) at port 8050.
Clicking on the link, in the default browser, the interactive Plotly dashboard:
The user can choose the year from the dropdown menu to update both the map and the horizontal bar chart.
By allowing users to select different years, we can show the evolving situation of global food security over time, highlighting issues of global undernourishment in an interactive and meaningful way.
In Summary…
Plotly Dash is a great tool for creating interactive multi-visual dashboards.
To create a Plotly dashboard we need to load the dataset, filter for relevant indicators, create a simplified DataFrame, and build a Dash interface with dropdown selections that trigger updates to the visuals.
If you copy/paste the code in this tutorial in sequence into a Python file (and you have the CSV file in the same directory as your .py file) it will work as-is.
This blueprint works with any dataset that contains global country indicators. I encourage you to try it with some other UN datasets.
With practice and experience, you can expand your toolset to include additional visualizations that are available with the Plotly library. For example, time-series line charts, scatter plots, and choropleth maps.
I encourage you to give these a try!
Thank you for reading.