Skip to main content

prettyvoronoi

World explorer Open in molab

Stock maps & image themes Open in molab

Animated prettyvoronoi demo showing zooming, panning, hierarchy selection, and country flag backgrounds

Zoom, pan, select hierarchy levels, and move between overview and detail.

  • Interactive weighted Voronoi treemaps for Marimo and JupyterLab.
  • Turn dataframe rows into cells whose areas represent a numeric variable.
  • Use color to show a second measure without changing cell area.
  • Arrange cells into multilevel categorical groups.
  • Send country, group, and multilevel selections back to Python.
  • Turn those selections into reactive companion charts, tables, or slide-ready stories.
  • Zoom, pan, focus groups, select several regions, and enter full screen.
  • Drop your own images onto cells or use bundled SVG country flags.
  • Reuse your own SVGs or images as categorical themes without copying them into every row.
  • Switch to an all-rectangular stock-map view for portfolios and company data.
  • Automatically combine visually unresolvable cells into drillable Other regions without losing their underlying dataframe rows.

Install

pip install prettyvoronoi

That is the minimal installation: the widget accepts lists of records and column mappings without installing a dataframe library. Add only the dataframe integration you use:

pip install "prettyvoronoi[data]"    # pandas and as_frame=True dataset loaders
pip install "prettyvoronoi[polars]"  # Polars dataframe input

Marimo, JupyterLab, Altair, test tools, and both dataframe libraries are kept out of the default installation.

Run an example

Marimo explorer

uv sync --extra demo
uv run marimo edit examples/marimo_world_demo.py

The Marimo app exposes the full experience as live controls. Try these combinations:

  • Land area grouped by continent, with CO₂ emissions as a heat gradient.
  • Population grouped by continent and language family.
  • GDP grouped by currency, with country flags as cell backgrounds.
  • Full-screen mode with additive selection to build a filtered dataframe.
  • A selection-driven country ranking that updates below the Voronoi map.

The companion gallery demonstrates company and energy use cases:

uv run marimo edit examples/marimo_gallery.py

Choose the company view for a familiar rectangular stock map, or the energy view to reuse local SVG icons for fossil, solar-and-wind, renewable, and nuclear cells. Selecting a company group creates a market-value ranking; selecting an energy region creates a stacked generation-mix chart.

JupyterLab notebook

uv sync --extra jupyter
uv run jupyter lab examples/jupyterlab_world_demo.ipynb

The ready-to-run JupyterLab notebook uses the same widget and bundled data. Its USE_FLAGS switch demonstrates the two intended visual modes: a numeric gradient or crisp SVG flag backgrounds. Zoom, pan, drill-down, full screen, image dropping, and hierarchy selection work in both notebook environments.

The JupyterLab gallery contains the same company stock-map and category-image examples.

uv run jupyter lab examples/jupyterlab_gallery.ipynb

Both notebooks attach a live observer to selected_rows. The companion chart refreshes immediately after a widget selection, without rerunning a cell. Their shared presentation helpers live in examples/narrative_charts.py, so the ranking and stacked-chart patterns can be copied into another notebook or slide app.

Small countries that cannot be drawn meaningfully at the current chart size are combined into dashed Other (n) regions. Double-click Other to explore those countries in a dedicated view. Selecting the combined region returns all of its underlying dataframe rows.

Load the example data

The example world dataset is packaged with the library and has an sklearn-style loader:

from prettyvoronoi import load_companies, load_energy_mix, load_world

dataset = load_world(as_frame=True)
countries = dataset.data

dataset.feature_names
dataset.DESCR

companies = load_companies(as_frame=True).data
energy_by_source = load_energy_mix(as_frame=True).data

The dependency-free default returns a list of row dictionaries. Pass an explicit target when you want a feature/target split:

X, population = load_world(
    as_frame=True,
    target="Population",
    return_X_y=True,
)

Dataset credit and AI-data disclaimer

The underlying country indicators are credited to World Bank Open Data. The CSV bundled with this project is an AI-modified derivative, not an unchanged World Bank download: missing language and currency values were inferred with AI assistance, labels were standardized, and visualization-oriented grouping fields were added. These modifications have not been verified or endorsed by the World Bank. Treat the dataset as demonstration data rather than an authoritative source.

The bundled companies and energy datasets are AI-generated demonstration data and have not been independently verified. Do not use them as current or authoritative financial, investment, engineering, or policy data.

Create a chart

from prettyvoronoi import VoronoiTreemap, load_world

countries = load_world(as_frame=True).data

