Skip to main content

weighted-voronoi

Voronoi treemaps from a spreadsheet, in Python and nothing else.

A treemap that tiles a square with cells whose areas are proportional to your weights, nested down a hierarchy — and where the cells are polygons rather than rectangles. Pure Python on top of numpy, scipy, pandas and matplotlib: no external tools to install, nothing to configure.

pip install weighted-voronoi

Excel input needs one extra (quote it — zsh treats the brackets as a glob):

pip install 'weighted-voronoi[excel]'

Quick start

from weighted_voronoi import treemap

fig = treemap("examples/ecoli_proteome_test.csv", label_level="Level2")
fig.savefig("treemap.png", dpi=200)

treemap() takes a path (CSV, TSV or Excel), a file object, CSV/TSV text, or a DataFrame, and returns a matplotlib Figure — so it saves to PNG/SVG/PDF, composes into a larger figure, and displays on its own in a notebook.

A worked example lives in examples/quickstart.ipynb, executed so its figures render without running anything. It covers label levels, styling, DataFrame input, reading the layout back as data, seeds and progress reporting.

Input format

One row per leaf, with the hierarchy spelled out in columns:

Column Meaning
Level1..LevelN the groups the leaf belongs to, outermost first (up to 10 levels)
Name the leaf's own name
Weight a positive number; cell areas follow it
Color optional — #1f77b4, rgb()/rgba(), hsl()/hsla(), or a CSS color name

Rows sharing a level path are grouped, and a group's weight is the sum of its children. Rows with a non-positive weight are dropped, and a blank level cell becomes a group named (none).

Colors are per row. Rows that leave the column blank get one derived from their position in the hierarchy — distinct hues per Level1 group, varied within it — and a group's own swatch is the weight-averaged color of the tiles beneath it.

Command line

weighted-voronoi examples/ecoli_proteome_test.csv -o map.png --level Level2 --label-color contrast
weighted-voronoi examples/microbe_test.xlsx       -o map.svg --borders none --font "Liberation Serif"
weighted-voronoi workbook.xlsx --sheet costs -o map.pdf --random-seed

weighted_voronoi and python -m weighted_voronoi do the same thing. Under uv, without installing anything:

uvx --from weighted-voronoi weighted-voronoi data.csv -o map.png

Output format follows the -o extension: .png, .svg, .pdf or .eps.

Flag Meaning
--level Level2 / --level Name / --level 2 which depth to label
--label-color white|black|contrast contrast picks per label, from the tile's luminance
--borders white|black|none none lets tiles touch
--font "DejaVu Serif" three sans and three serif families, all freely licensed
--seed N, --random-seed reshuffle the layout
--no-legend, --no-frame drop the legend column, drop the frame
--sheet which sheet of an Excel workbook
--size-inches, --dpi output size
--iterations, --convergence trade layout quality against time

--help lists them all.

A large file takes a few seconds to lay out, so there is a progress bar with an estimate of the time left. It appears only when stderr is a terminal, and --no-progress suppresses it. If a map is wanted sooner than exact, --iterations 20 (from the default 50) roughly halves the time, at the price of the worst-fitted tiles drifting from 0.05% to 0.3% of the map.

Rendering options

render(), and the corresponding treemap() keywords:

