Skip to main content

Python library for SVG plots, with multi-panel composition and an extension API for custom plot types.

Project description

plotlet

A Python library for SVG plots — with multi-panel composition, shared-axis layouts, and an extension API for custom plot types.

What it's for

plotlet is built for multi-panel scientific figures with custom plot types — genome tracks, spike rasters, climate stacks, Manhattan plots, phylogenetic trees. The core ships ~5 standard plots plus multi-panel composition (|, /, share_x()).

Custom plot types are a 3-step recipe (record, xdomain/ydomain, draw) and live in your own project (or cookbook/) rather than upstream. See docs/PHILOSOPHY.md for the full framing.

import plotlet as pt

data = {
    "x":      [1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
    "y":      [1, 4, 9, 16, 25, 1, 8, 27, 64, 125],
    "series": ["squares"] * 5 + ["cubes"] * 5,
}

c = pt.chart(data, title="Hello", xlabel="x", ylabel="y", legend=True, grid=True)
c.line(x="x", y="y", hue="series")
c                                        # auto-renders in Jupyter

Install

pip install plotlet

Properties

  • Minimal dependencies. fonttools for font handling. numpy / pandas / polars inputs work transparently if you have them.
  • Static SVG output. No interactivity, no animation. Same script → byte-identical SVG.
  • Cross-machine reproducible. Bundled DejaVu Sans + text-as-paths means rendering is identical on Linux, macOS, Windows, headless CI.
  • Jupyter-native. Chart._repr_html_ auto-renders the last expression in a cell.
  • Compact output. Each plot is ~50 KB SVG, self-contained.
  • Compositional. Multi-panel layouts via |, /, pt.grid; share scales with (a | b).share_x() or pt.grid(..., share_x="col"); layout-level legend with pt.legend() covering both discrete swatches and continuous gradients (the colorbar).
  • AI-readable. Every figure ships data-plotlet-* attributes describing plot type, axes, scales, ranges, and series labels — readable in one XML parse, no glyph-path OCR. Schema: docs/AI_ATTRS.md.

API

pt.chart(data, **opts) returns a Chart bound to a table — any object that supports data[col_name] returning an iterable (pandas / polars DataFrames, dict-of-lists, dict-of-arrays). All methods return self; _repr_html_ makes the chart auto-render as the last expression in a Jupyter cell.

Frame options

Pass at construction (pt.chart(data, title=..., grid=True, ...)) or as chained setters (c.title(...), etc.):

title, xlabel, ylabel, xlim=(a, b), ylim=(a, b), xscale="linear"|"log"|"category" (chained: c.xscale("category", order=[...], padding=0)), yscale=..., grid=True/False, legend=True/False, data_width, data_height (the data region — the figure canvas grows to fit titles, tick labels, and axis labels). Sizes accept bare pixels (400) or unit-suffixed strings ("4in", "10cm", "100mm", "72pt"). To fit a composition into a target SVG canvas, chain .fit(canvas_width=…, canvas_height=…) after composing — it rescales data regions while keeping fonts, spines, and margins at their absolute pixel sizes.

String-valued data on either axis (scatter(["a","b","c"], ...), bar, …) auto-switches to a categorical scale, alphabetical by default. padding=0 makes category bands contiguous (heatmap-track look).

Tick customization: c.xticks([0, 5, 10], ["A","B","C"], rotation=45, fontsize=12, direction="out", marks=False). Pass [] to hide. yticks(...) works the same way.

Mark methods

call options
.line(x=, y=, hue=, **opts) color, label, linewidth, linestyle ("-", "--", ":", "-."), marker ("o", "s", "^", "v", "x", "+"), markersize
.scatter(x=, y=, hue=, **opts) color, label, s (size), alpha, marker
.bar(x=, y=, **opts) color, label, alpha
.hist(x=, **opts) bins, color, alpha, label
.fill_between(x=, y1=, y2=, **opts) color, alpha, label
.axhline(y, **opts) / .axvline(x, **opts) color, linewidth, linestyle, alpha, label, axes-fraction xmin/xmax (or ymin/ymax)
.axhspan(ymin, ymax, **opts) / .axvspan(xmin, xmax, **opts) color, alpha, label, axes-fraction xmin/xmax (or ymin/ymax)
.imshow(data, **opts) cmap (any matplotlib name, default "viridis"), vmin, vmax, extent=(left, right, bottom, top)
.heatmap(df, **opts) cmap, vmin, vmax, norm, center, xticklabels, yticklabels, legend

hue=<col> (on .line / .scatter) splits into one call per unique value with auto-labels and tab10 colors. Reference lines and spans default to black; spans use alpha=0.2. They're drawn outside the data color cycle and don't participate in autoscaling — they're decorations on the frame, not data.

.imshow(data) renders a 2-D array as a colored grid. Small grids (nrows × ncols ≤ 10000) emit one <rect> per cell and stay vector-clean at any zoom; larger grids encode as a single base64 PNG and quantize to 256 levels. Image row 0 is rendered at the top of its rectangle; the y axis stays Cartesian (small at bottom). All ~180 matplotlib colormaps are vendored — see pt.list_colormaps().

.heatmap(df) is the DataFrame-aware companion to .imshow. A pandas DataFrame's index becomes the row tick labels and columns becomes the column tick labels; row 0 sits at the top. For a plain 2-D array, default labels are integer indices — pass xticklabels= / yticklabels= to override. Cells render at integer + 0.5 centers on a linear axis, which lines up with scipy's dendrogram leaf positions so a top/left dendrogram pairs cleanly via share_x / share_y.

Subplots

Compose multi-panel layouts with operators on Chart:

a | b                  # side-by-side
a / b                  # stacked
a | b | c              # left-fold flatten — one row of three, not nested

pt.grid([[a, b],       # 2-D grid; cells may be `None`
         [c, d]])

# Share x or y across panels — collapses the gap between them
# and unions data ranges; the first leaf in reading order anchors
# the scale, others are aspect-scaled to match.
top  = pt.chart()
main = pt.chart()
(top / main).share_x()         # vertically stacked, x-axis joined

# Layout-level legend (covers colorbar and discrete swatches in
# one constructor — geometry follows from the source's color mapping).
hm = pt.chart(); hm.imshow(matrix, cmap="viridis")
hm | pt.legend(hm)             # heatmap + colorbar (gradient strip)

# Multi-source: groups by chart, using each chart's title as
# section header. `names={chart: "Override"}` renames a header,
# `names={chart: None}` hides it, `group_by_chart=False` flattens.
(hm | top) | pt.legend()       # auto-collects from siblings
parent = a | b; parent.legend()  # sugar for parent | pt.legend()

A composed chart owns its children; render the parent ((a | b).show() or .to_svg() / .save_svg(...)). Calling .show() on a child raises. See docs/SUBPLOTS.md for the design rationale.

Render / save

c.show()                     # explicit display() inside a cell
c.to_svg()                   # raw SVG string
c.save_svg("plot.svg")       # SVG file
c.write_html("plot.html")    # standalone HTML

Color shortcuts

  • "C0""C9" → tab10 (matches matplotlib)
  • Named: "blue", "orange", "green", "red", "purple", "brown", "pink", "gray", "olive", "cyan"
  • Single-letter: "k", "w", "b", "g", "r"
  • Any hex / CSS color string passes through

Adding a new plot type

plotlet is designed so that adding a new plot type is a 3-step recipe (~50–100 lines) that gets axes, scales, legend, grid, and composability for free. The recommended home is your own project, or cookbook/ as reference. Full guide: docs/EXTENDING.md.

Testing

python tests/test_chart.py            # check vs. committed baselines
python tests/test_chart.py --update   # regenerate (review the diff!)
python tests/test_chart.py --gallery  # build tests/baseline_images/chart/index.html
python tests/test_subplots.py         # subplot baselines + composition invariants

Non-goals

  • No interactivity (hover, zoom, click). Static rendering is the point.
  • Not aiming for full coverage of standard statistical plots — those needs are well-served elsewhere.
  • Not a 3D plotter, not a dashboard tool.
  • Not a feature catalog — new plot types belong in user projects or cookbook/, not in the core.

License

MIT

Project details


Download files

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

Source Distribution

plotlet-0.4.0.tar.gz (573.8 kB view details)

Uploaded Source

Built Distribution

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

plotlet-0.4.0-py3-none-any.whl (559.5 kB view details)

Uploaded Python 3

File details

Details for the file plotlet-0.4.0.tar.gz.

File metadata

  • Download URL: plotlet-0.4.0.tar.gz
  • Upload date:
  • Size: 573.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for plotlet-0.4.0.tar.gz
Algorithm Hash digest
SHA256 e6ce0733f7c2d557fa9d802e05d6d77515cac45ec2374a96b89a5bc0613a66c2
MD5 b4c1ee527f6779f2ecd8825887b76be0
BLAKE2b-256 47f81c6b08832a2f4651ca7eac694cbf9c4a16aff36db6c51a88bc6d859638ae

See more details on using hashes here.

File details

Details for the file plotlet-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: plotlet-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 559.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for plotlet-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 45b8da9adf40a6034be7c654d76e443b6b381b2594fc5de534b76749aea38502
MD5 5b4b3f3fe8226f3f30a939c0b495a0f7
BLAKE2b-256 3333d0cb3bd07f6b1e287fb9f5eeb64c76408e72a656dae31592e7f905d89dc2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page