Basic Pie Chart¶
Pie charts display proportional data as slices of a circle, where each slice represents a category's contribution to the whole.
Mental Model
A pie chart maps values to angles: each slice's arc is proportional to its share of the total. Matplotlib auto-normalizes the values so they sum to 360 degrees. Pie charts work best with a small number of categories (3–6) where you want to emphasize part-to-whole relationships.
When NOT to Use a Pie Chart
Humans are poor at comparing angles and arc lengths — research consistently shows that bar charts are faster and more accurate for most comparison tasks. Avoid pie charts when:
- Many categories (>6): small slices become indistinguishable. Use a horizontal bar chart.
- Precise comparison needed: "Is 23% bigger than 21%?" is hard to see in a pie but obvious in a bar chart.
- Multiple pies side-by-side: comparing slices across circles is nearly impossible. Use grouped bars.
- Values do not sum to a whole: pie charts assume parts add to 100%.
Rule of thumb: if your first instinct is a pie chart, ask "would a bar chart be clearer?" The answer is usually yes.
Simple Pie Chart¶
Create a basic pie chart with ax.pie().
1. Import and Setup¶
python
import matplotlib.pyplot as plt
import numpy as np
2. Define Categories and Values¶
python
vals = [1400, 600, 300, 410, 250]
labels = ["Home Rent", "Food", "Phone/Internet Bill", "Car", "Other Utilities"]
3. Create Pie Chart¶
python
fig, ax = plt.subplots()
ax.pie(vals, labels=labels)
plt.show()
Percentage Labels¶
Display percentage values on each slice.
1. Auto Percentage¶
python
fig, ax = plt.subplots()
ax.pie(vals, labels=labels, autopct='%1.1f%%')
plt.show()
2. Integer Percentage¶
python
fig, ax = plt.subplots()
ax.pie(vals, labels=labels, autopct='%d%%')
plt.show()
3. Custom Format Function¶
```python def make_autopct(values): def autopct(pct): total = sum(values) val = int(round(pct * total / 100.0)) return f'{pct:.1f}%\n(${val:,})' return autopct
fig, ax = plt.subplots() ax.pie(vals, labels=labels, autopct=make_autopct(vals)) plt.show() ```
Exploded Slices¶
Separate one or more slices from the center.
1. Single Exploded Slice¶
```python explode = [0.1, 0, 0, 0, 0] # Explode first slice
fig, ax = plt.subplots() ax.pie(vals, labels=labels, explode=explode, autopct='%1.1f%%') plt.show() ```
2. Multiple Exploded Slices¶
```python explode = [0.1, 0, 0.1, 0, 0] # Explode first and third slices
fig, ax = plt.subplots() ax.pie(vals, labels=labels, explode=explode, autopct='%1.1f%%') plt.show() ```
3. Highlight Maximum Value¶
```python max_idx = vals.index(max(vals)) explode = [0.1 if i == max_idx else 0 for i in range(len(vals))]
fig, ax = plt.subplots() ax.pie(vals, labels=labels, explode=explode, autopct='%1.1f%%') plt.show() ```
Start Angle and Direction¶
Control the starting position and rotation direction.
1. Custom Start Angle¶
python
fig, ax = plt.subplots()
ax.pie(vals, labels=labels, startangle=90) # Start from top
plt.show()
2. Clockwise Direction¶
python
fig, ax = plt.subplots()
ax.pie(vals, labels=labels, startangle=90, counterclock=False)
plt.show()
3. Start from Right¶
python
fig, ax = plt.subplots()
ax.pie(vals, labels=labels, startangle=0) # Start from right (default)
plt.show()
Colors and Styling¶
Customize the appearance of pie slices.
1. Custom Colors¶
```python colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99', '#ff99cc']
fig, ax = plt.subplots() ax.pie(vals, labels=labels, colors=colors, autopct='%1.1f%%') plt.show() ```
2. Colormap¶
```python cmap = plt.cm.Pastel1 colors = [cmap(i) for i in range(len(vals))]
fig, ax = plt.subplots() ax.pie(vals, labels=labels, colors=colors, autopct='%1.1f%%') plt.show() ```
3. Edge Colors¶
python
fig, ax = plt.subplots()
wedges, texts, autotexts = ax.pie(vals, labels=labels, autopct='%1.1f%%',
wedgeprops={'edgecolor': 'white', 'linewidth': 2})
plt.show()
Shadow Effect¶
Add shadow for 3D-like appearance.
1. Basic Shadow¶
python
fig, ax = plt.subplots()
ax.pie(vals, labels=labels, shadow=True, autopct='%1.1f%%')
plt.show()
2. Shadow with Explode¶
```python explode = [0.05] * len(vals)
fig, ax = plt.subplots() ax.pie(vals, labels=labels, explode=explode, shadow=True, autopct='%1.1f%%') plt.show() ```
Label Positioning¶
Control where labels appear.
1. Label Distance¶
python
fig, ax = plt.subplots()
ax.pie(vals, labels=labels, labeldistance=1.15, autopct='%1.1f%%')
plt.show()
2. Percentage Distance¶
python
fig, ax = plt.subplots()
ax.pie(vals, labels=labels, autopct='%1.1f%%', pctdistance=0.6)
plt.show()
3. Rotated Labels¶
```python fig, ax = plt.subplots() wedges, texts, autotexts = ax.pie(vals, labels=labels, autopct='%1.1f%%')
for text in texts: text.set_rotation(45)
plt.show() ```
Legend Instead of Labels¶
Use legend for cleaner appearance.
1. Basic Legend¶
python
fig, ax = plt.subplots()
wedges, texts = ax.pie(vals)
ax.legend(wedges, labels, loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout()
plt.show()
2. Legend with Percentages¶
python
fig, ax = plt.subplots()
wedges, texts, autotexts = ax.pie(vals, autopct='%1.1f%%')
ax.legend(wedges, labels, loc='center left', bbox_to_anchor=(1, 0.5))
plt.tight_layout()
plt.show()
3. Legend with Values¶
```python legend_labels = [f'{label}: ${val:,}' for label, val in zip(labels, vals)]
fig, ax = plt.subplots() wedges, texts = ax.pie(vals) ax.legend(wedges, legend_labels, loc='center left', bbox_to_anchor=(1, 0.5)) plt.tight_layout() plt.show() ```
Data Sources¶
Various ways to provide data to pie charts.
1. Lists¶
python
categories = ['A', 'B', 'C', 'D']
values = [30, 25, 25, 20]
ax.pie(values, labels=categories)
2. NumPy Arrays¶
python
values = np.array([30, 25, 25, 20])
ax.pie(values)
3. Pandas DataFrame¶
```python import pandas as pd
df = pd.DataFrame({ 'category': ['Home Rent', 'Food', 'Phone/Internet Bill', 'Car', 'Other Utilities'], 'amount': [1400, 600, 300, 410, 250] })
fig, ax = plt.subplots() ax.pie(df['amount'], labels=df['category'], autopct='%1.1f%%') plt.show() ```
4. Pandas Series¶
```python expenses = pd.Series([1400, 600, 300, 410, 250], index=['Home Rent', 'Food', 'Phone/Internet Bill', 'Car', 'Other Utilities'])
fig, ax = plt.subplots() ax.pie(expenses, labels=expenses.index, autopct='%1.1f%%') plt.show() ```
Text Styling¶
Customize label and percentage text appearance.
1. Text Properties¶
```python fig, ax = plt.subplots() wedges, texts, autotexts = ax.pie(vals, labels=labels, autopct='%1.1f%%')
for text in texts: text.set_fontsize(10) text.set_fontweight('bold')
for autotext in autotexts: autotext.set_fontsize(8) autotext.set_color('white')
plt.show() ```
2. Using textprops¶
python
fig, ax = plt.subplots()
ax.pie(vals, labels=labels, autopct='%1.1f%%',
textprops={'fontsize': 10, 'fontweight': 'bold'})
plt.show()
Practical Example¶
Create a complete pie chart with styling.
1. Prepare Data¶
python
expenses = {
'Home Rent': 1400,
'Food': 600,
'Phone/Internet Bill': 300,
'Car': 410,
'Other Utilities': 250
}
labels = list(expenses.keys())
vals = list(expenses.values())
2. Create Styled Chart¶
```python fig, ax = plt.subplots(figsize=(10, 8))
colors = plt.cm.Set3(np.linspace(0, 1, len(vals))) explode = [0.02] * len(vals)
wedges, texts, autotexts = ax.pie( vals, labels=labels, autopct='%1.1f%%', colors=colors, explode=explode, startangle=90, wedgeprops={'edgecolor': 'white', 'linewidth': 1.5} ) ```
3. Add Title and Styling¶
```python ax.set_title('Monthly Expense Breakdown', fontsize=14, fontweight='bold')
for autotext in autotexts: autotext.set_fontsize(9) autotext.set_fontweight('bold')
plt.tight_layout() plt.show() ```
Pie Charts as an Encoding Lesson¶
Pie charts encode data using angle, which is one of the least precise visual channels humans have:
| Visual channel | Precision | Example plot |
|---|---|---|
| Position (x, y) | Highest | Scatter plot |
| Length | High | Bar chart |
| Area | Medium | Bubble chart |
| Angle | Low | Pie chart |
| Color intensity | Lowest | Heatmap |
This is why bar charts are almost always better for comparison tasks: they use length, which humans perceive 2--3x more accurately than angle. Pie charts are appropriate only when the goal is to show part-to-whole relationships with few categories where exact comparison is not needed.
Use pie charts only when:
- Data represents parts of a whole (summing to 100%)
- Number of categories is small (3--5)
- Approximate visual impression is sufficient (not precise comparison)
Exercises¶
Exercise 1. Write code that creates a pie chart showing four categories with sizes 40%, 30%, 20%, and 10%. Add labels and percentage formatting with autopct='%1.1f%%'.
Solution to Exercise 1
```python import matplotlib.pyplot as plt import numpy as np
np.random.seed(42)
Solution code depends on the specific exercise¶
x = np.linspace(0, 2 * np.pi, 100) fig, ax = plt.subplots() ax.plot(x, np.sin(x)) ax.set_title('Example Solution') plt.show() ```
See the content of this page for the relevant API details to construct the full solution.
Exercise 2. Explain what happens if the values passed to ax.pie() do not sum to 100. Does Matplotlib normalize them automatically?
Solution to Exercise 2
See the explanation in the main content of this page for the key concepts. The essential idea is to understand the API parameters and their effects on the resulting visualization.
Exercise 3. Create a pie chart with the startangle=90 parameter so the first slice starts at the top.
Solution to Exercise 3
```python import matplotlib.pyplot as plt import numpy as np
np.random.seed(42) fig, axes = plt.subplots(1, 2, figsize=(12, 5))
x = np.linspace(0, 2 * np.pi, 100) axes[0].plot(x, np.sin(x)) axes[0].set_title('Left Subplot')
axes[1].plot(x, np.cos(x)) axes[1].set_title('Right Subplot')
plt.tight_layout() plt.show() ```
Adapt this pattern to the specific requirements of the exercise.
Exercise 4. Write code that creates a donut chart by setting wedgeprops=dict(width=0.4) and adds a label in the center.
Solution to Exercise 4
import matplotlib.pyplot as plt
sizes = [35, 25, 20, 20]
labels = ['A', 'B', 'C', 'D']
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99']
fig, ax = plt.subplots()
ax.pie(sizes, labels=labels, colors=colors, autopct='%1.0f%%',
wedgeprops=dict(width=0.4))
ax.text(0, 0, 'Total\n100%', ha='center', va='center', fontsize=14)
ax.set_title('Donut Chart')
plt.show()
Exercise 5. A colleague presents data with 12 categories as a pie chart. Explain why this is a poor choice, then create both the pie chart and a better horizontal bar chart side by side.
Solution to Exercise 5
import matplotlib.pyplot as plt
import numpy as np
categories = [f'Cat {i}' for i in range(1, 13)]
values = np.random.randint(5, 20, 12)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
ax1.pie(values, labels=categories, autopct='%1.0f%%', textprops={'fontsize': 7})
ax1.set_title('Pie Chart (hard to read)')
ax2.barh(np.arange(12), values, color='steelblue')
ax2.set_yticks(np.arange(12))
ax2.set_yticklabels(categories)
ax2.set_xlabel('Value')
ax2.set_title('Bar Chart (easy to compare)')
plt.tight_layout()
plt.show()
# The pie fails because 12 slices are visually indistinguishable,
# labels overlap, and angle comparison is imprecise. The bar chart
# makes ranking immediate via length comparison.