- Lab
- Data

Build a Donut Chart in Python to Visualize Employee Skill Distribution
In this Code Lab, you will build polished donut charts in Python to visualize the distribution of employee skills across a tech company. You will start by creating a basic donut chart using Matplotlib. Next, you will customize it with a color palette, labels, legends, explode effects, and rotation. You will then build an interactive donut chart using Plotly Express, adding hover details and a slice pull to emphasize the largest category. Finally, you will annotate your chart with the total employee count and use pandas to identify the most and least common skills.

Path Info
Table of Contents
-
Challenge
Step 1: Build a Basic Donut Chart with Matplotlib
Step 1: Build a Basic Donut Chart with Matplotlib
In this step, you'll construct a foundational donut chart using Matplotlib. You'll begin by rendering a basic pie chart and then apply styling to transform it into a donut. This includes setting the wedge width and ensuring the chart maintains a consistent aspect ratio.
This step establishes the structural core of the chart. All enhancements in future steps, such as colors, interactivity, and annotations, will build upon this initial configuration.
What You’ll Learn in This Step
- Use
matplotlib.pyplot
to generate a pie chart with categorical labels - Modify pie chart geometry using the
wedgeprops
parameter - Normalize aspect ratio using
axis("equal")
for consistent layout
Open the Notebook
From the workspace panel, open the notebook file:
1-step-one.ipynb
.info> Important: Save your notebook (Ctrl/Cmd + S) before clicking Validate. Validation inspects the most recent saved checkpoint.
Understanding Donut Geometry and
ax.pie()
The foundation of any Matplotlib chart begins with creating a
Figure
andAxes
object usingplt.subplots()
. This object-oriented approach offers fine-grained control over how plots are rendered and customized.To draw a pie chart, Matplotlib provides the
Axes.pie()
method. It takes a sequence of values (such as category counts) and renders them as proportional wedges in a circular plot. For each wedge, you can supply:labels
: a list of category labels to annotate each segmentwedgeprops
: a dictionary that defines how the wedges are styled
To create a donut chart, set
wedgeprops={"width": 0.3}
. This parameter reduces the width of each wedge, creating a hollow center. Unlike pie charts, this space can later be used to display aggregate metrics or annotations.Lastly, the chart won't display automatically unless you call
plt.show()
—a required step when working in scripts or Jupyter notebooks that don’t have auto-rendering enabled.This task focuses purely on initializing and rendering the visual structure of the donut.
Controlling Aspect Ratio for Accurate Donut Charts
Matplotlib allows full control over plot layout through its axis configuration system. When rendering circular visualizations—such as pie or donut charts—maintaining a 1:1 aspect ratio between the x and y axes is critical for preserving geometric fidelity.
By default, Matplotlib may stretch or compress a plot to fit the cell’s available space, especially in notebook environments. This can cause a donut chart to appear elliptical, even if the underlying data is correct.
To enforce a true circle, you must explicitly set the aspect ratio to "equal" using:
ax.axis("equal")
This instructs Matplotlib to allocate equal units along both axes, preventing distortion. It’s a common best practice in all circular data visualizations—especially when visual proportions must be preserved for accurate interpretation.
- Use
-
Challenge
Step 2: Enhance the Donut Chart with Colors and Legends
Step 2: Enhance the Donut Chart with Colors and Legends
In this step, you’ll improve the readability and clarity of your donut chart by applying stylistic enhancements. Using Matplotlib's customization features, you’ll define a color palette, add percentage labels, and include a legend and title for better context.
You’ll also use an explode effect to offset the dominant skill slice and apply rotation to control how the chart is oriented.
What You’ll Learn in This Step
- Apply a categorical color palette to
ax.pie()
- Display percentage labels on each wedge
- Add a chart title and build a custom legend
- Emphasize the largest slice with an explode effect
- Rotate the chart using the
startangle
parameter
Open the Notebook
From the workspace panel, open the notebook file:
2-step-two.ipynb
.info> Important: Save your notebook (Ctrl/Cmd + S) before clicking Validate. Validation inspects the most recent saved checkpoint. ### Applying Color Palettes with Matplotlib’s
get_cmap()
To control the visual styling of your donut chart, Matplotlib allows you to assign a color to each wedge using the
colors
argument inax.pie()
.Instead of hardcoding RGB or hex values, you can use built-in colormap objects. These are retrieved with
plt.cm.get_cmap()
, which returns a color map you can sample programmatically.For categorical data like skills, a qualitative palette such as
"Pastel1"
or"Set3"
works well. To assign colors, call the colormap with the number of categories:colors = plt.cm.get_cmap("Pastel1")(range(len(employee_data)))
This line generates a list of RGBA color values based on the number of skill categories in your dataset.
You then pass this list to the
colors
parameter in yourax.pie()
call:ax.pie(..., colors=colors)
Other Available Colormaps
You can experiment with other pastel or qualitative palettes to adjust the look of your chart. Here are a few compatible options:
"Pastel1" (default in this step)
"Pastel2"
"Set1"
"Set2"
"Set3"
"Accent"
"Paired"
"tab10"
"tab20"
To learn more, visit: https://matplotlib.org/stable/users/explain/colors/colormaps.htmlMatplotlib’s
ax.pie()
function supports displaying percentage labels on each wedge using theautopct
argument. This argument accepts a format string or a function that controls how percentages are displayed.A common approach is to pass a format string like:
autopct="%.1f%%"
This renders each wedge with its percentage value to one decimal place (e.g.,
22.5%
). Internally, Matplotlib calculates these values based on the fraction of the total sum each wedge represents.If you need more control over formatting or filtering, you can supply a custom function to
autopct
instead. However, for most categorical data visualization, a simple format string is sufficient.The percentage labels are automatically placed at the center of each wedge unless offset by
labeldistance
orpctdistance
.Using
autopct
makes the chart self-contained and interpretable without requiring the user to cross-reference a legend or raw data table. ### Adding Titles and Legends for ClarityA visualization is only effective if the audience can interpret it quickly. In Matplotlib, you can add essential context with titles and legends.
Titles
Useax.set_title("Your Title")
to display a descriptive heading above the chart. Titles should summarize what the viewer is seeing in concise terms, such as "Employee Skill Distribution".Legends
Legends in Matplotlib are built usingax.legend()
. Whenax.pie()
is called withlabels=
, those labels can be reused in the legend automatically. You can also customize the legend’s position using theloc
parameter (e.g.,"center left"
,"upper right"
, etc.) and anchor its placement withbbox_to_anchor
.Example:
ax.legend(loc="center left", bbox_to_anchor=(1, 0, 0.5, 1))
This places the legend outside the chart area, ensuring it doesn’t overlap with the donut itself.
- Combined Effect Together, titles and legends provide both immediate chart context and a key for interpreting wedges, reducing cognitive load on the viewer.
This task focuses on integrating both elements so your donut chart communicates data effectively at a glance. ### Highlighting the Largest Wedge Using Explode Logic
Matplotlib’s
explode
parameter allows individual wedges in a pie or donut chart to be offset from the center, visually drawing attention to a particular category. This is especially useful when you want to emphasize the largest segment in a dataset.To determine which wedge represents the dominant category, use the
idxmax()
method on the relevant column of your DataFrame. This function returns the index of the row containing the highest value in a Series. For example, if"count"
is the column representing category totals, the syntax would be:idx = dataframe["count"].idxmax()
This index can then be used to build a list of floats, one for each row in the dataset, where only the largest category receives an offset value (commonly
0.1
), and all others remain at0
. This list must be the same length as the number of wedges you plan to render. Use a list comprehension like the following:explode = [0.1 if i == idx else 0 for i in range(len(dataframe))]
The
explode
list should be passed into theax.pie()
method to apply the offset:ax.pie( dataframe["count"], labels=dataframe["skill"], wedgeprops={"width": 0.3}, autopct="%.1f%%", explode=explode )
The
explode
values control how far each wedge is pushed outward from the chart’s center. This feature is a valuable design tool for drawing attention to specific insights in your data, particularly when identifying top-performing categories. ### Improving Readability with Padding and Font SizeWhen working with donut charts that contain labels and percentage values, small adjustments in padding and text formatting can significantly enhance readability—especially when dealing with long labels or dense chart segments.
Matplotlib allows for these refinements through several parameters inside the
ax.pie()
function:labeldistance
controls the radial distance of category labels from the center. Increasing this value (e.g.,1.15
) pushes the labels further outward, reducing overlap with the chart.pctdistance
adjusts how close the percentage text sits to the center. Lower values (e.g.,0.65
) move the percentages deeper inside the donut ring for balance and clarity.
Both values are expressed as floats relative to the chart's radius.
To further improve visual clarity, you can modify text style directly. After rendering the pie chart,
ax.pie()
returns three elements: wedges, text labels, and percentage labels. You can loop through the percentage labels (autotexts
) and apply custom formatting like font size:for autotext in autotexts: autotext.set_fontsize(10)
This approach is particularly helpful when default font sizes appear too small or inconsistent across different resolutions. These small refinements ensure that your donut chart is not only functionally correct but also visually professional.
- Apply a categorical color palette to
-
Challenge
Step 3: Create an Interactive Donut Chart with Plotly Express
Step 3: Create an Interactive Donut Chart with Plotly Express
In this step, you'll transition from static Matplotlib charts to dynamic Plotly visualizations. Plotly Express makes it easy to convert your donut chart into an interactive experience, complete with hover details, layout customization, and animated transitions.
This step teaches how to recreate the same visual insights from previous steps while enhancing user interaction through a modern plotting library.
What You’ll Learn in This Step
- Create a donut chart using
plotly.express.pie()
with interactive features - Customize the layout, hover behavior, and styling using
update_layout
- Use the
pull
parameter to highlight slices dynamically
Open the Notebook
From the workspace panel, open the notebook file:
3-step-three.ipynb
.info> Important: Save your notebook (Ctrl/Cmd + S) before clicking Validate. Validation inspects the most recent saved checkpoint. ### Generate a Donut Chart Using Plotly Express
Plotly Express is a high-level API that simplifies the creation of interactive visualizations. To generate a donut chart, you use the
px.pie()
function. Although namedpie
, this function supports donut charts by setting thehole
parameter to a value between0
and1
.The two most important arguments for
px.pie()
are:names
: a sequence of categorical values used to label each wedgevalues
: a sequence of numeric values that determine the size of each wedge
Both
names
andvalues
can be passed as column references from a Pandas DataFrame, or as separate sequences. Typically,names
is a column of typeobject
orstring
, whilevalues
must be numeric—either integers or floats. These inputs allow Plotly to compute proportions and assign labels automatically.To create a donut chart, include the
hole
argument:import plotly.express as px fig = px.pie( names=your_dataframe["label_column"], values=your_dataframe["value_column"], hole=0.4 )
The returned object
fig
is a PlotlyFigure
that can be further customized. To display the chart, use:fig.show()
This produces an interactive donut chart with tooltips, hover events, zoom, and export features—all rendered in the notebook. ### Add Hover Data and Color Customization in Plotly Express
Plotly Express supports custom tooltips and color styling to enrich interactive charts. When using
px.pie()
, these features are controlled by thehover_data
andcolor_discrete_sequence
parameters.The
hover_data
argument accepts a list of column names to display on hover. These should reference valid columns from the DataFrame and typically contain numerical values that provide extra insight, such as raw counts or pre-calculated percentages. If a column is not included innames
orvalues
, adding it tohover_data
ensures it still appears on hover.To match the look of earlier Matplotlib charts, you can apply a consistent pastel palette using the
color_discrete_sequence
argument. This parameter expects a list of color values as hex strings. A common technique is to convert colors from Matplotlib’s "Pastel1" colormap usingmatplotlib.colors.to_hex()
:import matplotlib.colors as mcolors cmap = plt.cm.get_cmap("Pastel1") colors = [mcolors.to_hex(cmap(i)) for i in range(len(employee_data))]
This list can then be passed directly into
px.pie()
:px.pie(..., color_discrete_sequence=colors)
Hover data and color consistency enhance readability and ensure a smooth visual transition between static and interactive charts.
Other Available Colormaps
You can experiment with other pastel or qualitative palettes to adjust the look of your chart. Here are a few compatible options:
"Pastel1"
(default in this step)"Pastel2"
"Set1"
"Set2"
"Set3"
"Accent"
"Paired"
"tab10"
"tab20"
To preview them, visit: https://matplotlib.org/stable/users/explain/colors/colormaps.html
Plotly figures can be styled using the
update_layout()
method, which modifies global chart properties like title, legend, margins, and background. This method operates on theFigure
object returned by functions such aspx.pie()
.To set the main chart title, use
title_text
. This is a string that appears above the chart and helps users understand the context of the data.To customize the legend, you can use
legend_title
, which sets a label above the category list on the right-hand side. This is especially useful when thenames
field in your chart is derived from a generic column like "skill".Finally, to control whitespace around the plot, pass a dictionary to the
margin
parameter. For example:fig.update_layout( title_text="Employee Skill Distribution", legend_title="Skill", margin=dict(l=0, r=0, t=50, b=0) )
l
,r
,t
, andb
stand for left, right, top, and bottom margins respectively.- These values are in pixels and control how tightly the chart content is packed.
This method is essential for refining a chart’s appearance in both notebooks and exported dashboards.
Emphasize the Largest Slice in an Interactive Plotly Donut
Plotly Express doesn't support the
pull
argument directly insidepx.pie()
. To offset or emphasize a specific wedge, you use theupdate_traces()
method on the returnedFigure
object.This method lets you set the
pull
value per slice after the chart has been created.pull
takes a list of floats, where each value represents the radial distance to offset that slice. For example, a value of0.1
pulls a slice outward slightly, while0
keeps it in place.To programmatically offset the largest slice:
- First, use
.idxmax()
to find the index of the row with the highest count:idx = employee_data["count"].idxmax()
This returns an integer representing the row position of the dominant skill.
-
Then, build a list of offsets using a list comprehension:
pull_values = [0.1 if i == idx else 0 for i in range(len(employee_data))]
This list ensures only the largest category is emphasized.
Finally, apply the pull setting by calling:
fig.update_traces(pull=pull_values)
This approach creates a subtle highlight that draws the viewer's attention without distorting the chart structure.
- Create a donut chart using
-
Challenge
Step 4: Annotate and Analyze the Chart
Step 4: Annotate and Analyze the Chart
In this step, you'll enhance your visualization by embedding meaningful annotations and summarizing key insights from the dataset. Annotations like totals inside the donut and lists of top/bottom skills help viewers quickly understand the chart's context and scale.
By combining pandas analysis with chart annotations, you'll make your visual output both informative and interactive.
What You’ll Learn in This Step
- Insert dynamic annotations into a Matplotlib chart using
ax.text()
- Use pandas methods like
sum()
,nlargest()
, andnsmallest()
for insight extraction - Combine data summary with visualization for full analytical impact
Open the Notebook
From the workspace panel, open the notebook file:
4-step-four.ipynb
.info> Important: Save your notebook (Ctrl/Cmd + S) before clicking Validate. Validation inspects the most recent saved checkpoint.
Centering Text in Donut Charts with
ax.text()
To display a total value inside the center of a donut chart, you'll combine Matplotlib’s
ax.text()
method with a computed total from your dataset.You can calculate the total number of employees by summing the values in the
count
column using:total = employee_data["count"].sum()
This returns a single number representing the total across all categories.
To place this number in the center of your donut, use:
ax.text(0, 0, f"Total\n{total}", ha="center", va="center")
The coordinates
(0, 0)
correspond to the center of the chart. Thef"Total\n{total}"
string places the label and number on separate lines. Theha
(horizontal alignment) andva
(vertical alignment) parameters ensure the text is centered precisely inside the donut.This technique improves chart readability and offers a quick summary insight directly within the visual. ### Extracting Top and Bottom Records Using
nlargest()
andnsmallest()
Pandas provides built-in methods to quickly identify the largest or smallest records in a DataFrame—helpful for surfacing insights like most or least common values.
nlargest(n, column)
returns the topn
rows sorted by the specified column in descending order.nsmallest(n, column)
returns the bottomn
rows sorted by the specified column in ascending order.
Both methods preserve the original column order and return a new DataFrame. The first argument specifies how many records to return, while the second identifies which column to sort by.
In this task, you’ll use:
employee_data.nlargest(3, "count") employee_data.nsmallest(3, "count")
These two calls return the top and bottom three skills based on frequency. You can pass the results directly to
print()
or assign them to variables if further analysis is needed.This technique is a quick and powerful way to highlight dominant or rare categories in your data.
- Insert dynamic annotations into a Matplotlib chart using
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.