Stack Plots & Area Charts

Reviewed & published by Brayan K

A stack plot layers several filled series on top of one another so the top edge traces the running total while each colored band shows how much one series contributes over time.

Part of the free Matplotlib course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

In this lesson you'll fill the area under a single curve, stack multiple series with ax.stackplot(), add a legend, normalize to a 100% chart, and change the baseline.

What You'll Learn in This Lesson

1 A Simple Area Chart

Before stacking many series, start with one. An area chart is just a line plot with the space beneath it filled. You can build it with ax.fill_between(x, y) or, for a single series, with ax.stackplot(x, y). Both shade everything from the curve down to zero, giving a sense of volume rather than just a thin line.

import matplotlib.pyplot as plt
import numpy as np

days = np.arange(1, 13)
visitors = [120, 150, 170, 140, 200, 230, 260, 240, 280, 300, 330, 360]

fig, ax = plt.subplots()
ax.stackplot(days, visitors, color="mediumseagreen", alpha=0.7)
ax.plot(days, visitors, color="darkgreen")   # crisp top edge

ax.set_title("Daily Website Visitors")
ax.set_xlabel("Day")
ax.set_ylabel("Visitors")

plt.show()
The chart this code draws: Fill under one curve with ax.stackplot()
What this code draws, rendered with Matplotlib.

What you'll see: a green filled region climbing from left to right, capped by a darker green line that traces the visitor count. The filled body makes the upward trend feel substantial — you see growing volume, not just a rising line.

2 Stacking Multiple Series

Pass several y arrays to ax.stackplot() and Matplotlib layers them automatically: the first series sits on the bottom, the next stacks on top, and so on. The top edge of the whole stack shows the total. Add a labels list and call ax.legend() so each colored band is named.

import matplotlib.pyplot as plt
import numpy as np

years = np.arange(2018, 2025)
solar = [5, 8, 12, 18, 26, 35, 48]
wind  = [20, 24, 28, 30, 33, 36, 40]
hydro = [15, 15, 16, 16, 17, 17, 18]

fig, ax = plt.subplots()
ax.stackplot(years, solar, wind, hydro,
             labels=["Solar", "Wind", "Hydro"],
             alpha=0.8)

ax.set_title("Renewable Energy Output by Source")
ax.set_xlabel("Year")
ax.set_ylabel("Output (TWh)")
ax.legend(loc="upper left")

plt.show()
The chart this code draws: Layer three energy sources with a legend
What this code draws, rendered with Matplotlib.

What you'll see: three colored bands stacked into one growing mountain. Hydro forms a steady base, Wind adds a thick middle layer, and Solar — the fastest-growing — fattens the top over time. The very top edge shows the combined total climbing year after year.

3 100% Stacked & the Baseline

Sometimes you care about share, not absolute size. A 100% stacked chart normalizes every column so the series always fill the full height — each band shows its percentage of the whole. Divide each series by the total at each x. You can also change the baseline argument to "sym" or "wiggle" for a centered streamgraph look.

import matplotlib.pyplot as plt
import numpy as np

months = np.arange(1, 13)
mobile  = np.array([30, 35, 40, 45, 50, 55, 60, 62, 65, 68, 70, 72])
desktop = np.array([60, 55, 52, 48, 44, 40, 36, 34, 31, 28, 26, 24])
tablet  = np.array([10, 10, 8, 7, 6, 5, 4, 4, 4, 4, 4, 4])

total = mobile + desktop + tablet
fig, ax = plt.subplots()
ax.stackplot(months,
             mobile / total * 100,
             desktop / total * 100,
             tablet / total * 100,
             labels=["Mobile", "Desktop", "Tablet"],
             alpha=0.85)

ax.set_title("Traffic Share by Device (100% Stacked)")
ax.set_xlabel("Month")
ax.set_ylabel("Share (%)")
ax.set_ylim(0, 100)
ax.legend(loc="lower left")

plt.show()
The chart this code draws: Normalize series to show share of the whole
What this code draws, rendered with Matplotlib.

What you'll see: a chart that fills the full 0–100 height at every month. The Mobile band steadily swells while Desktop shrinks, vividly showing the shift to mobile as a change in share rather than raw counts.

🎯 Your Turn: Fill in the Blanks

Replace each ___ to stack two series and show a legend.

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(1, 8)
free = [50, 55, 60, 58, 65, 70, 75]
paid = [10, 14, 18, 22, 26, 30, 36]

fig, ax = plt.subplots()

# Stack the two series with labels
ax.___(x, free, paid, labels=["Free", "Paid"], alpha=0.8)

# Show the legend so the bands are named
ax.___()

ax.set_ylabel("Users")

plt.show()

📋 Common Errors & Quick Reference

Every stacked series must be the same length as x. Double-check that all arrays line up.

❌ The legend is empty

Pass a labels list to stackplot and call ax.legend() — you need both.

❌ The 100% chart doesn't reach the top

You divided by the wrong total. Sum the series element-wise with np.array values, then divide each one by that sum.

CallWhat It Does
ax.stackplot(x, a, b)Stack series as areas
labels=[...]Name each layer
a / total * 100Normalize to percent
baseline="wiggle"Streamgraph look
ax.fill_between(x, y)Single filled area

🎯 Mini-Challenge: Absolute vs Share

Show three product lines two ways: a normal stacked area chart on the left (absolute totals) and a 100% stacked chart on the right (share of the whole).

import matplotlib.pyplot as plt
import numpy as np

q = np.arange(1, 9)
basic = np.array([20, 22, 25, 28, 30, 33, 36, 40])
pro   = np.array([10, 13, 17, 22, 28, 34, 41, 50])
team  = np.array([5, 6, 8, 9, 11, 13, 16, 20])
labels = ["Basic", "Pro", "Team"]
total = basic + pro + team

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 4))

