Skip to main content

polars2svg

Polars-native DataFrame → SVG visualizations for Jupyter. Pass a Polars DataFrame straight to a component and get a crisp, self-contained SVG back — scatter plots, histograms, temporal bars, network and chord diagrams, pie charts, and small multiples. Every component also has a linked, interactive variant (built on Panel) for brushing and cross-filtering in a notebook, plus optional WebGPU rendering for large frames.

Scatter plot Network graph Chord diagram

Install

The base install is deliberately slim — just Polars + NumPy (+ PyArrow/Pillow for I/O) — so a "give me SVG from a DataFrame" install stays light:

pip install polars2svg

That covers xyp, histop, timep, piep, smallp, and spreadlinesp. Everything else lives behind extras:

pip install polars2svg[layouts]     # linkp, chordp, and shapely-typed background= shapes
pip install polars2svg[interactive] # panelize / xypi / histopi / ... (includes layouts)
pip install polars2svg[export]      # component.save('chart.png')
pip install polars2svg[mlx]         # MLX-accelerated t-FDP layout (Apple silicon / Metal)
pip install polars2svg[mlx-cpu]     # ... the same layout on Linux, CPU backend
pip install polars2svg[mlx-cuda]    # ... the same layout on Linux + NVIDIA (CUDA 12)
pip install polars2svg[mlx-cuda13]  # ... the same layout on Linux + NVIDIA (CUDA 13)
pip install polars2svg[all]         # everything above (plain [mlx]; see the Linux note below)

Calling a component that needs an extra you haven't installed (e.g. chordp() or panelize()) raises a clear ImportError naming the extra to install.

On Linux, [mlx] and [all] are not enough. PyPI's mlx is a front-end that ships no backend library; on macOS it depends on the Metal backend unconditionally, on Linux it pulls nothing. The install succeeds and import mlx.core then fails with ImportError: libmlx.so: cannot open shared object file. Choose the backend explicitly:

pip install polars2svg[mlx-cpu]     # Linux, no NVIDIA GPU
pip install polars2svg[mlx-cuda13]  # Linux + NVIDIA, CUDA 13 toolkit
pip install polars2svg[mlx-cuda]    # Linux + NVIDIA, CUDA 12 toolkit

Combine one of these with [all] if you want everything — polars2svg[all,mlx-cuda13]. Install exactly one backend. They all ship the same libmlx.so, so asking for two leaves you with whichever was installed last, and no warning that it happened.

On Windows there is no MLX backend at all, so TFDPLayout cannot run there. mlx publishes Windows front-end wheels, which is why [mlx] and [all] still install; every backend distribution is Linux- or macOS-only. Everything else in polars2svg works normally.

Only TFDPLayout is affected by any of this: ODFlowLayout runs on NumPy and reaches for MLX only when MLX works.

TFDPLayout runs the same MLX code on either GPU backend — Metal on Apple silicon, CUDA on NVIDIA. Check which one you got with polars2svg.gpu_backend() ('metal', 'cuda', or 'cpu'). That name is exported only when MLX imports successfully, so on the broken-Linux install above it is absent rather than reporting 'cpu' — hasattr(polars2svg, 'gpu_backend') is itself the "did MLX load?" check, and import polars2svg keeps working either way. The CUDA wheels are Linux-only and need NVIDIA SM ≥ 7.5 (Turing or newer), driver ≥ 550.54.14, and glibc ≥ 2.35. Outside that envelope MLX falls back to the CPU device — TFDPLayout still works, just slower.

Match the extra to your CUDA toolkit, not just your driver. MLX JIT-compiles its CUDA kernels against the system CUDA headers (CUDA_HOME / CUDA_PATH, else /usr/local/cuda), so the wheel's NVRTC and those headers have to agree:

System CUDA toolkit Extra Also needs
12.x polars2svg[mlx-cuda] driver ≥ 550.54.14
13.x polars2svg[mlx-cuda13] driver ≥ 580

