PlotNado is a lightweight Python package for building genome browser-style figures.
It supports two complementary workflows:
- A chainable Python API built around
GenomicFigure - A template-driven CLI for generating, validating, and rendering YAML specs
Use the Python API if you want plotting in notebooks or scripts. Use the CLI if you want an editable YAML template and a file-driven workflow.
uv is the recommended way to work with PlotNado.
For a project-local environment:
uv venv
source .venv/bin/activate
uv pip install plotnadoFor a global CLI install:
uv tool install plotnadoIf you prefer standard Python tooling, pip also works:
python -m venv .venv
source .venv/bin/activate
pip install plotnadoIf you use Conda:
conda create -n plotnado python=3.12
conda activate plotnado
pip install plotnadoChoose the workflow that matches how you want to work:
- Python API: build figures directly in code
- CLI + YAML template: infer a template from files, edit it, then render plots
from plotnado import GenomicFigure
import numpy as np
import pandas as pd
bins = np.arange(1_000_000, 1_100_000, 1_000)
signal = pd.DataFrame({
"chrom": "chr1",
"start": bins,
"end": bins + 1_000,
"value": 5 + 2 * np.sin(np.linspace(0, 6, len(bins))),
})
fig = GenomicFigure()
fig.scalebar()
fig.axis()
fig.genes("hg38")
fig.bigwig(signal, title="Synthetic signal", style="fill", color="#1f77b4")
fig.save("quickstart.png", "chr1:1,010,000-1,080,000")GenomicFigure() uses publication-style defaults automatically. Use GenomicFigure(theme=None) to opt out.
Generate a template from your files:
plotnado init sample1.bw sample2.bw peaks.narrowpeak --auto --output template.yamlValidate it:
plotnado validate template.yamlRender a region:
plotnado plot template.yaml --region chr1:1,000,000-1,100,000 --output browser_view.pngYou can also plot by gene name when the template defines genome:
plotnado plot template.yaml --region GNAQIf you want to return to Python after generating a template:
from plotnado import GenomicFigure
fig = GenomicFigure.from_template("template.yaml")
fig.save("browser_view.png", region="chr1:1,000,000-1,100,000")The generated YAML is meant to be human-editable:
genome: hg38
guides:
genes: true
tracks:
- path: sample1.bw
type: bigwig
title: sample1
- path: peaks.narrowpeak
type: narrowpeak
title: peaks- Chainable
GenomicFigureAPI for fast composition. - Template-driven CLI built around
init,validate, andplot. - Alias-based track creation (
fig.add_track("bigwig", ...)). - Built-in themes, autocolor, autoscale, and highlight overlays.
- Broad track support: BigWig, BED, narrowPeak, genes, axis, scalebar, links,
OverlayTrack, cooler-based matrix tracks.
- Docs home:
docs/index.md - Quick start:
docs/quickstart.md - CLI guide:
docs/cli.md - Track catalog + options:
docs/track_catalog.md - API and generated references:
docs/api_reference.md
- Start with
python examples/basic_figure.py - Then run
python examples/advanced_features.py - Full curated suite:
python examples/run_examples.py - Additional focused examples live in:
examples/quickstart/examples/tracks/examples/recipes/
All scripts write outputs to examples/output/.
from plotnado import GenomicFigure, BigWigTrack
GenomicFigure.available_track_aliases()
GenomicFigure.track_options("bigwig")
GenomicFigure.track_options_markdown("bigwig")
BigWigTrack.options_markdown()uv sync --extra docs
uv run mkdocs build --strict
uv run mkdocs serveFor development from source:
git clone https://github.com/alsmith151/plotnado
cd plotnado
uv venv
source .venv/bin/activate
uv sync --extra dev --extra docs
# required developer setup: run local quality hooks before every commit
uv run pre-commit installDeveloper workflow:
- Do not edit autogenerated typing surfaces directly:
plotnado/figure.pyi- auto-generated overload blocks in
plotnado/figure.py(# BEGIN/END AUTO-GENERATED OVERLOAD)
- Make API changes in the source Pydantic models first (track models/aesthetics/labels), then regenerate artifacts:
uv run python scripts/generate_figure_overloads.py
uv run python scripts/generate_figure_stub.py- Always run pre-commit before pushing:
uv run pre-commit run --all-files