Argument Values
label_level None/"Name" for the leaves, a level column name, or a 1-based depth
label_color "white", "black", "contrast" (per label, from the tile's luminance)
borders "white", "black", None (tiles touch)
font a key of FONT_STACKS — three sans, three serif, all freely licensed
legend, map_inches, legend_inches, dpi, margin, frame figure geometry

Labels are sized to the room their own cell actually has at the anchor, and dropped when they would come out unreadably small. The default font is DejaVu Sans because matplotlib ships it: the figure looks the same on a machine with no fonts installed at all.

There is no interactivity — no hover tooltips, no rebuild button. Pass a different seed to reshuffle, and re-render to change options. Tile shares are available programmatically: every record from compute_layout() carries value and its path.

Using the layout on its own

weighted_voronoi.layout is useful without any of the spreadsheet machinery above. It needs only numpy and scipy — matplotlib is imported lazily, when something asks to draw.

import numpy as np
from weighted_voronoi.layout import power_diagram, voronoi_map, voronoi_treemap

square = [[0, 0], [0, 900], [900, 900], [900, 0]]

# A power (Laguerre) diagram: one cell per site, heavier sites take more room
# from their neighbours.
cells = power_diagram(np.array([[300., 450.], [600., 450.]]),
                      np.array([5000., 1000.]), square)

# Cells whose *areas* follow the weights, found by relaxation.
result = voronoi_map([1.0, 2.0, 4.0, 8.0], square, seed=0)
result["cells"], result["converged"], result["area_error_ratio"]

# The same, recursively, down a hierarchy of dicts.
records = voronoi_treemap({"name": "root", "children": [...]}, square, seed=0)

# Anything slow enough to be worth watching takes a progress callback.
records = voronoi_treemap(tree, square, seed=0,
                          progress=lambda done, total: print(done, "of", total))

Nothing in there knows about spreadsheets, colors or pandas. Cell areas land within 0.2% of the weights they were asked for, and every loop is bounded — no input hangs the layout, and no seed needs retrying.

Speed is set by the number of nodes: 14 nodes lay out in 0.07 s, 1080 in about 5 s.

In the browser

milo-lab-public.gitlab.io/weighted-voronoi is this package running under Pyodide: pick a spreadsheet and the layout is computed by weighted_voronoi.layout in WebAssembly. The file never leaves your machine — it is read in the browser. Exports are SVG, PNG and PDF.

It is a demonstration, not a replacement: the page downloads ~86 MB of wheels, and WebAssembly runs the layout about 2.6× slower again — a 1000-leaf file takes roughly 12 s. Small maps are comfortable.

The page is index.html, which Pages publishes from the default branch. It is generated from the package, not written by hand:

python tools/build_web.py

The markup lives beside the builder in tools/template.html. The builder pastes the package's own modules in verbatim, and tests/test_web_page.py regenerates and compares, so the committed page cannot fall behind the source.

Tests

pip install 'weighted-voronoi[test]'
python -m pytest tests -q

Without that extra the suite still passes; only the Excel tests skip.

tests/golden/reference.json holds expected layout outputs, captured once and checked in; test_reference_parity.py checks against them. tests/golden/regenerate.py says what regenerating it takes, and refuses to run rather than guessing. Never edit it by hand.

Releasing

Tagging is the whole procedure. CI runs the suite on every push, and on a v* tag it builds a wheel and an sdist, checks them, and uploads to PyPI via Trusted Publishing — no token stored here.

# bump `version` in pyproject.toml first -- CI refuses a tag that disagrees
git tag v0.1.1 && git push origin v0.1.1

Licensing

This package's own code is MIT, in LICENSE. layout/ is a derived work: power.py and clip.py port d3-weighted-voronoi, relax.py ports d3-voronoi-map, treemap.py ports d3-voronoi-treemap — all BSD 3-Clause, Copyright (c) 2018, LEBEAU Franck — and clip.py is additionally derived from D3 (BSD 3-Clause, Copyright 2010-2017 Mike Bostock).

No JavaScript is redistributed here, but a port is a derived work, so the notices travel with it: see weighted_voronoi/THIRD-PARTY-LICENSES.md, which ships inside the installed package and in the wheel.

parsing.py and drawing.py are copies of files in the origin project's python/proteomap/. Nothing enforces that they stay copies — if you change the parsing rules in one place, change them in all three.

Metadata

Release files for weighted-voronoi 0.1.0

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

Source distribution (sdist)

Source distribution for weighted-voronoi 0.1.0
File Size Uploaded
weighted_voronoi-0.1.0.tar.gz 65.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for weighted-voronoi 0.1.0
File Interpreter ABI Platform
weighted_voronoi-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 114.6 kB

Release files / weighted_voronoi-0.1.0.tar.gz

Download URL weighted_voronoi-0.1.0.tar.gz
Size 65.0 kB
Tags Source
SHA-256 checksum
How to use checksums
db64dbb0eb3048153ac417d91dc5ae7ee9328bc2e1463d58865f634befa3ceea
BLAKE2b-256 checksum
How to use checksums
c0b458bc3e42f73f66dbcfebc6b257a7c807a420bd2c5ced40dd8cca435ff4ab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / weighted_voronoi-0.1.0-py3-none-any.whl

Download URL weighted_voronoi-0.1.0-py3-none-any.whl
Size 49.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a2189321fadaad2d16cd03cefb5406561cddad2b2a017e2150e9d2f331000f19
BLAKE2b-256 checksum
How to use checksums
f24a54bd519f805d60129c80d98dff21f9e79c8dfdc42534927946110c4cd3fb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.0 This release

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