prettyvoronoi
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.
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:
- Use the Voronoi or stock map as the visual overview.
- Select one leaf, several leaves, or a complete hierarchy group.
- Let a familiar bar or stacked chart explain the selected subset precisely.
- 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, orEscapeto 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32e4e9c4e755c0b54fc6ec6c0cbbb64f74611320c862c1e7f0b98100b5cb86e9
|
|
| MD5 |
82a58798d3b7d00ba386ecc695cf7f89
|
|
| BLAKE2b-256 |
584c3485bd54646a3f306ea244c2945b7625a285953a8e0b102129c0f838723e
|
File details
Details for the file prettyvoronoi-0.1.0-py3-none-any.whl.
File metadata
- Download URL: prettyvoronoi-0.1.0-py3-none-any.whl
- Upload date:
- Size: 1.6 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.8.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c61ecea420671e03aef5ca7cf31aafac993185aff5fcd8841e4bc0d49bbd3df7
|
|
| MD5 |
510a37adfc567b7a8f4010608d81d580
|
|
| BLAKE2b-256 |
88454fe2fd88620627721e64334ca5a21ec985742fa86d5e40ed934f3b34d6d6
|