100% Stacked Area Chart — plotnine

A stacked area chart normalized to 100%, where each area represents the percentage contribution of a category to the total. The combined height always equals 100%, showing proportional changes over time.

100% Stacked Area Chart rendered with plotnine

Python source (plotnine)

""" anyplot.ai
area-stacked-percent: 100% Stacked Area Chart
Library: plotnine 0.15.4 | Python 3.13.13
Quality: 83/100 | Updated: 2026-05-12
"""

import os
import sys


sys.path = [p for p in sys.path if p != os.path.dirname(__file__)]

import pandas as pd
from plotnine import (
    aes,
    element_line,
    element_rect,
    element_text,
    geom_area,
    ggplot,
    labs,
    scale_fill_manual,
    scale_x_continuous,
    scale_y_continuous,
    theme,
    theme_minimal,
)


# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"

# Okabe-Ito palette - first position is ALWAYS #009E73
IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233"]

# Data - Market share evolution with more dramatic proportion shifts
years = list(range(2015, 2025))

# Generate category data with realistic and dramatic trends
smartphones = [50, 48, 44, 40, 35, 32, 28, 25, 22, 20]
tablets = [28, 26, 22, 18, 15, 12, 10, 8, 6, 5]
wearables = [4, 8, 14, 20, 26, 32, 38, 42, 46, 48]
laptops = [18, 18, 20, 22, 24, 24, 24, 25, 26, 27]

# Create DataFrame in long format for plotnine
df_list = []
for i, year in enumerate(years):
    total = smartphones[i] + tablets[i] + wearables[i] + laptops[i]
    df_list.append(
        {"Year": year, "Category": "Smartphones", "Value": smartphones[i], "Percent": smartphones[i] / total * 100}
    )
    df_list.append({"Year": year, "Category": "Tablets", "Value": tablets[i], "Percent": tablets[i] / total * 100})
    df_list.append(
        {"Year": year, "Category": "Wearables", "Value": wearables[i], "Percent": wearables[i] / total * 100}
    )
    df_list.append({"Year": year, "Category": "Laptops", "Value": laptops[i], "Percent": laptops[i] / total * 100})

df = pd.DataFrame(df_list)

# Set category order for stacking
df["Category"] = pd.Categorical(
    df["Category"], categories=["Smartphones", "Tablets", "Wearables", "Laptops"], ordered=True
)

# Create plot
plot = (
    ggplot(df, aes(x="Year", y="Percent", fill="Category"))
    + geom_area(position="stack", alpha=0.85)
    + scale_fill_manual(values=IMPRINT)
    + scale_x_continuous(breaks=range(2015, 2025, 2))
    + scale_y_continuous(breaks=[0, 25, 50, 75, 100], labels=["0%", "25%", "50%", "75%", "100%"])
    + labs(
        title="area-stacked-percent · plotnine · anyplot.ai", x="Year", y="Market Share (%)", fill="Product Category"
    )
    + theme_minimal()
    + theme(
        figure_size=(16, 9),
        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),
        panel_background=element_rect(fill=PAGE_BG),
        panel_grid_major=element_line(color=INK, size=0.3, alpha=0.10),
        panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.05),
        panel_border=element_rect(color=INK_SOFT, fill=None),
        plot_title=element_text(size=24, weight="bold", color=INK),
        axis_title=element_text(size=20, color=INK),
        axis_text=element_text(size=16, color=INK_SOFT),
        axis_line=element_line(color=INK_SOFT, size=0.4),
        legend_position="bottom",
        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),
        legend_text=element_text(size=16, color=INK_SOFT),
        legend_title=element_text(size=18, color=INK),
    )
)

# Save
plot.save(f"plot-{THEME}.png", dpi=300, verbose=False)

Part of 100% Stacked Area Chart on anyplot.ai.

Other implementations