Stacked Histogram in plotnine (Python)

The same plot in 14 other libraries — Python: Altair, Bokeh, lets-plot, Matplotlib, Plotly, Pygal, Seaborn; R: ggplot2; Julia: Makie.jl; JavaScript: Chart.js, D3.js, Apache ECharts, Highcharts, MUI X Charts. Compare all 15 side by side: Stacked Histogram in Python, R, Julia and JavaScript.

A histogram showing multiple groups stacked on top of each other within each bin. The total bar height represents the combined frequency, while colored segments show individual group contributions.

Stacked Histogram rendered with plotnine

Renders

Python source (plotnine)

""" anyplot.ai
histogram-stacked: Stacked Histogram
Library: plotnine 0.15.4 | Python 3.13.13
Quality: 88/100 | Updated: 2026-05-12
"""

import os
import pathlib

import numpy as np
import pandas as pd
from plotnine import (
    aes,
    element_line,
    element_rect,
    element_text,
    geom_histogram,
    ggplot,
    labs,
    scale_fill_manual,
    theme,
    theme_minimal,
)


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"

IMPRINT = ["#009E73", "#C475FD", "#4467A3"]

# Data - Plant growth measurements across different soil types
np.random.seed(42)

soil_a = np.random.normal(loc=25, scale=5, size=150)
soil_b = np.random.normal(loc=30, scale=6, size=120)
soil_c = np.random.normal(loc=22, scale=4, size=100)

df = pd.DataFrame(
    {
        "height": np.concatenate([soil_a, soil_b, soil_c]),
        "soil_type": (["Sandy Soil"] * 150 + ["Loamy Soil"] * 120 + ["Clay Soil"] * 100),
    }
)

# Plot
anyplot_theme = theme(
    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, size=0.4),
    axis_title=element_text(color=INK, size=20),
    axis_text=element_text(color=INK_SOFT, size=16),
    axis_line=element_line(color=INK_SOFT, size=0.5),
    plot_title=element_text(color=INK, size=24, weight="medium"),
    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),
    legend_text=element_text(color=INK_SOFT, size=16),
    legend_title=element_text(color=INK, size=18),
    legend_position="right",
    figure_size=(16, 9),
)

plot = (
    ggplot(df, aes(x="height", fill="soil_type"))
    + geom_histogram(bins=20, position="stack", alpha=0.85, color=PAGE_BG, size=0.4)
    + labs(x="Plant Height (cm)", y="Frequency", title="histogram-stacked · plotnine · anyplot.ai", fill="Soil Type")
    + scale_fill_manual(values=IMPRINT)
    + theme_minimal()
    + anyplot_theme
)

# Save
plot_dir = pathlib.Path(__file__).parent
plot_dir.mkdir(parents=True, exist_ok=True)
plot.save(str(plot_dir / f"plot-{THEME}.png"), dpi=300)

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/histogram-stacked/plotnine/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "histogram-stacked",
  "language": "python",
  "library": "plotnine",
  "page": "https://anyplot.ai/histogram-stacked/python/plotnine",
  "hub": "https://anyplot.ai/histogram-stacked",
  "code_json": "https://api.anyplot.ai/specs/histogram-stacked/plotnine/code",
  "spec_json": "https://api.anyplot.ai/specs/histogram-stacked",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/histogram-stacked/python/plotnine/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/histogram-stacked/python/plotnine/plot-dark.png",
  "quality_score": 88.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Stacked Histogram on anyplot.ai.

Other implementations