chart = VoronoiTreemap(
    countries,
    values="Population",
    groups=["Official language", "Currency-Code"],
    label="Country",
    id_column="Abbreviation",
    color_by="Birth Rate",
    color_range=["#edf8e9", "#15803d"],
    shape="circle",
    width=1000,
    aspect_ratio=16 / 9,
    fit_viewport=True,
    tiny_cells="auto",
    selection_mode="multiple",
    sync_selection_records=True,
    tooltip=["Country", "Population", "Birth Rate"],
)

chart

values determines cell area. groups can contain any number of categorical columns and defines the visible hierarchy. color_by adds an independent numeric gradient without changing area.

Circular weighted Voronoi treemap where land area controls cell size and birth rate controls a green gradient

Land area controls cell size while birth rate independently controls color intensity.

Supported Voronoi boundaries are circle, ellipse, rectangle, square, hexagon, diamond, and triangle. Use shape="stock-shape" when every group and leaf should be a rectangle:

from prettyvoronoi import VoronoiTreemap, load_companies

companies = load_companies(as_frame=True).data

stock_map = VoronoiTreemap(
    companies,
    values="Market Cap ($B)",
    groups=["Sector", "Industry"],
    label="Company",
    id_column="Ticker",
    color_by="YoY Revenue Growth (%)",
    shape="stock-shape",
    fit_viewport=True,
)

This view behaves like the familiar stock-market map: rectangle area represents the selected value, while nested rectangles preserve sector and industry. Selection, zoom, full screen, tooltips, and downstream dataframe filtering work exactly as in the Voronoi views. Because rectangular subdivision is exact, this is also the safest layout when sibling values differ by several orders of magnitude.

Build a downstream workflow from selections

Marimo

import marimo as mo

voronoi = mo.ui.anywidget(chart)
voronoi

Selections are reactive, so another Marimo cell can immediately use them:

selected_countries = countries.iloc[voronoi.selected_rows]
selected_countries

The examples go one step further and turn the selected dataframe into an Altair chart. This creates a useful presentation flow:

  1. Use the Voronoi or stock map as the visual overview.
  2. Select one leaf, several leaves, or a complete hierarchy group.
  3. Let a familiar bar or stacked chart explain the selected subset precisely.
  4. Reuse that same subset for a table, model, export, or following slide.

The widget exposes several useful views of the same selection:

voronoi.selected_node  # last group or leaf you interacted with
voronoi.selected_nodes  # all selected hierarchy nodes
voronoi.selected_rows  # deduplicated dataframe row positions
voronoi.selected_records  # row dictionaries when synchronization is enabled

Single-click replaces the current selection. Ctrl/Cmd/Shift-click toggles a node, while Select many makes additive selection comfortable without a keyboard. Countries and groups at every visible hierarchy level are selectable.

JupyterLab

The same synchronized traits are available on the widget instance. A normal trait observer can update another output immediately:

def update_story(change):
    selected = countries.iloc[change["new"]]
    # Render a chart, table, or narrative from selected.

chart.observe(update_story, names="selected_rows")

The bundled Jupyter notebooks include a complete live companion-chart output. Selecting an Other region returns all of its source rows; double-click it first when you want to select the tiny countries individually.

Explore the chart

  • Scroll or pinch to zoom around the pointer.
  • Drag anywhere to pan.
  • Double-click a cell or group to focus it.
  • Double-click at maximum zoom to return to the full view.
  • Use Show all, 0, or Escape to reset.
  • Use Full screen for a presentation-sized chart.

The outer shape, major groups, inner groups, and leaf cells use progressively lighter boundaries. Each group boundary combines light and dark contrast, so the hierarchy remains readable over both flags and gradients.

Set fit_viewport=True to use most of the notebook height even before entering full screen. zoom_sensitivity controls the wheel and trackpad speed, and max_zoom limits how far users can zoom.

Keep tiny cells honest

chart = VoronoiTreemap(
    countries,
    values="Land Area(Km2)",
    groups=["Official language"],
    label="Country",
    tiny_cells="auto",
)

Automatic mode only groups sibling leaves that are too small to represent well. It never changes the source dataframe or loses selection data. The default size threshold adapts to the chart; override it with tiny_cell_area=48 when you want a consistent visual target. max_other_share=0.15 limits how much of a parent can be folded into one Other region. Use tiny_cells="show" when seeing every cell in the overview is more important than resolution-aware grouping.

Weighted Voronoi layout is iterative. For highly skewed values, start with tiny_cells="auto"; if an area warning or layout failure remains, switch to shape="stock-shape" for exact rectangular areas. The chart reports an area warning if a visible polygon differs too much from the value it should represent, so extreme datasets do not fail silently.

