rootfig 0.2.1__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. {rootfig-0.2.1 → rootfig-0.2.2}/PKG-INFO +58 -62
  2. rootfig-0.2.2/README.md +164 -0
  3. {rootfig-0.2.1 → rootfig-0.2.2}/pyproject.toml +1 -1
  4. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/__init__.py +1 -1
  5. rootfig-0.2.1/README.md +0 -168
  6. {rootfig-0.2.1 → rootfig-0.2.2}/.gitignore +0 -0
  7. {rootfig-0.2.1 → rootfig-0.2.2}/CONTRIBUTING.md +0 -0
  8. {rootfig-0.2.1 → rootfig-0.2.2}/LICENSE +0 -0
  9. {rootfig-0.2.1 → rootfig-0.2.2}/docs/api.md +0 -0
  10. {rootfig-0.2.1 → rootfig-0.2.2}/docs/composable.md +0 -0
  11. {rootfig-0.2.1 → rootfig-0.2.2}/docs/ecosystem.md +0 -0
  12. {rootfig-0.2.1 → rootfig-0.2.2}/docs/expressions.md +0 -0
  13. {rootfig-0.2.1 → rootfig-0.2.2}/docs/gallery.md +0 -0
  14. {rootfig-0.2.1 → rootfig-0.2.2}/docs/hooks/gallery.py +0 -0
  15. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/arrays.png +0 -0
  16. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/cms_density.png +0 -0
  17. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/correlation.png +0 -0
  18. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/efficiency.png +0 -0
  19. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/expressions.png +0 -0
  20. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/fill_stats.png +0 -0
  21. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/hist2d.png +0 -0
  22. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/log_axes.png +0 -0
  23. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/luminosity.png +0 -0
  24. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/many_plots.png +0 -0
  25. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/object_vs_event.png +0 -0
  26. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/overlay_ratio.png +0 -0
  27. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/profile.png +0 -0
  28. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/quick.png +0 -0
  29. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/ratio_reference.png +0 -0
  30. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/robust_range.png +0 -0
  31. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/stack_data.png +0 -0
  32. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/style_colors.png +0 -0
  33. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/variable_bins.png +0 -0
  34. {rootfig-0.2.1 → rootfig-0.2.2}/docs/images/gallery/xbreak_ratio.png +0 -0
  35. {rootfig-0.2.1 → rootfig-0.2.2}/docs/index.md +0 -0
  36. {rootfig-0.2.1 → rootfig-0.2.2}/docs/plotting.md +0 -0
  37. {rootfig-0.2.1 → rootfig-0.2.2}/docs/quickstart.md +0 -0
  38. {rootfig-0.2.1 → rootfig-0.2.2}/examples/gallery/__init__.py +0 -0
  39. {rootfig-0.2.1 → rootfig-0.2.2}/examples/gallery/__main__.py +0 -0
  40. {rootfig-0.2.1 → rootfig-0.2.2}/examples/gallery/data.py +0 -0
  41. {rootfig-0.2.1 → rootfig-0.2.2}/examples/gallery/registry.py +0 -0
  42. {rootfig-0.2.1 → rootfig-0.2.2}/mkdocs.yml +0 -0
  43. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/_typing.py +0 -0
  44. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/api.py +0 -0
  45. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/errors.py +0 -0
  46. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/expressions/__init__.py +0 -0
  47. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/expressions/functions.py +0 -0
  48. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/expressions/parser.py +0 -0
  49. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/histograms/__init__.py +0 -0
  50. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/histograms/build.py +0 -0
  51. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/histograms/cutflow.py +0 -0
  52. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/histograms/efficiency.py +0 -0
  53. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/histograms/normalize.py +0 -0
  54. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/histograms/pipeline.py +0 -0
  55. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/histograms/ratio.py +0 -0
  56. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/histograms/stats.py +0 -0
  57. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/io/__init__.py +0 -0
  58. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/io/sources.py +0 -0
  59. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/model/__init__.py +0 -0
  60. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/model/binning.py +0 -0
  61. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/model/cuts.py +0 -0
  62. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/model/samples.py +0 -0
  63. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/model/style.py +0 -0
  64. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/model/units.py +0 -0
  65. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/model/variables.py +0 -0
  66. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/plotting/__init__.py +0 -0
  67. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/plotting/annotations.py +0 -0
  68. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/plotting/correlation.py +0 -0
  69. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/plotting/figure.py +0 -0
  70. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/plotting/hist1d.py +0 -0
  71. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/plotting/hist2d.py +0 -0
  72. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/plotting/points.py +0 -0
  73. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/plotting/ratio.py +0 -0
  74. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/plotting/result.py +0 -0
  75. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/plotting/style.py +0 -0
  76. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/py.typed +0 -0
  77. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/selection/__init__.py +0 -0
  78. {rootfig-0.2.1 → rootfig-0.2.2}/src/rootfig/selection/columns.py +0 -0
  79. {rootfig-0.2.1 → rootfig-0.2.2}/tests/conftest.py +0 -0
  80. {rootfig-0.2.1 → rootfig-0.2.2}/tests/data/split_collection.root +0 -0
  81. {rootfig-0.2.1 → rootfig-0.2.2}/tests/test_api.py +0 -0
  82. {rootfig-0.2.1 → rootfig-0.2.2}/tests/test_expressions.py +0 -0
  83. {rootfig-0.2.1 → rootfig-0.2.2}/tests/test_gallery.py +0 -0
  84. {rootfig-0.2.1 → rootfig-0.2.2}/tests/test_histograms.py +0 -0
  85. {rootfig-0.2.1 → rootfig-0.2.2}/tests/test_io.py +0 -0
  86. {rootfig-0.2.1 → rootfig-0.2.2}/tests/test_model.py +0 -0
  87. {rootfig-0.2.1 → rootfig-0.2.2}/tests/test_plotting.py +0 -0
  88. {rootfig-0.2.1 → rootfig-0.2.2}/tests/test_selection.py +0 -0
  89. {rootfig-0.2.1 → rootfig-0.2.2}/tests/test_tutorials.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: rootfig
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Publication-quality figures straight from ROOT trees, without ROOT: uproot + Awkward + hist + mplhep with a TTree::Draw-like API.
5
5
  Project-URL: Homepage, https://github.com/jbeirer/rootfig
