Matplotlib Pie Charts
Learn to create and customize pie charts in Python with Matplotlib: colors, explode, donut charts, labels, legends, and saving to file.
Pie charts divide a circle into slices, where each slice's angle is proportional to the value it represents. They work best when you have a small number of categories (ideally 2–6) and want to show how each part relates to the whole — for example, market share by product or budget allocation by department.
This chapter covers everything from a minimal first chart to advanced techniques like donut charts, custom label placement, and saving publication-ready figures. All examples use matplotlib.pyplot, the standard high-level API.
Installation and Setup
If you have not installed Matplotlib yet, run this command in your terminal:
pip install matplotlibEvery example in this chapter starts with this import:
import matplotlib.pyplot as pltplt is the conventional alias for matplotlib.pyplot. It gives you functions like plt.pie(), plt.title(), and plt.show().
Creating a Basic Pie Chart
plt.pie() is the core function. At minimum it takes a sequence of numerical values — the proportions are calculated for you automatically.
import matplotlib.pyplot as plt
labels = ['Desktop', 'Mobile', 'Tablet']
values = [60, 30, 10]
plt.pie(values, labels=labels, autopct='%1.1f%%')
plt.title('Website Visitors by Device')
plt.show()Key parameters used here:
| Parameter | Purpose |
|---|---|
values | Numeric data; Matplotlib converts them to proportions |
labels | Category names drawn next to each slice |
autopct | Format string for percentage labels; '%1.1f%%' gives 60.0% |
The chart starts drawing slices counter-clockwise from the 3-o'clock position by default. The next section explains how to change that.
Controlling Rotation with startangle
Setting startangle=90 rotates the whole chart so the first slice begins at the 12-o'clock position, which is the most natural starting point for most readers.
import matplotlib.pyplot as plt
labels = ['Desktop', 'Mobile', 'Tablet']
values = [60, 30, 10]
plt.pie(
values,
labels=labels,
autopct='%1.1f%%',
startangle=90,
)
plt.title('Website Visitors by Device')
plt.show()startangle accepts any angle in degrees, measured counter-clockwise from the positive x-axis. A value of 90 points straight up.
Customizing Colors
Pass a list of hex color codes or named colors to the colors parameter. The list must be at least as long as your data.
import matplotlib.pyplot as plt
labels = ['Desktop', 'Mobile', 'Tablet']
values = [60, 30, 10]
colors = ['#ff9999', '#66b3ff', '#99ff99']
plt.pie(
values,
labels=labels,
colors=colors,
autopct='%1.1f%%',
startangle=90,
)
plt.title('Website Visitors by Device')
plt.show()You can also use any of Matplotlib's built-in colormaps. For example, to pull three colors from the tab10 palette:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
labels = ['Desktop', 'Mobile', 'Tablet']
values = [60, 30, 10]
cmap = cm.get_cmap('tab10')
colors = [cmap(i) for i in range(len(values))]
plt.pie(values, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
plt.title('Website Visitors by Device')
plt.show()Exploding a Slice
The explode parameter offsets one or more slices outward to draw attention to them. It takes a tuple of float values, one per slice. A value of 0.1 offsets a slice by 10 % of the chart radius.
import matplotlib.pyplot as plt
labels = ['Desktop', 'Mobile', 'Tablet']
values = [60, 30, 10]
colors = ['#ff9999', '#66b3ff', '#99ff99']
explode = (0.1, 0, 0) # offset the first slice (Desktop) only
plt.pie(
values,
labels=labels,
colors=colors,
explode=explode,
autopct='%1.1f%%',
startangle=90,
shadow=True, # drop shadow for depth
)
plt.title('Website Visitors by Device')
plt.show()Setting shadow=True adds a subtle drop shadow that emphasizes the exploded slice even more.
Adding a Title and Legend
Use plt.title() for the chart title and plt.legend() for a separate legend box. A legend is useful when label text is long or when the slices are too small to label clearly.
import matplotlib.pyplot as plt
labels = ['Desktop', 'Mobile', 'Tablet']
values = [60, 30, 10]
colors = ['#ff9999', '#66b3ff', '#99ff99']
plt.pie(
values,
labels=labels,
colors=colors,
autopct='%1.1f%%',
startangle=90,
)
plt.title('Website Visitors by Device')
plt.legend(title='Device type', loc='lower right')
plt.show()loc accepts standard position strings such as 'upper right', 'lower left', and 'center'. You can also provide a bbox_to_anchor tuple for precise pixel-level placement.
Controlling Label Distances
Two parameters control how far labels sit from the center of the chart:
labeldistance— distance of the category label from the center, as a fraction of the radius. Default is1.1(just outside the slice).pctdistance— distance of the autopct percentage label from the center. Default is0.6(inside the slice).
import matplotlib.pyplot as plt
labels = ['Desktop', 'Mobile', 'Tablet']
values = [60, 30, 10]
plt.pie(
values,
labels=labels,
autopct='%1.1f%%',
startangle=90,
labeldistance=1.2, # push category labels further out
pctdistance=0.75, # move percentages slightly outward from center
)
plt.title('Website Visitors by Device')
plt.show()Increase pctdistance when slices are large and the default position looks cluttered; decrease it when you have many small slices.
Creating a Donut Chart
A donut chart is a pie chart with a hole in the middle. Use the wedgeprops parameter to set a ring width, expressed as a fraction of the radius. For example, width=0.5 leaves 50 % of the radius as the hole.
import matplotlib.pyplot as plt
labels = ['Desktop', 'Mobile', 'Tablet']
values = [60, 30, 10]
colors = ['#ff9999', '#66b3ff', '#99ff99']
plt.pie(
values,
labels=labels,
colors=colors,
autopct='%1.1f%%',
startangle=90,
pctdistance=0.85,
wedgeprops=dict(width=0.5), # ring width = 50 % of radius
)
plt.title('Website Visitors by Device')
plt.show()Donut charts are popular because the empty center provides space for a total count or summary metric, which you can add with plt.text(0, 0, 'Total\n100', ha='center', va='center', fontsize=14).
Multiple Pie Charts Side by Side
Use plt.subplots() to place two or more pie charts on the same figure. Each ax.pie() call targets its own axes object.
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
# --- Chart 1: this month ---
labels = ['Desktop', 'Mobile', 'Tablet']
values_this_month = [60, 30, 10]
ax1.pie(values_this_month, labels=labels, autopct='%1.1f%%', startangle=90)
ax1.set_title('This Month')
# --- Chart 2: last month ---
values_last_month = [70, 20, 10]
ax2.pie(values_last_month, labels=labels, autopct='%1.1f%%', startangle=90)
ax2.set_title('Last Month')
fig.suptitle('Website Visitors by Device', fontsize=14)
plt.tight_layout()
plt.show()fig.suptitle() adds a title above all subplots. plt.tight_layout() prevents overlapping labels between charts.
Saving a Pie Chart to a File
Replace plt.show() with plt.savefig() to write the chart to disk instead of displaying it in a window. This is essential for scripts running on headless servers or for generating report assets.
import matplotlib.pyplot as plt
labels = ['Desktop', 'Mobile', 'Tablet']
values = [60, 30, 10]
plt.pie(values, labels=labels, autopct='%1.1f%%', startangle=90)
plt.title('Website Visitors by Device')
plt.savefig('pie_chart.png', dpi=150, bbox_inches='tight')Common savefig() options:
| Option | Effect |
|---|---|
dpi=150 | Resolution in dots per inch (72–300 for typical use) |
bbox_inches='tight' | Trims whitespace around the figure |
transparent=True | Saves with a transparent background |
format='svg' | Writes vector SVG instead of raster PNG |
Common Gotchas
Values that do not add up to 100. plt.pie() always normalizes the values so that the slices fill the full circle. If your values represent percentages that already sum to 100, this is fine. If they represent raw counts, that is also fine — Matplotlib divides each value by the total. The autopct label always shows the proportion, not the raw value.
Zero or negative values. Matplotlib silently skips zero-value slices (they produce no visible slice). Negative values raise a ValueError. Filter them out before calling plt.pie().
Too many slices. Pie charts become unreadable with more than 6–8 slices. For many categories, consider a bar chart instead. Alternatively, group the smallest categories into an "Other" slice.
Label overlap. When slices are small, labels collide. Solutions include increasing labeldistance, using a legend instead of inline labels (set labels=None and call plt.legend()), or using a donut chart with more room on the outside.
Quick Reference
| Parameter | Type | Default | Purpose |
|---|---|---|---|
x | sequence | — | The data values |
labels | list | None | Category names |
colors | list | cycle | Slice colors |
explode | tuple | None | Per-slice offset from center |
autopct | str / callable | None | Format for percentage labels |
pctdistance | float | 0.6 | Distance of pct label from center |
labeldistance | float | 1.1 | Distance of category label from center |
startangle | float | 0 | Starting angle in degrees |
shadow | bool | False | Drop shadow |
wedgeprops | dict | None | Properties passed to each wedge (e.g. width for donut) |
counterclock | bool | True | Direction slices are drawn |
Related Topics
- Matplotlib Bar Charts — better than pie charts when you have many categories or need precise comparisons.
- Matplotlib Histograms — for visualizing the distribution of continuous data.
- Matplotlib Labels — how to add and style axis labels, titles, and annotations.
- Matplotlib Subplots — placing multiple charts on one figure.