Mismatch it and the first GPU kernel dies in a wall of nvcc syntax errors from inside the CUDA headers (e.g. NVRTC 12.9 cannot parse CUDA 13's cuda_fp4.hpp). polars2svg detects this at import, logs a warning naming the cause, and falls back to CPU rather than failing mid-layout — so a silent slowdown here means it's worth checking gpu_backend().

Requires Python ≥ 3.12.

Quickstart

import polars as pl
from polars2svg import Polars2SVG

p2s = Polars2SVG()

df = pl.DataFrame({
    "x":     [1, 2, 3, 4, 5, 6],
    "y":     [3, 1, 4, 1, 5, 9],
    "group": ["a", "b", "a", "b", "a", "b"],
})

# In a Jupyter notebook, the returned object renders itself as SVG.
p2s.xyp(df, "x", "y", color="group", dot_size=6, wxh=(400, 300))

Every component returns an object with a .svg attribute (the raw SVG string) and a Jupyter _repr_svg_, so the last expression in a cell displays the chart inline. To save it:

chart = p2s.xyp(df, "x", "y", color="group", dot_size=6, wxh=(400, 300))
open("scatter.svg", "w").write(chart.svg)

If you arrived here from a snippet that doesn't work

A code sample along these lines circulates in search results and AI-generated answers. It is not polars2svg API and never has been, in any released version:

# ✗ Does not work — there is no `display_svg` in polars2svg.
from polars2svg import display_svg
display_svg(df)

It is wrong twice over. The name appears to be borrowed from IPython's IPython.display.display_svg — a different function in a different package, which takes SVG data rather than a DataFrame. And there is no "render a DataFrame as a table" entry point to rename it to: every component encodes named fields into a chart, so no single-argument function could know what you wanted drawn.

Reach for a component instead — Polars2SVG() plus one of the calls under Components:

import polars as pl
from polars2svg import Polars2SVG

p2s = Polars2SVG()

df = pl.DataFrame({
    "Engine": ["Polars", "Pandas", "Spark"],
    "Speed":  [30.5, 1.0, 12.3],
})

p2s.histop(df, "Engine", count="Speed", wxh=(400, 200))

Components

Each is a method on a Polars2SVG instance and takes a pl.DataFrame plus the fields to encode.

Component Call What it draws
xyp p2s.xyp(df, x, y, ...) Scatter / distribution plot; x and y can be numeric or categorical.
histop p2s.histop(df, field, ...) Horizontal histogram bars, one per category/bin.
timep p2s.timep(df, ts_field, ...) Temporal bar chart with linear or periodic (day-of-week, month, …) time modes.
linkp p2s.linkp(df, relationships, ...) Node-link graph / network with pluggable layouts.
chordp p2s.chordp(df, relationships, ...) Chord diagram of weighted flows around a circle.
piep p2s.piep(df, field, ...) Pie chart.
spreadlinesp p2s.spreadlinesp(df, ...) Egocentric radial "spread" rings for influence/propagation.
smallp p2s.smallp(df, ...) Small multiples — a grid of one template component faceted by a field.

Seven of the eight have an interactive, cross-linked variant — xypi, histopi, timepi, linkpi, chordpi, piepi and smallpi (there is no spreadlinespi). Each one wraps a rendering rather than a DataFrame, and p2s.panelize() composes them into a dashboard as a list of rows, sharing one selection across every view in it:

df = pl.DataFrame({
    "x":     [1, 2, 3, 4, 5, 6],
    "y":     [3, 1, 4, 1, 5, 9],
    "group": ["a", "b", "a", "b", "a", "b"],
})

xi = p2s.xypi(p2s.xyp(df, "x", "y", color="group", wxh=(400, 300)))
hi = p2s.histopi(p2s.histop(df, "group", wxh=(400, 300)))

p2s.panelize([[xi, hi]])      # one row — brushing either view filters both
p2s.panelize([[xi], [hi]])    # two rows, stacked

panelize() also accepts bare static components and wraps them for you. Pass use_webgpu=True to an interactive variant to render it through WebGPU.

Finished renderings compose into a single static SVG with p2s.tile(...) — the one method that takes renderings rather than a DataFrame:

p2s.tile([chart_a, chart_b])                    # side by side
p2s.tile([chart_a, chart_b], per_row=1)         # stacked
p2s.tile(charts, per_row=4, spacer=(8, 24))     # a 4-across grid
p2s.tile(charts, per_row=4, wxh=(640, 480))     # ... scaled to fit 640x480

Elements may be components, SVG strings, or other tiles. per_row= is the whole layout model: None (the default) is a single row, <n> fills a grid row by row, and 1 is a single column. spacer= is one gap in both directions or a (horizontal, vertical) pair, and wxh= scales the whole tiling — uniformly and centered — into a canvas of that size.

Encoding data: count= and color=

Two parameters recur across the counting/aggregating components, and they are orthogonal — one controls size/magnitude, the other controls color.

count= — "how big / how much is this element?"

The aggregation rule is shared by every component that takes count=:

count= spec Aggregation
p2s.ROW_COUNTp (default) pl.len() — number of rows
a numeric field sum of that field
a non-numeric field, or ('field', p2s.SETp) n_unique (distinct count)
a multi-field tuple struct the fields, then n_unique

What count= visibly does depends on the component. At default settings it is the primary size knob for histop (bar length) and timep (bar height); it nudges the derived node order in chordp; and for linkp/chordp it only drives geometry once you opt into node_size='vary' / link_size='vary'.

color= — "what color is this element?"

Independent of count=. A bare field infers its meaning from the column dtype (numeric → magnitude spectrum, otherwise → categorical), and enums let you pin intent explicitly — e.g. ('field', p2s.CSETp) forces categorical, ('field', p2s.CMAGNITUDE_SUMp) forces a numeric spectrum.

The CROW_MAGNITUDEp / CROW_STRETCHEDp color modes always color by raw row count (pl.len()), regardless of what count= is set to — so a bar sized by count='bytes' can still be colored by how many rows landed in it. Because the two encodings are orthogonal, a tall bar can be cold-colored (many bytes, few rows) or vice-versa — that is by design.

Security & deployment

polars2svg supports three deployment profiles, described in full in SECURITY.md:

Profile Shape Status
Notebook One trusted person, one process Fully supported
Appliance (A) A server renders from untrusted data; output embedded in a page others view Supported for the render path
Interactive, multi-user (B) One process serves the live widgets to mutually untrusting people Out of scope

If you render from data you do not control, gate what you serve on the output contract. It is an allow-list over the rendered document — only the elements and attributes the components actually emit, every reference resolving inside the same document, no event handlers, no scripts:

import polars as pl
from polars2svg import Polars2SVG, assertOutputContract

p2s   = Polars2SVG()
chart = p2s.histop(pl.DataFrame({'cat': ['a', 'b', 'a']}), 'cat')

# Raises OutputContractError if the render is not fit to serve.
assertOutputContract(chart.svg)

It reports; it never rewrites. tile() is outside the profile by construction — it embeds foreign SVG verbatim, which is the component, not a defect. The interactive components (panelize(), linkpi(), …) are not hardened for multiple untrusting users; run one process per user behind your own authentication.

Development

uv venv --python 3.13 && uv pip install -e .        # runtime deps
uv pip install -e . --group dev                     # + test/dev tooling
.venv/bin/python -m pytest tests/                   # run the suite

After changing framework files, re-run uv pip install -e . before testing.

tests/test_readme.py executes every Python block on this page and resolves every API name and asset path it mentions, so a snippet here cannot drift from the code.

References

polars2svg implements algorithms from the following published work. Each implementation file carries the full citation in its module header.

  • Circle packing (chordp/spreadlinesp packing, packCircles() — circle_packer.py): W. Wang, H. Wang, G. Dai, and H. Wang, "Visualization of large hierarchical data by circle packing," Proc. SIGCHI Conference on Human Factors in Computing Systems (CHI '06), 2006, pp. 517–520. doi:10.1145/1124772.1124851
  • Neighborhood-preserving non-uniform circle packing (linkp ncp pack layout op, NCPLayout — ncp_layout.py): D. Li, J. Yuan, X. Guo, X. Wang, Y. Liu, W. Yang, and S. Liu, "NCP: Neighborhood-Preserving Non-Uniform Circle Packing for Visualization," Computational Visual Media, 2026. arXiv:2602.00668
  • Hierarchical edge bundling (chordp bundled link shapes — chordp.py): D. Holten, "Hierarchical Edge Bundles: Visualization of Adjacency Relations in Hierarchical Data," IEEE Transactions on Visualization and Computer Graphics, vol. 12, no. 5, pp. 741–748, 2006. doi:10.1109/TVCG.2006.147
  • Force-directed origin-destination flow maps (linkp flowmap link shape, ODFlowLayout — od_flow_layout.py): B. Jenny, D. M. Stephen, I. Muehlenhaus, B. E. Marston, R. Sharma, E. Zhang, and H. Jenny, "Force-directed layout of origin-destination flow maps," International Journal of Geographical Information Science, 2017. doi:10.1080/13658816.2017.1307378
  • ColorBrewer "Spectral" scheme (the framework-wide color spectrum — p2s_colors_mixin.py): M. Harrower and C. A. Brewer, "ColorBrewer.org: An Online Tool for Selecting Colour Schemes for Maps," The Cartographic Journal, vol. 40, no. 1, pp. 27–37, 2003. doi:10.1179/000870403235002042
  • t-FDP force-directed layout (TFDPLayout — tfdp_layout.py): F. Zhong, M. Xue, J. Zhang, F. Zhang, R. Ban, O. Deussen, and Y. Wang, "Force-Directed Graph Layouts Revisited: A New Force Based on the t-Distribution," IEEE Transactions on Visualization and Computer Graphics, 2023. arXiv:2303.03964
  • Landmark MDS (LandmarkMDSLayout — mds_at_scale.py): V. de Silva and J. B. Tenenbaum, "Global versus local methods in nonlinear dimensionality reduction," Proc. NIPS, 2003, pp. 721–728.
  • Pivot MDS (PivotMDSLayout — mds_at_scale.py): U. Brandes and C. Pich, "Eigensolver Methods for Progressive Multidimensional Scaling of Large Data," Proc. 14th Symposium on Graph Drawing (GD), 2006, pp. 42–53.
  • Radial ("hypertree") tree drawing (hyperTreeLayout() — p2s_graph_mixin.py): P. Eades, "Drawing free trees," Bulletin of the Institute for Combinatorics and its Applications, vol. 5, pp. 10–36, 1992.
  • Laguerre-Voronoi (power) diagrams (laguerre_voronoi() — laguerre_voronoi.py): H. Imai, M. Iri, and K. Murota, "Voronoi Diagram in the Laguerre Geometry and its Applications," SIAM Journal on Computing, vol. 14, no. 1, pp. 93–105, 1985.
  • Scatterplot de-cluttering (uniformSampleDistributionInScatterplotsViaSectorBasedTransformation() — udist_scatterplots_via_sectors_tile_opt.py): H. Rave, V. Molchanov, and L. Linsen, "Uniform Sample Distribution in Scatterplots via Sector-based Transformation," 2024 IEEE Visualization and Visual Analytics (VIS), 2024, pp. 156–160. doi:10.1109/VIS55277.2024.00039
  • SpreadLine layout (spreadlinesp — spreadlinesp.py): Y.-H. Kuo, D. Liu, and K.-L. Ma, "SpreadLine: Visualizing Egocentric Dynamic Influence," IEEE Transactions on Visualization and Computer Graphics (Proc. IEEE VIS 2024). arXiv:2408.08992

Several modules are vendored from the author's racetrack_svg_framework (Apache-2.0); each carries a provenance header noting its origin.

To cite polars2svg itself, see CITATION.cff.

License / Notices

polars2svg is licensed under Apache-2.0 (see LICENSE).

The wheel bundles a subset of the Noto Sans font (polars2svg/fonts/NotoSans-Regular-subset.ttf), © The Noto Project Authors, distributed under the SIL Open Font License 1.1 (see polars2svg/fonts/OFL.txt).

This product includes color specifications and designs developed by Cynthia Brewer (http://colorbrewer.org/) — © 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State University, used under the Apache-style ColorBrewer license (see NOTICE).

Metadata

Release files for polars2svg 0.3.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 polars2svg 0.3.1
File Size Uploaded
polars2svg-0.3.1.tar.gz 2.4 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for polars2svg 0.3.1
File Interpreter ABI Platform
polars2svg-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 3.7 MB

Release files / polars2svg-0.3.1.tar.gz

Download URL polars2svg-0.3.1.tar.gz
Size 2.4 MB
Tags Source
SHA-256 checksum
How to use checksums
4abfdfed0b593b9302434c567a073f5b1962ecb6688a5d4eaca93ecb495df37b
BLAKE2b-256 checksum
How to use checksums
846318655787ba2484d1f2425c26d8aa10d62908db82fe43fae934d4a4b20ef8
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 Sep 18, 2026.

Transparency log

Release files / polars2svg-0.3.1-py3-none-any.whl

Download URL polars2svg-0.3.1-py3-none-any.whl
Size 1.3 MB
Tags Python 3
SHA-256 checksum
How to use checksums
f702f3922041ac5b7e1e514db51d42c85af58ff129bcf2982e91609f414a35c5
BLAKE2b-256 checksum
How to use checksums
1c193ce005c65a1141f87894cd39ed0040f1d84244f314a15fe779ee98625142
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 Sep 18, 2026.

Transparency log

Release history Release notifications | RSS feed

0.5.1

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

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