Add color or images

Use a gradient to compare a second measure:

chart = VoronoiTreemap(
    countries,
    values="Land Area(Km2)",
    color_by="Birth Rate",
    color_range=["#fff7bc", "#d7301f"],
    show_color_legend=True,
)

Use a dataframe column containing image URLs or data URIs for cell backgrounds:

from prettyvoronoi import flag_theme

countries["Flag"] = countries["Abbreviation"].fillna("").map(flag_theme)

chart = VoronoiTreemap(
    countries,
    values="Population",
    label="Country",
    image="Flag",
    image_mode="cover",  # also "contain" or "stretch"
    allow_image_drop=True,
)

The world demo uses bundled SVG flags, which remain sharp while zooming. With image dropping enabled, drag an SVG, PNG, JPEG, WebP, or GIF directly onto a leaf to replace its background. Dropped images are returned through custom_images and can be removed from the widget header.

Color gradients and image backgrounds compete for attention, so choosing an image column pauses the numeric gradient by default and uses a neutral backing behind the images. Enable color_with_images=True only when combining both is intentional. A missing numeric color value also gets the neutral backing rather than an unrelated categorical color.

For repeated categories, map the category column directly to local files, URLs, or data URIs. Each image is embedded and sent to the browser once:

from pathlib import Path

from prettyvoronoi import VoronoiTreemap, load_energy_mix

energy = load_energy_mix(as_frame=True).data
icons = Path("my-energy-icons")

chart = VoronoiTreemap(
    energy,
    values="Generation (TWh)",
    groups=["Continent", "Country"],
    label="Energy Source",
    id_column="Cell ID",
    image="Energy Source",
    image_map={
        "Fossil fuels": icons / "fossil.svg",
        "Solar & wind": icons / "solar-wind.svg",
        "Hydro & other renewables": icons / "renewables.svg",
        "Nuclear": icons / "nuclear.svg",
    },
    image_mode="contain",
    image_padding=0.12,
)

SVG is ideal for deep zoom. Unmapped categories keep a neutral background, and a dropped image temporarily overrides the mapped theme for that individual cell. image_uri(...) is also available when you need to prepare a standalone local image for another dataframe workflow.

Project notes

The blueprint describes the product direction, and the implementation plan tracks completed and future work.

Non-rectangular layouts are powered by Franck Lebeau's permissively licensed d3-voronoi-treemap, d3-voronoi-map, and d3-weighted-voronoi, built on D3. Full bundled-component notices are in THIRD_PARTY_LICENSES.md.

Test the project

Install the development tools once and enable the Git hook:

uv sync --all-extras
uv run pre-commit install

Every commit now fixes and checks Python with Ruff, strips notebook outputs and execution counts, and catches malformed configuration, merge markers, private keys, and whitespace problems. Run the complete hook set manually with:

uv run pre-commit run --all-files

Run the project test suites separately:

npm --prefix frontend install
npm test
npm run build

uv run pytest
uv run marimo check examples/marimo_world_demo.py examples/marimo_gallery.py

# Starts a local JupyterLab and browser and tests the world notebook
npm run test:jupyterlab

The live JupyterLab smoke test expects google-chrome to be available on the development machine.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

prettyvoronoi-0.1.0.tar.gz (4.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

prettyvoronoi-0.1.0-py3-none-any.whl (1.6 MB view details)

Uploaded Python 3

File details

Details for the file prettyvoronoi-0.1.0.tar.gz.

File metadata

  • Download URL: prettyvoronoi-0.1.0.tar.gz
  • Upload date:
  • Size: 4.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.4

File hashes

Hashes for prettyvoronoi-0.1.0.tar.gz
Algorithm Hash digest
SHA256 32e4e9c4e755c0b54fc6ec6c0cbbb64f74611320c862c1e7f0b98100b5cb86e9
MD5 82a58798d3b7d00ba386ecc695cf7f89
BLAKE2b-256 584c3485bd54646a3f306ea244c2945b7625a285953a8e0b102129c0f838723e

See more details on using hashes here.

File details

Details for the file prettyvoronoi-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for prettyvoronoi-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c61ecea420671e03aef5ca7cf31aafac993185aff5fcd8841e4bc0d49bbd3df7
MD5 510a37adfc567b7a8f4010608d81d580
BLAKE2b-256 88454fe2fd88620627721e64334ca5a21ec985742fa86d5e40ed934f3b34d6d6

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 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