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.
import rootfig as rf
rf.plot("events.root", "Muon_pt", tree="events", selection="Muon_pt > 20", bins=50)
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.py, 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 (
histwithWeightstorage), 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, withpt,p,theta,costheta,eta,phiandmasshelpers. - Also: 2D histograms, summary statistics tables, statistics boxes, correlation matrices, multi-file globs, entry ranges for quick looks.
Documentation
- Quick start
- Expressions and selections
- Samples, variables, cuts and styles
- Plotting options
- Relation to uproot, Awkward, hist, mplhep and matplotlib
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
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 rootfig-0.1.0.tar.gz.
File metadata
- Download URL: rootfig-0.1.0.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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
21e1a7e0ef6d83c7c90c5a2985d48a2405851855310d6c311b8f2b031f0443fe
|
|
| MD5 |
f289c55d608fcb6ecf8018e04d36bae3
|
|
| BLAKE2b-256 |
daa6fcb23ee068e8d293cac061cd5507aae214c12719a48645e81ab9a02d029f
|
Provenance
The following attestation bundles were made for rootfig-0.1.0.tar.gz:
Publisher:
release.yml on jbeirer/rootfig
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rootfig-0.1.0.tar.gz -
Subject digest:
21e1a7e0ef6d83c7c90c5a2985d48a2405851855310d6c311b8f2b031f0443fe - Sigstore transparency entry: 2798951878
- Sigstore integration time:
-
Permalink:
jbeirer/rootfig@c1df23aab7b838b1af1473131bfae87d4bd5291d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jbeirer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c1df23aab7b838b1af1473131bfae87d4bd5291d -
Trigger Event:
push
-
Statement type:
File details
Details for the file rootfig-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rootfig-0.1.0-py3-none-any.whl
- Upload date:
- Size: 96.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2b4b629735857a226cf33c08dfaa611cb4bc97b6409e96e7ad15291264ccb7e
|
|
| MD5 |
3116df379c1fb21ad04d6f0c91a28fbf
|
|
| BLAKE2b-256 |
784cff585b084a3f43c3ef7a00a4e7e277ffef3a915e4c5aa2ecd3b4bf4fac44
|
Provenance
The following attestation bundles were made for rootfig-0.1.0-py3-none-any.whl:
Publisher:
release.yml on jbeirer/rootfig
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rootfig-0.1.0-py3-none-any.whl -
Subject digest:
c2b4b629735857a226cf33c08dfaa611cb4bc97b6409e96e7ad15291264ccb7e - Sigstore transparency entry: 2798951937
- Sigstore integration time:
-
Permalink:
jbeirer/rootfig@c1df23aab7b838b1af1473131bfae87d4bd5291d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jbeirer
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c1df23aab7b838b1af1473131bfae87d4bd5291d -
Trigger Event:
push
-
Statement type: