Skip to main content

rootfig

Publication-quality figures straight from ROOT trees, without ROOT.

rootfig is the TTree::Draw workflow for the Scientific Python HEP stack: give it ROOT files, a tree, an expression, a selection and a weight, and get a styled matplotlib figure back in one call. It reads with uproot, computes with Awkward Array, fills hist histograms and draws with mplhep. It adds the missing glue: predictable per-event/per-object selection semantics, weights, shared binning across samples, normalisation, ratio panels and good defaults.

CI codecov PyPI Python License

import rootfig as rf

rf.plot("events.root", "Muon_pt", tree="events", selection="Muon_pt > 20", bins=50)

Stacked simulation with data and a ratio panel Broken x axis with a ratio panel

Per-object versus per-event selections Two-dimensional histogram

These and a dozen more figures, each next to the code that made it, are in the gallery. All of them come from examples/gallery, which writes toy ROOT files and draws every example in a few seconds; the same figures are pixel-compared in CI.

Installation

pip install rootfig
# or
uv add rootfig

Python 3.12 or newer. No ROOT installation is needed; TTree and RNTuple files are both supported.

Quick start

import rootfig as rf

# Overlay two samples, normalised to unity, with a ratio panel.
rf.plot(
    ["signal.root", "background.root"],
    "Muon_pt",
    tree="events",
    selection="abs(Muon_eta) < 2.5",
    weight="event_weight",
    bins=(50, 0, 200),
    normalize=True,
    ratio=True,
)

For analysis scripts with many samples, variables and plots, describe things once and reuse them:

import rootfig as rf

signal = rf.Sample("sig_*.root", tree="events", label="Signal", weight="mc_weight")
background = rf.Sample("bkg.root", tree="events", label="Background", weight="mc_weight")
data = rf.Sample("data.root", tree="events", label="Data", is_data=True)

pt = rf.Variable("Muon_pt", bins=(50, 0, 200), label=r"$p_T^{\mu}$", unit="GeV")
baseline = rf.Cut("nMuon >= 1") & "abs(Muon_eta) < 2.5"
style = rf.Style(experiment="ATLAS", status="Internal", lumi=140, com=13.6)

p = rf.plot(
    [background, signal],
    pt,
    observed=data,
    selection=baseline,
    stack=True,
    ratio=True,
    logy=True,
    style=style,
)
p.ax.set_ylim(top=1e5)  # it is a normal matplotlib Axes
p.save("muon_pt.pdf")

Everything you get back is a standard object: p.fig and p.ax are matplotlib Figure/Axes, p.hists are hist.Hist objects, and rf.load(...) returns Awkward arrays.

Features

  • One call from files to figure, reading only the branches the expressions need.
  • Expressions in Python syntax: sqrt(px**2 + py**2), count(Jet_pt) >= 2, `jet1_b-tag` > 0.5, and/or/not, chained comparisons.
  • Jagged branches done right: per-object cuts mask objects, per-event cuts drop events, ambiguous combinations raise a clear error instead of silently broadcasting.
  • Weights: per-event weights broadcast onto objects, per-object weights, constant scale factors, multiplicative combination of sample and plot weights.
  • Histograms with uncertainties (hist with Weight storage), shared binning across samples, automatic or robust ranges, log bins, flow bins.
  • Overlays, stacks, data points, ratio panels with correct error propagation for weighted histograms and a reference-uncertainty band.
  • Normalisation: to unity, density, per bin width, or to a number; or to a luminosity from cross sections and generated-event counts (Sample(xsec="0.2 pb", ngen="eventsProcessed"), lumi="10.8 ab^-1").
  • Analysis tables and panels: cut flows with yields and efficiencies, significance panels (S/√B), efficiency-versus-variable plots with binomial intervals, profiles and resolutions.
  • Experiment-neutral defaults, with mplhep styles and labels for ATLAS, CMS, LHCb, ALICE and DUNE one keyword away; any other experiment name, GeV and ab⁻¹ work too.
  • EDM4hep-friendly: sub-branches of split collections are addressed as ReconstructedParticles.momentum.x, with pt, p, theta, costheta, eta, phi and mass helpers.
  • Also: 2D histograms, summary statistics tables, statistics boxes, correlation matrices, multi-file globs, entry ranges for quick looks.

Documentation

Relation to the ecosystem

rootfig does not replace any of the libraries it builds on:

Task Library What rootfig adds
Reading ROOT files uproot file globs, tree auto-detection, reading only the required branches
Jagged arrays Awkward Array the per-event/per-object rules for cuts and weights
Histograms hist / boost-histogram shared binning, automatic ranges, normalisation, ratios
Drawing mplhep + matplotlib overlays, stacks, ratio panels, labels and legends with good defaults

If you already have hist.Hist objects, rf.plot_histograms draws them with the same options. If you want the arrays, rf.load returns them.

Development

git clone https://github.com/jbeirer/rootfig
cd rootfig
uv sync --all-groups
uv run pytest
uv run ruff check . && uv run ruff format --check .
uv run mypy

See CONTRIBUTING.md for details.

License

MIT. See LICENSE.

Download files

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

Source Distribution

rootfig-0.2.1.tar.gz (1.3 MB view details)

Uploaded Source

Built Distribution

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

rootfig-0.2.1-py3-none-any.whl (105.4 kB view details)

Uploaded Python 3

File details

Details for the file rootfig-0.2.1.tar.gz.

File metadata

  • Download URL: rootfig-0.2.1.tar.gz
  • Upload date:
  • Size: 1.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for rootfig-0.2.1.tar.gz
Algorithm Hash digest
SHA256 45520a5b05d59c49980e3ec7507b7afefd5c5db69fc48ae2a8666ee811c3adf7
MD5 6f044186a703bbbe24ef05f0888d6eda
BLAKE2b-256 8a76b765a19350ee7d06005b57ddd4c17bc94c314ea95e1017c25bc979a78f26

See more details on using hashes here.

Provenance

The following attestation bundles were made for rootfig-0.2.1.tar.gz:

Publisher: release.yml on jbeirer/rootfig

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rootfig-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: rootfig-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 105.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for rootfig-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1d4672013900bc4a8c6fb3bae926947a16d96f4bc664b9e96d9dce8153d5a003
MD5 207dbd2f388ce3546c37cc088dd0e2f3
BLAKE2b-256 dcbc451db965589e339de90f015a0be68b1a4c61ab3abef443ac7aa1f3a8567e

See more details on using hashes here.

Provenance

The following attestation bundles were made for rootfig-0.2.1-py3-none-any.whl:

Publisher: release.yml on jbeirer/rootfig

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.2

2 files

This release

0.2.1 This release

2 files

0.2.0

2 files

0.1.0

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