# Left: absolute stacked area
ax1.stackplot(q, basic, pro, team, labels=labels, alpha=0.85)
ax1.set_title("Absolute (TWh)")
ax1.set_xlabel("Quarter")
ax1.legend(loc="upper left", fontsize=8)

# Right: 100% stacked
ax2.stackplot(q, basic/total*100, pro/total*100, team/total*100,
              labels=labels, alpha=0.85)
ax2.set_title("Share (%)")
ax2.set_xlabel("Quarter")
ax2.set_ylim(0, 100)

fig.suptitle("Subscriptions: Totals vs Share")
plt.tight_layout()
plt.show()
The chart this code draws: Stacked totals beside a 100% stacked share chart
What this code draws, rendered with Matplotlib.
import matplotlib.pyplot as plt

years = [2021, 2022, 2023]
web = [10, 14, 18]
app = [5, 9, 15]
other = [2, 2, 3]

fig, ax = plt.subplots()
polys = ax.stackplot(years, web, app, other, labels=["web", "app", "other"])
ax.legend(loc="upper left")

totals = [w + a + o for w, a, o in zip(web, app, other)]
print("Layers:", len(polys))
print("Legend entries:", [t.get_text() for t in ax.get_legend().get_texts()])
print("Totals per year:", totals)
print("Top of the stack matches the total:", max(totals) == totals[-1])
print("Y axis reaches the total:", ax.get_ylim()[1] >= max(totals))

# ✅ Expected output:
# Layers: 3
# Legend entries: ['web', 'app', 'other']
# Totals per year: [17, 25, 36]
# Top of the stack matches the total: True
# Y axis reaches the total: True
The chart this code draws: One polygon per layer, and the top of the stack matching the totals you can add up yourself
What this code draws, rendered with Matplotlib.

❓ Frequently Asked Questions

Lesson complete — you can show parts of a whole over time!

You filled a single area, stacked multiple series with ax.stackplot(), added a legend, normalized to a 100% chart, and learned how the baseline changes the look.

Practice quiz

Which method stacks several filled series as areas?

  • ax.areaplot()
  • ax.stackplot()
  • ax.stack()
  • ax.fillplot()

Answer: ax.stackplot(). ax.stackplot(x, a, b, c) layers the series into a stacked area chart.

What does the top edge of a stack plot show?

  • The first series only
  • The smallest series
  • The running total
  • The average

Answer: The running total. The top edge traces the running total of all stacked series.

How do you give each stacked band a name?

  • Pass labels=[...] to stackplot
  • Pass names=[...]
  • Pass title=[...]
  • Use ax.set_band()

Answer: Pass labels=[...] to stackplot. Pass a labels list to stackplot, then call ax.legend().

What two steps make a legend appear on a stack plot?

  • Only call ax.legend()
  • Only pass labels=
  • Pass labels= and call ax.legend()
  • Set baseline='legend'

Answer: Pass labels= and call ax.legend(). You need both a labels list and a call to ax.legend().

How do you make a 100% stacked chart?

  • Pass percent=True
  • Divide each series by the total at each x
  • Use baseline='100'
  • Call ax.normalize()

Answer: Divide each series by the total at each x. Normalize each series by the element-wise total so they fill the full height.

Which baseline value creates a streamgraph look?

  • 'zero'
  • 'edge'
  • 'flat'
  • 'wiggle'

Answer: 'wiggle'. baseline='wiggle' (or 'sym') centers the stack for a flowing streamgraph.

What is the default baseline for stackplot?

  • 'zero'
  • 'sym'
  • 'wiggle'
  • 'weighted_wiggle'

Answer: 'zero'. The default 'zero' stacks upward from the x-axis.

Which call fills the area under a single curve?

  • ax.under(x, y)
  • ax.shade(x, y)
  • ax.area(x, y)
  • ax.fill_between(x, y)

Answer: ax.fill_between(x, y). ax.fill_between(x, y) shades from the curve down to zero.

What error appears if a stacked series differs in length from x?

  • KeyError
  • ValueError: shape mismatch
  • NameError
  • ImportError

Answer: ValueError: shape mismatch. Every stacked series must match the length of x, or a shape mismatch ValueError occurs.

On a 100% stacked chart, what should ax.set_ylim be?

  • (0, 1)
  • (0, 10)
  • (0, 100)
  • (0, 360)

Answer: (0, 100). After normalizing to percentages, set the y-axis to (0, 100).

Continue this course

Frequently asked questions

What is a stack plot?

A stack plot draws several series stacked on top of one another as filled areas, so the top of the stack shows the running total and each colored band shows one series' contribution. Call ax.stackplot(x, series1, series2, ...) and Matplotlib layers them automatically. It is the area-chart cousin of a stacked bar chart for continuous x values like time.

How do I add a legend to a stack plot?

Pass a labels list to ax.stackplot(..., labels=['A', 'B', 'C']) and then call ax.legend(). Matplotlib matches each label to its colored band so readers can tell the layers apart. Placing the legend in an empty corner keeps it from covering the data.

How do I make a 100% stacked area chart?

Normalize each column so the series sum to one (or 100). Divide every series by the total at each x: take the element-wise sum across series, then divide each series by it and multiply by 100. After normalizing, the stack always fills the full height, showing each series' share of the whole.

What does the baseline argument do?

baseline controls where the stack is anchored. The default 'zero' stacks upward from the x-axis. 'sym' centers the stack symmetrically around zero, and 'wiggle' minimizes the slope changes to create a flowing streamgraph. Switching the baseline changes the look without changing the data.