6
- Project-URL: Documentation, https://github.com/jbeirer/rootfig#readme
6
+ Project-URL: Documentation, https://jbeirer.github.io/rootfig/
7
7
  Project-URL: Repository, https://github.com/jbeirer/rootfig
8
8
  Project-URL: Issues, https://github.com/jbeirer/rootfig/issues
9
9
  Author-email: Joshua Falco Beirer <jbeirer@cern.ch>
@@ -35,41 +35,42 @@ Description-Content-Type: text/markdown
35
35
 
36
36
  **Publication-quality figures straight from ROOT trees, without ROOT.**
37
37
 
38
- `rootfig` is the `TTree::Draw` workflow for the Scientific Python HEP stack:
39
- give it ROOT files, a tree, an expression, a selection and a weight, and get a
40
- styled matplotlib figure back in one call. It reads with
41
- [uproot](https://github.com/scikit-hep/uproot5), computes with
42
- [Awkward Array](https://github.com/scikit-hep/awkward), fills
43
- [hist](https://github.com/scikit-hep/hist) histograms and draws with
44
- [mplhep](https://github.com/scikit-hep/mplhep). It adds the missing glue:
45
- predictable per-event/per-object selection semantics, weights, shared
46
- binning across samples, normalisation, ratio panels and good defaults.
47
-
38
+ [![Docs](https://img.shields.io/badge/docs-online-blue)](https://jbeirer.github.io/rootfig/)
48
39
  [![CI](https://github.com/jbeirer/rootfig/actions/workflows/ci.yml/badge.svg)](https://github.com/jbeirer/rootfig/actions/workflows/ci.yml)
49
40
  [![codecov](https://codecov.io/gh/jbeirer/rootfig/branch/main/graph/badge.svg)](https://codecov.io/gh/jbeirer/rootfig)
50
41
  [![PyPI](https://img.shields.io/pypi/v/rootfig.svg)](https://pypi.org/project/rootfig/)
51
42
  [![Python](https://img.shields.io/pypi/pyversions/rootfig.svg)](https://pypi.org/project/rootfig/)
52
43
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
53
44
 
45
+ **[Documentation](https://jbeirer.github.io/rootfig/) ·
46
+ [Gallery](https://jbeirer.github.io/rootfig/gallery/) ·
47
+ [Quick start](https://jbeirer.github.io/rootfig/quickstart/)**
48
+
49
+ Go from ROOT files to a styled figure in one call. Choose a variable, add a
50
+ selection, and plot:
51
+
54
52
  ```python
55
53
  import rootfig as rf
56
54
 
57
55
  rf.plot("events.root", "Muon_pt", tree="events", selection="Muon_pt > 20", bins=50)
58
56
  ```
59
57
 
58
+ Start with a single distribution; add samples, weights, stacks and ratio
59
+ panels as your analysis grows. Every plot gives you a matplotlib figure to
60
+ customise and save. No ROOT installation required.
61
+
60
62
  <p align="center">
61
- <img src="docs/images/gallery/stack_data.png" alt="Stacked simulation with data and a ratio panel" width="48%">
62
- <img src="docs/images/gallery/xbreak_ratio.png" alt="Broken x axis with a ratio panel" width="48%">
63
+ <a href="https://jbeirer.github.io/rootfig/gallery/"><img src="docs/images/gallery/stack_data.png" alt="Stacked simulation with data and a ratio panel" width="48%"></a>
64
+ <a href="https://jbeirer.github.io/rootfig/gallery/"><img src="docs/images/gallery/xbreak_ratio.png" alt="Broken x axis with a ratio panel" width="48%"></a>
63
65
  </p>
64
66
  <p align="center">
65
- <img src="docs/images/gallery/object_vs_event.png" alt="Per-object versus per-event selections" width="48%">
66
- <img src="docs/images/gallery/hist2d.png" alt="Two-dimensional histogram" width="48%">
67
+ <a href="https://jbeirer.github.io/rootfig/gallery/"><img src="docs/images/gallery/object_vs_event.png" alt="Per-object versus per-event selections" width="48%"></a>
68
+ <a href="https://jbeirer.github.io/rootfig/gallery/"><img src="docs/images/gallery/hist2d.png" alt="Two-dimensional histogram" width="48%"></a>
67
69
  </p>
68
70
 
69
- These and a dozen more figures, each next to the code that made it, are in the
70
- [gallery](https://jbeirer.github.io/rootfig/gallery/). All of them come from
71
- [`examples/gallery`](examples/gallery/__init__.py), which writes toy ROOT files and
72
- draws every example in a few seconds; the same figures are pixel-compared in CI.
71
+ **[Explore the gallery →](https://jbeirer.github.io/rootfig/gallery/)**
72
+ See each figure alongside the code that makes it, from simple overlays to
73
+ stacked data/MC comparisons, broken axes and 2D histograms.
73
74
 
74
75
  ## Installation
75
76
 
@@ -82,7 +83,7 @@ uv add rootfig
82
83
  Python 3.12 or newer. No ROOT installation is needed; `TTree` and `RNTuple`
83
84
  files are both supported.
84
85
 
85
- ## Quick start
86
+ ## Compare samples in one call
86
87
 
87
88
  ```python
88
89
  import rootfig as rf
@@ -100,8 +101,9 @@ rf.plot(
100
101
  )
101
102
  ```
102
103
 
103
- For analysis scripts with many samples, variables and plots, describe things
104
- once and reuse them:
104
+ ## Build up to a full analysis
105
+
106
+ Define samples, variables, cuts and styles once, then reuse them across plots:
105
107
 
106
108
  ```python
107
109
  import rootfig as rf
@@ -132,56 +134,50 @@ Everything you get back is a standard object: `p.fig` and `p.ax` are
132
134
  matplotlib `Figure`/`Axes`, `p.hists` are `hist.Hist` objects, and
133
135
  `rf.load(...)` returns Awkward arrays.
134
136
 
135
- ## Features
136
-
137
- - **One call from files to figure**, reading only the branches the expressions need.
138
- - **Expressions in Python syntax**: `sqrt(px**2 + py**2)`, `count(Jet_pt) >= 2`,
139
- `` `jet1_b-tag` > 0.5 ``, `and`/`or`/`not`, chained comparisons.
140
- - **Jagged branches done right**: per-object cuts mask objects, per-event
141
- cuts drop events, ambiguous combinations raise a clear error instead of
142
- silently broadcasting.
143
- - **Weights**: per-event weights broadcast onto objects, per-object weights,
144
- constant scale factors, multiplicative combination of sample and plot weights.
145
- - **Histograms with uncertainties** (`hist` with `Weight` storage), shared
146
- binning across samples, automatic or robust ranges, log bins, flow bins.
147
- - **Overlays, stacks, data points, ratio panels** with correct error
148
- propagation for weighted histograms and a reference-uncertainty band.
149
- - **Normalisation**: to unity, density, per bin width, or to a number; or to
150
- a **luminosity** from cross sections and generated-event counts
151
- (`Sample(xsec="0.2 pb", ngen="eventsProcessed")`, `lumi="10.8 ab^-1"`).
152
- - **Analysis tables and panels**: cut flows with yields and efficiencies,
153
- significance panels (S/√B), efficiency-versus-variable plots with binomial
154
- intervals, profiles and resolutions.
155
- - **Experiment-neutral defaults**, with mplhep styles and labels for ATLAS,
156
- CMS, LHCb, ALICE and DUNE one keyword away; any other experiment name, GeV
157
- and ab⁻¹ work too.
158
- - **EDM4hep-friendly**: sub-branches of split collections are addressed as
159
- `ReconstructedParticles.momentum.x`, with `pt`, `p`, `theta`, `costheta`,
160
- `eta`, `phi` and `mass` helpers.
161
- - **Also**: 2D histograms, summary statistics tables, statistics boxes,
162
- correlation matrices, multi-file globs, entry ranges for quick looks.
137
+ ## What you can do
138
+
139
+ - **Select events and objects with readable expressions.** Write cuts such as
140
+ `count(Jet_pt) >= 2` or `Muon_pt > 20`; event and object selections have
141
+ explicit rules, and event weights carry through to each selected object.
142
+ - **Compare samples with a few keywords.** Overlays, stacks, data points and
143
+ ratio panels share binning and propagate histogram uncertainties.
144
+ Normalise to unity, density, bin width or luminosity.
145
+ - **Style figures for your analysis.** Add experiment labels, units, log axes
146
+ and broken axes, then refine the result with matplotlib.
147
+ - **Go beyond 1D plots.** Draw 2D histograms, correlations, efficiencies,
148
+ profiles, resolutions and significance panels; produce cut flows and
149
+ summary statistics from the same inputs.
150
+ - **Work directly with your files.** Read `TTree` and `RNTuple` data, combine
151
+ files with globs, limit entry ranges for quick checks, and use EDM4hep
152
+ split collections. Only the branches your expressions need are read.
163
153
 
164
154
  ## Documentation
165
155
 
166
- - [Quick start](docs/quickstart.md)
167
- - [Expressions and selections](docs/expressions.md)
168
- - [Samples, variables, cuts and styles](docs/composable.md)
169
- - [Plotting options](docs/plotting.md)
170
- - [Relation to uproot, Awkward, hist, mplhep and matplotlib](docs/ecosystem.md)
156
+ **[Read the docs](https://jbeirer.github.io/rootfig/)** or
157
+ **[browse the gallery](https://jbeirer.github.io/rootfig/gallery/)** for examples
158
+ with figures and code.
159
+
160
+ - [Quick start](https://jbeirer.github.io/rootfig/quickstart/): your first plot, selections and weights.
161
+ - [Expressions and selections](https://jbeirer.github.io/rootfig/expressions/): syntax and event/object rules.
162
+ - [Samples, variables, cuts and styles](https://jbeirer.github.io/rootfig/composable/): reusable analysis definitions.
163
+ - [Plotting options](https://jbeirer.github.io/rootfig/plotting/): binning, normalisation, panels and styling.
164
+ - [API reference](https://jbeirer.github.io/rootfig/api/): full signatures and options.
171
165
 
172
166
  ## Relation to the ecosystem
173
167
 
174
- `rootfig` does not replace any of the libraries it builds on:
168
+ `rootfig` brings a `TTree::Draw`-like workflow to the Scientific Python HEP
169
+ stack, building on familiar libraries:
175
170
 
176
171
  | Task | Library | What rootfig adds |
177
172
  | --- | --- | --- |
178
- | Reading ROOT files | uproot | file globs, tree auto-detection, reading only the required branches |
179
- | Jagged arrays | Awkward Array | the per-event/per-object rules for cuts and weights |
180
- | Histograms | hist / boost-histogram | shared binning, automatic ranges, normalisation, ratios |
181
- | Drawing | mplhep + matplotlib | overlays, stacks, ratio panels, labels and legends with good defaults |
173
+ | Reading ROOT files | [uproot](https://github.com/scikit-hep/uproot5) | file globs, tree auto-detection, reading only the required branches |
174
+ | Jagged arrays | [Awkward Array](https://github.com/scikit-hep/awkward) | the per-event/per-object rules for cuts and weights |
175
+ | Histograms | [hist](https://github.com/scikit-hep/hist) / boost-histogram | shared binning, automatic ranges, normalisation, ratios |
176
+ | Drawing | [mplhep](https://github.com/scikit-hep/mplhep) + matplotlib | overlays, stacks, ratio panels, labels and legends with good defaults |
182
177
 
183
178
  If you already have `hist.Hist` objects, `rf.plot_histograms` draws them with
184
- the same options. If you want the arrays, `rf.load` returns them.
179
+ the same options. If you want the arrays, `rf.load` returns them. See
180
+ [the ecosystem guide](https://jbeirer.github.io/rootfig/ecosystem/) for details.
185
181
 
186
182
  ## Development
187
183
 
@@ -0,0 +1,164 @@
1
+ # rootfig
2
+
3
+ **Publication-quality figures straight from ROOT trees, without ROOT.**
4
+
5
+ [![Docs](https://img.shields.io/badge/docs-online-blue)](https://jbeirer.github.io/rootfig/)
6
+ [![CI](https://github.com/jbeirer/rootfig/actions/workflows/ci.yml/badge.svg)](https://github.com/jbeirer/rootfig/actions/workflows/ci.yml)
7
+ [![codecov](https://codecov.io/gh/jbeirer/rootfig/branch/main/graph/badge.svg)](https://codecov.io/gh/jbeirer/rootfig)
8
+ [![PyPI](https://img.shields.io/pypi/v/rootfig.svg)](https://pypi.org/project/rootfig/)
9
+ [![Python](https://img.shields.io/pypi/pyversions/rootfig.svg)](https://pypi.org/project/rootfig/)
10
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
11
+
12
+ **[Documentation](https://jbeirer.github.io/rootfig/) ·
13
+ [Gallery](https://jbeirer.github.io/rootfig/gallery/) ·
14
+ [Quick start](https://jbeirer.github.io/rootfig/quickstart/)**
15
+
16
+ Go from ROOT files to a styled figure in one call. Choose a variable, add a
17
+ selection, and plot:
18
+
19
+ ```python
20
+ import rootfig as rf
21
+
22
+ rf.plot("events.root", "Muon_pt", tree="events", selection="Muon_pt > 20", bins=50)
23
+ ```
24
+
25
+ Start with a single distribution; add samples, weights, stacks and ratio
26
+ panels as your analysis grows. Every plot gives you a matplotlib figure to
27
+ customise and save. No ROOT installation required.
28
+
29
+ <p align="center">
30
+ <a href="https://jbeirer.github.io/rootfig/gallery/"><img src="docs/images/gallery/stack_data.png" alt="Stacked simulation with data and a ratio panel" width="48%"></a>
31
+ <a href="https://jbeirer.github.io/rootfig/gallery/"><img src="docs/images/gallery/xbreak_ratio.png" alt="Broken x axis with a ratio panel" width="48%"></a>
32
+ </p>
33
+ <p align="center">
34
+ <a href="https://jbeirer.github.io/rootfig/gallery/"><img src="docs/images/gallery/object_vs_event.png" alt="Per-object versus per-event selections" width="48%"></a>
35
+ <a href="https://jbeirer.github.io/rootfig/gallery/"><img src="docs/images/gallery/hist2d.png" alt="Two-dimensional histogram" width="48%"></a>
36
+ </p>
37
+
38
+ **[Explore the gallery →](https://jbeirer.github.io/rootfig/gallery/)**
39
+ See each figure alongside the code that makes it, from simple overlays to
40
+ stacked data/MC comparisons, broken axes and 2D histograms.
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install rootfig
46
+ # or
47
+ uv add rootfig
48
+ ```
49
+
50
+ Python 3.12 or newer. No ROOT installation is needed; `TTree` and `RNTuple`
51
+ files are both supported.
52
+
53
+ ## Compare samples in one call
54
+
55
+ ```python
56
+ import rootfig as rf
57
+
58
+ # Overlay two samples, normalised to unity, with a ratio panel.
59
+ rf.plot(
60
+ ["signal.root", "background.root"],
61
+ "Muon_pt",
62
+ tree="events",
63
+ selection="abs(Muon_eta) < 2.5",
64
+ weight="event_weight",
65
+ bins=(50, 0, 200),
66
+ normalize=True,
67
+ ratio=True,
68
+ )
69
+ ```
70
+
71
+ ## Build up to a full analysis
72
+
73
+ Define samples, variables, cuts and styles once, then reuse them across plots:
74
+
75
+ ```python
76
+ import rootfig as rf
77
+
78
+ signal = rf.Sample("sig_*.root", tree="events", label="Signal", weight="mc_weight")
79
+ background = rf.Sample("bkg.root", tree="events", label="Background", weight="mc_weight")
80
+ data = rf.Sample("data.root", tree="events", label="Data", is_data=True)
81
+
82
+ pt = rf.Variable("Muon_pt", bins=(50, 0, 200), label=r"$p_T^{\mu}$", unit="GeV")
83
+ baseline = rf.Cut("nMuon >= 1") & "abs(Muon_eta) < 2.5"
84
+ style = rf.Style(experiment="ATLAS", status="Internal", lumi=140, com=13.6)
85
+
86
+ p = rf.plot(
87
+ [background, signal],
88
+ pt,
89
+ observed=data,
90
+ selection=baseline,
91
+ stack=True,
92
+ ratio=True,
93
+ logy=True,
94
+ style=style,
95
+ )
96
+ p.ax.set_ylim(top=1e5) # it is a normal matplotlib Axes
97
+ p.save("muon_pt.pdf")
98
+ ```
99
+
100
+ Everything you get back is a standard object: `p.fig` and `p.ax` are
101
+ matplotlib `Figure`/`Axes`, `p.hists` are `hist.Hist` objects, and
102
+ `rf.load(...)` returns Awkward arrays.
103
+
104
+ ## What you can do
105
+
106
+ - **Select events and objects with readable expressions.** Write cuts such as
107
+ `count(Jet_pt) >= 2` or `Muon_pt > 20`; event and object selections have
108
+ explicit rules, and event weights carry through to each selected object.
109
+ - **Compare samples with a few keywords.** Overlays, stacks, data points and
110
+ ratio panels share binning and propagate histogram uncertainties.
111
+ Normalise to unity, density, bin width or luminosity.
112
+ - **Style figures for your analysis.** Add experiment labels, units, log axes
113
+ and broken axes, then refine the result with matplotlib.
114
+ - **Go beyond 1D plots.** Draw 2D histograms, correlations, efficiencies,
115
+ profiles, resolutions and significance panels; produce cut flows and
116
+ summary statistics from the same inputs.
117
+ - **Work directly with your files.** Read `TTree` and `RNTuple` data, combine
118
+ files with globs, limit entry ranges for quick checks, and use EDM4hep
119
+ split collections. Only the branches your expressions need are read.
120
+
121
+ ## Documentation
122
+
123
+ **[Read the docs](https://jbeirer.github.io/rootfig/)** or
124
+ **[browse the gallery](https://jbeirer.github.io/rootfig/gallery/)** for examples
125
+ with figures and code.
126
+
127
+ - [Quick start](https://jbeirer.github.io/rootfig/quickstart/): your first plot, selections and weights.
128
+ - [Expressions and selections](https://jbeirer.github.io/rootfig/expressions/): syntax and event/object rules.
129
+ - [Samples, variables, cuts and styles](https://jbeirer.github.io/rootfig/composable/): reusable analysis definitions.
130
+ - [Plotting options](https://jbeirer.github.io/rootfig/plotting/): binning, normalisation, panels and styling.
131
+ - [API reference](https://jbeirer.github.io/rootfig/api/): full signatures and options.
132
+
133
+ ## Relation to the ecosystem
134
+
135
+ `rootfig` brings a `TTree::Draw`-like workflow to the Scientific Python HEP
136
+ stack, building on familiar libraries:
137
+
138
+ | Task | Library | What rootfig adds |
139
+ | --- | --- | --- |
140
+ | Reading ROOT files | [uproot](https://github.com/scikit-hep/uproot5) | file globs, tree auto-detection, reading only the required branches |
141
+ | Jagged arrays | [Awkward Array](https://github.com/scikit-hep/awkward) | the per-event/per-object rules for cuts and weights |
142
+ | Histograms | [hist](https://github.com/scikit-hep/hist) / boost-histogram | shared binning, automatic ranges, normalisation, ratios |
143
+ | Drawing | [mplhep](https://github.com/scikit-hep/mplhep) + matplotlib | overlays, stacks, ratio panels, labels and legends with good defaults |
144
+
145
+ If you already have `hist.Hist` objects, `rf.plot_histograms` draws them with
146
+ the same options. If you want the arrays, `rf.load` returns them. See
147
+ [the ecosystem guide](https://jbeirer.github.io/rootfig/ecosystem/) for details.
148
+
149
+ ## Development
150
+
151
+ ```bash
152
+ git clone https://github.com/jbeirer/rootfig
153
+ cd rootfig
154
+ uv sync --all-groups
155
+ uv run pytest
156
+ uv run ruff check . && uv run ruff format --check .
157
+ uv run mypy
158
+ ```
159
+
160
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
161
+
162
+ ## License
163
+
164
+ MIT. See [LICENSE](LICENSE).
@@ -40,7 +40,7 @@ dependencies = [
40
40
 
41
41
  [project.urls]
42
42
  Homepage = "https://github.com/jbeirer/rootfig"
43
- Documentation = "https://github.com/jbeirer/rootfig#readme"
43
+ Documentation = "https://jbeirer.github.io/rootfig/"
44
44
  Repository = "https://github.com/jbeirer/rootfig"
45
45
  Issues = "https://github.com/jbeirer/rootfig/issues"
46
46
 
@@ -52,7 +52,7 @@ from rootfig.histograms import (
52
52
  from rootfig.model import Cut, Sample, Style, Variable, log_bins
53
53
  from rootfig.plotting import Plot, use_style
54
54
 
55
- __version__ = "0.2.1"
55
+ __version__ = "0.2.2"
56
56
 
57
57
  __all__ = [
58
58
  "BinningError",
rootfig-0.2.1/README.md DELETED
@@ -1,168 +0,0 @@
1
- # rootfig
2
-
3
- **Publication-quality figures straight from ROOT trees, without ROOT.**
4
-
5
- `rootfig` is the `TTree::Draw` workflow for the Scientific Python HEP stack:
6
- give it ROOT files, a tree, an expression, a selection and a weight, and get a
7
- styled matplotlib figure back in one call. It reads with
8
- [uproot](https://github.com/scikit-hep/uproot5), computes with
9
- [Awkward Array](https://github.com/scikit-hep/awkward), fills
10
- [hist](https://github.com/scikit-hep/hist) histograms and draws with
11
- [mplhep](https://github.com/scikit-hep/mplhep). It adds the missing glue:
12
- predictable per-event/per-object selection semantics, weights, shared
13
- binning across samples, normalisation, ratio panels and good defaults.
14
-
15
- [![CI](https://github.com/jbeirer/rootfig/actions/workflows/ci.yml/badge.svg)](https://github.com/jbeirer/rootfig/actions/workflows/ci.yml)
16
- [![codecov](https://codecov.io/gh/jbeirer/rootfig/branch/main/graph/badge.svg)](https://codecov.io/gh/jbeirer/rootfig)
17
- [![PyPI](https://img.shields.io/pypi/v/rootfig.svg)](https://pypi.org/project/rootfig/)
18
- [![Python](https://img.shields.io/pypi/pyversions/rootfig.svg)](https://pypi.org/project/rootfig/)
19
- [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
20
-
21
- ```python
22
- import rootfig as rf
23
-
24
- rf.plot("events.root", "Muon_pt", tree="events", selection="Muon_pt > 20", bins=50)
25
- ```
26
-
27
- <p align="center">
28
- <img src="docs/images/gallery/stack_data.png" alt="Stacked simulation with data and a ratio panel" width="48%">
29
- <img src="docs/images/gallery/xbreak_ratio.png" alt="Broken x axis with a ratio panel" width="48%">
30
- </p>
31
- <p align="center">
32
- <img src="docs/images/gallery/object_vs_event.png" alt="Per-object versus per-event selections" width="48%">
33
- <img src="docs/images/gallery/hist2d.png" alt="Two-dimensional histogram" width="48%">
34
- </p>
35
-
36
- These and a dozen more figures, each next to the code that made it, are in the
37
- [gallery](https://jbeirer.github.io/rootfig/gallery/). All of them come from
38
- [`examples/gallery`](examples/gallery/__init__.py), which writes toy ROOT files and
39
- draws every example in a few seconds; the same figures are pixel-compared in CI.
40
-
41
- ## Installation
42
-
43
- ```bash
44
- pip install rootfig
45
- # or
46
- uv add rootfig
47
- ```
48
-
49
- Python 3.12 or newer. No ROOT installation is needed; `TTree` and `RNTuple`
50
- files are both supported.
51
-
52
- ## Quick start
53
-
54
- ```python
55
- import rootfig as rf
56
-
57
- # Overlay two samples, normalised to unity, with a ratio panel.
58
- rf.plot(
59
- ["signal.root", "background.root"],
60
- "Muon_pt",
61
- tree="events",
62
- selection="abs(Muon_eta) < 2.5",
63
- weight="event_weight",
64
- bins=(50, 0, 200),
65
- normalize=True,
66
- ratio=True,
67
- )
68
- ```
69
-
70
- For analysis scripts with many samples, variables and plots, describe things
71
- once and reuse them:
72
-
73
- ```python
74
- import rootfig as rf
75
-
76
- signal = rf.Sample("sig_*.root", tree="events", label="Signal", weight="mc_weight")
77
- background = rf.Sample("bkg.root", tree="events", label="Background", weight="mc_weight")
78
- data = rf.Sample("data.root", tree="events", label="Data", is_data=True)
79
-
80
- pt = rf.Variable("Muon_pt", bins=(50, 0, 200), label=r"$p_T^{\mu}$", unit="GeV")
81
- baseline = rf.Cut("nMuon >= 1") & "abs(Muon_eta) < 2.5"
82
- style = rf.Style(experiment="ATLAS", status="Internal", lumi=140, com=13.6)
83
-
84
- p = rf.plot(
85
- [background, signal],
86
- pt,
87
- observed=data,
88
- selection=baseline,
89
- stack=True,
90
- ratio=True,
91
- logy=True,
92
- style=style,
93
- )
94
- p.ax.set_ylim(top=1e5) # it is a normal matplotlib Axes
95
- p.save("muon_pt.pdf")
96
- ```
97
-
98
- Everything you get back is a standard object: `p.fig` and `p.ax` are
99
- matplotlib `Figure`/`Axes`, `p.hists` are `hist.Hist` objects, and
100
- `rf.load(...)` returns Awkward arrays.
101
-
102
- ## Features
103
-
104
- - **One call from files to figure**, reading only the branches the expressions need.
105
- - **Expressions in Python syntax**: `sqrt(px**2 + py**2)`, `count(Jet_pt) >= 2`,
106
- `` `jet1_b-tag` > 0.5 ``, `and`/`or`/`not`, chained comparisons.
107
- - **Jagged branches done right**: per-object cuts mask objects, per-event
108
- cuts drop events, ambiguous combinations raise a clear error instead of
109
- silently broadcasting.
110
- - **Weights**: per-event weights broadcast onto objects, per-object weights,
111
- constant scale factors, multiplicative combination of sample and plot weights.
112
- - **Histograms with uncertainties** (`hist` with `Weight` storage), shared
113
- binning across samples, automatic or robust ranges, log bins, flow bins.
114
- - **Overlays, stacks, data points, ratio panels** with correct error
115
- propagation for weighted histograms and a reference-uncertainty band.
116
- - **Normalisation**: to unity, density, per bin width, or to a number; or to
117
- a **luminosity** from cross sections and generated-event counts
118
- (`Sample(xsec="0.2 pb", ngen="eventsProcessed")`, `lumi="10.8 ab^-1"`).
119
- - **Analysis tables and panels**: cut flows with yields and efficiencies,
120
- significance panels (S/√B), efficiency-versus-variable plots with binomial
121
- intervals, profiles and resolutions.
122
- - **Experiment-neutral defaults**, with mplhep styles and labels for ATLAS,
123
- CMS, LHCb, ALICE and DUNE one keyword away; any other experiment name, GeV
124
- and ab⁻¹ work too.
125
- - **EDM4hep-friendly**: sub-branches of split collections are addressed as
126
- `ReconstructedParticles.momentum.x`, with `pt`, `p`, `theta`, `costheta`,
127
- `eta`, `phi` and `mass` helpers.
128
- - **Also**: 2D histograms, summary statistics tables, statistics boxes,
129
- correlation matrices, multi-file globs, entry ranges for quick looks.
130
-
131
- ## Documentation
132
-
133
- - [Quick start](docs/quickstart.md)
134
- - [Expressions and selections](docs/expressions.md)
135
- - [Samples, variables, cuts and styles](docs/composable.md)
136
- - [Plotting options](docs/plotting.md)
137
- - [Relation to uproot, Awkward, hist, mplhep and matplotlib](docs/ecosystem.md)
138
-
139
- ## Relation to the ecosystem
140
-
141
- `rootfig` does not replace any of the libraries it builds on:
142
-
143
- | Task | Library | What rootfig adds |
144
- | --- | --- | --- |
145
- | Reading ROOT files | uproot | file globs, tree auto-detection, reading only the required branches |
146
- | Jagged arrays | Awkward Array | the per-event/per-object rules for cuts and weights |
147
- | Histograms | hist / boost-histogram | shared binning, automatic ranges, normalisation, ratios |
148
- | Drawing | mplhep + matplotlib | overlays, stacks, ratio panels, labels and legends with good defaults |
149
-
150
- If you already have `hist.Hist` objects, `rf.plot_histograms` draws them with
151
- the same options. If you want the arrays, `rf.load` returns them.
152
-
153
- ## Development
154
-
155
- ```bash
156
- git clone https://github.com/jbeirer/rootfig
157
- cd rootfig
158
- uv sync --all-groups
159
- uv run pytest
160
- uv run ruff check . && uv run ruff format --check .
161
- uv run mypy
162
- ```
163
-
164
- See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
165
-
166
- ## License
167
-
168
- MIT. See [LICENSE](LICENSE).
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes