W3docs

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 matplotlib

Every example in this chapter starts with this import:

import matplotlib.pyplot as plt

plt 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:

ParameterPurpose
valuesNumeric data; Matplotlib converts them to proportions
labelsCategory names drawn next to each slice
autopctFormat 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 is 1.1 (just outside the slice).
  • pctdistance — distance of the autopct percentage label from the center. Default is 0.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:

OptionEffect
dpi=150Resolution in dots per inch (72–300 for typical use)
bbox_inches='tight'Trims whitespace around the figure
transparent=TrueSaves 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

ParameterTypeDefaultPurpose
xsequenceThe data values
labelslistNoneCategory names
colorslistcycleSlice colors
explodetupleNonePer-slice offset from center
autopctstr / callableNoneFormat for percentage labels
pctdistancefloat0.6Distance of pct label from center
labeldistancefloat1.1Distance of category label from center
startanglefloat0Starting angle in degrees
shadowboolFalseDrop shadow
wedgepropsdictNoneProperties passed to each wedge (e.g. width for donut)
counterclockboolTrueDirection slices are drawn
Was this page helpful?