Skip to main content

sankey_mpl

PyPI CI Python Ruff License: MIT

Sankey diagrams for matplotlib, as clean vector output.

86,400 Seconds in the Life of a Cat

from sankey_mpl import render_sankey, save

nodes = {
    "visits": {"label": "Visits (12,400)", "color": "#9AA5B1"},
    "signup": {"label": "Signed up (3,100)", "color": "#4C78A8"},
    "bounced": {"label": "Bounced (9,300)", "color": "#E45756"},
    "paid": {"label": "Subscribed (820)", "color": "#54A24B"},
    "lapsed": {"label": "Lapsed (2,280)", "color": "#F58518"},
}
links = [
    {"from": "visits", "to": "signup", "flow": 3100},
    {"from": "visits", "to": "bounced", "flow": 9300},
    {"from": "signup", "to": "paid", "flow": 820},
    {"from": "signup", "to": "lapsed", "flow": 2280},
]

result = render_sankey(nodes, links)
save(result, "funnel.svg")

Why this one

  • Genuinely vector. No rasterised artists anywhere, so SVG and PDF stay scalable and the text stays selectable. Most matplotlib sankey code fakes its gradients with an image, which silently embeds a bitmap per ribbon.
  • Reproducible. The same input produces byte-identical output, so a generated document's content hash is stable and golden-file tests are possible.
  • Exact pixel geometry. One data unit is one point is one pixel, so the diagram lands at the size you asked for and fontsize=12 means 12 pixels.
  • Everything is a config key. Node width, gap, curve shape, gradient resolution, label placement, export settings. No subclassing to tune a number.
  • It refuses bad input. Cycles, backward links, disconnected graphs and nodes too short for their own links raise instead of rendering something misleading.

Not a general graph-drawing library: it lays out acyclic left-to-right flows. Note that matplotlib ships an unrelated matplotlib.sankey for engineering flow diagrams; this is not that.

Gallery

These five are the test fixtures. They are deliberately silly, and they are deliberately different shapes. Each one exists because it exercises something the others cannot, so the gallery doubles as a map of what the layout does. Every image here is rendered by tools/render_previews.py from the same data the test suite runs against, and the SVG next to each PNG is the real vector output.

The Sourdough Dynasty: flows that split and rejoin. Both feeding regimes reach "an actual loaf"; three of four generations reach "hooch". The split-and-rejoin diamonds are what make link stacking order visible.

The Sourdough Dynasty

Break Room Forensics: six columns and dense sharing, the widest of the five. Every middle node is fed by four to six upstream nodes, so the per-column overlap sweep does real work in every column and the node gap carries over across five column boundaries.

Break Room Forensics

The Group Chat Decides Where to Get Brunch: a pure fan-out tree. No middle node is shared, so nothing ever overlaps and the bands run perfectly parallel; this is the control case, and you can see the absence in the picture. The one plan that actually happened carries four messages and is too thin to label, which is the joke and also the label drop rule working.

The Group Chat Decides Where to Get Brunch

It Was DNS: a convergent funnel spanning six orders of magnitude, and the real limit of a linear sankey. 2.4 million alerts against a single cosmic-ray alert means most of the diagram is one slab and nine of twenty-two labels fall below the drop threshold. That is the intended outcome rather than a bug: the library drops a label it cannot place legibly instead of stacking it on top of its neighbour.

It Was DNS

The cat-day diagram at the top of this page is the fifth. Its flows are seconds and they conserve to exactly 86,400, so its arithmetic can be checked by hand, which is why it is the one to reach for when debugging the height calculation.

Install

pip install sankey_mpl

Requires Python 3.10+, matplotlib and numpy.

Documentation

docs/usage.md is the full usage spec: the data model, every configuration key, the coordinate contract, label placement, export and determinism, and what each error means.

Provenance

The layout is a Python port of the algorithm in chartjs-chart-sankey 0.15.0 (MIT, © Jukka Kurkela), reimplemented from a written specification of its behaviour. Given the same input, this library reproduces that library's geometry, verified against golden data generated by running the original. A handful of deliberate differences are listed in docs/usage.md. See NOTICE for the upstream copyright.

UPSTREAM_VERSION records which upstream release the geometry tracks.

Development

pip install -e ".[dev]"
pytest
ruff check .

The golden data in tests/data is regenerated by tools/generate_golden.mjs, which runs the original JavaScript library over the five dataset specs in tools/datasets/. That needs Node, and only if you are changing the datasets; CI does not run it.

cd tools && npm install && node generate_golden.mjs   # goldens (needs Node)
python tools/render_previews.py                       # gallery images (no Node)

The test suite is parametrised over all five datasets, so a change that only breaks one shape still fails. tests/data/datasets.json records what each is for.

License

MIT.

Attribution

Built with Claude Code.

Release files for sankey-mpl 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sankey-mpl 0.1.1
File Size Uploaded
sankey_mpl-0.1.1.tar.gz 79.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sankey-mpl 0.1.1
File Interpreter ABI Platform
sankey_mpl-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 109.4 kB

Release files / sankey_mpl-0.1.1.tar.gz

Download URL sankey_mpl-0.1.1.tar.gz
Size 79.8 kB
Tags Source
SHA-256 checksum
How to use checksums
9a6178c99d9705e8c2a82b3065a0d7126877073463df604207294cfbd64250cf
BLAKE2b-256 checksum
How to use checksums
43ff795052269f68d56ca13177efaab001a2c17bf28628d1b936a3038b9ca8f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / sankey_mpl-0.1.1-py3-none-any.whl

Download URL sankey_mpl-0.1.1-py3-none-any.whl
Size 29.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
157b90f16685005b55d6587a021963456abfed563403d3a7561f8904cc184584
BLAKE2b-256 checksum
How to use checksums
dbc6c0b40bfb87304f397a45fc14ca6780153f01019da5a0b92bd99331e28164
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page