rootfig 0.6.0__tar.gz → 0.7.0__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.
- {rootfig-0.6.0 → rootfig-0.7.0}/CONTRIBUTING.md +10 -2
- {rootfig-0.6.0 → rootfig-0.7.0}/PKG-INFO +52 -5
- {rootfig-0.6.0 → rootfig-0.7.0}/README.md +48 -1
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/api.md +10 -0
- rootfig-0.7.0/docs/batch.md +411 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/composable.md +62 -3
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/index.md +1 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/plotting.md +15 -2
- {rootfig-0.6.0 → rootfig-0.7.0}/mkdocs.yml +1 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/pyproject.toml +8 -6
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/__init__.py +14 -3
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/api/__init__.py +5 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/api/_common.py +23 -9
- rootfig-0.7.0/src/rootfig/api/_discover.py +527 -0
- rootfig-0.7.0/src/rootfig/api/_pdf.py +181 -0
- rootfig-0.7.0/src/rootfig/api/batch.py +992 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/api/data.py +22 -12
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/api/plots1d.py +225 -78
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/api/plots2d.py +2 -2
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/expressions/__init__.py +10 -2
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/expressions/parser.py +25 -1
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/histograms/__init__.py +10 -1
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/histograms/build.py +15 -3
- rootfig-0.7.0/src/rootfig/histograms/groups.py +50 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/histograms/pipeline.py +153 -59
- rootfig-0.7.0/src/rootfig/histograms/prefetch.py +212 -0
- rootfig-0.7.0/src/rootfig/histograms/sources.py +24 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/histograms/stored.py +76 -16
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/histograms/systematics.py +3 -1
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/io/__init__.py +10 -1
- rootfig-0.7.0/src/rootfig/io/cache.py +80 -0
- rootfig-0.7.0/src/rootfig/io/objects.py +165 -0
- rootfig-0.7.0/src/rootfig/io/schema.py +98 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/io/sources.py +167 -47
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/model/__init__.py +11 -1
- rootfig-0.7.0/src/rootfig/model/filenames.py +68 -0
- rootfig-0.7.0/src/rootfig/model/groups.py +139 -0
- rootfig-0.7.0/src/rootfig/model/inputs.py +156 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/model/samples.py +2 -83
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/model/variables.py +15 -21
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/plotting/__init__.py +8 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/plotting/figure.py +84 -18
- rootfig-0.7.0/src/rootfig/plotting/pages.py +279 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/plotting/result.py +29 -1
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/plotting/style.py +39 -11
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/test_api.py +201 -1
- rootfig-0.7.0/tests/test_batch.py +1269 -0
- rootfig-0.7.0/tests/test_discovery.py +1139 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/test_expressions.py +39 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/test_histograms.py +494 -1
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/test_io.py +448 -1
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/test_model.py +249 -3
- rootfig-0.7.0/tests/test_pages.py +256 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/test_plotting.py +74 -0
- rootfig-0.7.0/tests/test_save_pdf.py +688 -0
- rootfig-0.7.0/tests/type_checks/batch.py +71 -0
- rootfig-0.7.0/tests/type_checks/groups.py +23 -0
- rootfig-0.6.0/src/rootfig/io/objects.py +0 -128
- {rootfig-0.6.0 → rootfig-0.7.0}/.gitignore +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/LICENSE +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/ecosystem.md +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/expressions.md +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/gallery/index.md +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/hooks/gallery.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/arrays.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/correlation.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/density_flow.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/efficiency.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/expressions.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/fill_stats.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/hist2d.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/log_axes.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/luminosity-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/luminosity.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/many_plots-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/many_plots.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/object_vs_event.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/overlay_ratio.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/profile.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/quick.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/ratio_reference.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/robust_range-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/robust_range.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/stack_data.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/style_colors-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/style_colors.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/systematics.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/variable_bins.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-alice-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-alice.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-atlas-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-atlas.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-cms-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-cms.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-dune-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-dune.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-lhcb-dark.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio-lhcb.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/images/gallery/xbreak_ratio.png +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/quickstart.md +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/docs/stylesheets/gallery.css +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/examples/gallery/__init__.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/examples/gallery/__main__.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/examples/gallery/data.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/examples/gallery/registry.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/_mapping.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/_storage.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/_typing.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/api/_hists.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/api/measures.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/api/tables.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/errors.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/expressions/functions.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/histograms/cutflow.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/histograms/efficiency.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/histograms/normalize.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/histograms/ratio.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/histograms/stats.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/model/binning.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/model/cuts.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/model/style.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/model/systematics.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/model/units.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/plotting/annotations.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/plotting/correlation.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/plotting/hist1d.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/plotting/hist2d.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/plotting/points.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/plotting/ratio.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/py.typed +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/selection/__init__.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/src/rootfig/selection/columns.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/conftest.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/data/split_collection.root +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/test_gallery.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/test_selection.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/test_tutorials.py +0 -0
- {rootfig-0.6.0 → rootfig-0.7.0}/tests/type_checks/systematics.py +0 -0
|
@@ -32,6 +32,14 @@ that use ROOT's tutorial files run only when `root-config` is available
|
|
|
32
32
|
locally and are skipped otherwise; do not add
|
|
33
33
|
tests that require ROOT or network access.
|
|
34
34
|
|
|
35
|
+
`key4hep.yml` runs the tests on the Key4hep nightlies (LCG `devkey-head`) and the
|
|
36
|
+
latest Key4hep release, weekly as well, since the stacks change without a commit
|
|
37
|
+
here. `.github/scripts/key4hep-test.sh` installs rootfig into a
|
|
38
|
+
[cvmfs-venv](https://github.com/jbeirer/cvmfs-venv) with `--no-index`, so every
|
|
39
|
+
runtime dependency must come from the stack: the floors in `pyproject.toml` stay
|
|
40
|
+
at or below what the nightlies ship. Locally: `.github/scripts/key4hep-test.sh
|
|
41
|
+
/cvmfs/sw-nightlies.hsf.org/key4hep/setup.sh`.
|
|
42
|
+
|
|
35
43
|
## Layout
|
|
36
44
|
|
|
37
45
|
```
|
|
@@ -40,9 +48,9 @@ src/rootfig/
|
|
|
40
48
|
errors.py exception hierarchy
|
|
41
49
|
expressions/ parse, validate and evaluate expression strings
|
|
42
50
|
io/ file and in-memory data sources
|
|
43
|
-
model/ Sample, Variable, Cut, Style, binning
|
|
51
|
+
model/ Sample, Group, Variable, Cut, Style, binning
|
|
44
52
|
selection/ per-event / per-object semantics -> flat columns
|
|
45
|
-
histograms/ filling, normalisation, ratios, statistics, pipeline
|
|
53
|
+
histograms/ filling, normalisation, ratios, statistics, groups, pipeline
|
|
46
54
|
plotting/ matplotlib/mplhep rendering, styles, annotations
|
|
47
55
|
tests/ one module per layer plus end-to-end API tests
|
|
48
56
|
docs/ MkDocs sources
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: rootfig
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
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
6
|
Project-URL: Documentation, https://jbeirer.github.io/rootfig/
|
|
@@ -24,11 +24,11 @@ Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
|
24
24
|
Classifier: Typing :: Typed
|
|
25
25
|
Requires-Python: >=3.12
|
|
26
26
|
Requires-Dist: awkward>=2.8
|
|
27
|
-
Requires-Dist: hist>=2.
|
|
27
|
+
Requires-Dist: hist>=2.9
|
|
28
28
|
Requires-Dist: matplotlib>=3.11
|
|
29
|
-
Requires-Dist: mplhep>=1.
|
|
29
|
+
Requires-Dist: mplhep>=1.2.1
|
|
30
30
|
Requires-Dist: numpy>=1.26
|
|
31
|
-
Requires-Dist: uproot>=5.7.
|
|
31
|
+
Requires-Dist: uproot>=5.7.1
|
|
32
32
|
Description-Content-Type: text/markdown
|
|
33
33
|
|
|
34
34
|
<p align="center">
|
|
@@ -53,6 +53,7 @@ Description-Content-Type: text/markdown
|
|
|
53
53
|
<a href="https://jbeirer.github.io/rootfig/"><img src="https://img.shields.io/badge/docs-online-blue" alt="Documentation"></a>
|
|
54
54
|
<a href="https://doi.org/10.5281/zenodo.22726311"><img src="https://zenodo.org/badge/1366702602.svg" alt="DOI"></a>
|
|
55
55
|
<a href="https://github.com/jbeirer/rootfig/actions/workflows/ci.yml"><img src="https://github.com/jbeirer/rootfig/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
56
|
+
<a href="https://github.com/jbeirer/rootfig/actions/workflows/key4hep.yml"><img src="https://github.com/jbeirer/rootfig/actions/workflows/key4hep.yml/badge.svg" alt="Key4hep"></a>
|
|
56
57
|
<a href="https://codecov.io/gh/jbeirer/rootfig"><img src="https://codecov.io/gh/jbeirer/rootfig/branch/main/graph/badge.svg" alt="codecov"></a>
|
|
57
58
|
<a href="https://pypi.org/project/rootfig/"><img src="https://img.shields.io/pypi/v/rootfig.svg" alt="PyPI"></a>
|
|
58
59
|
<a href="https://pypi.org/project/rootfig/"><img src="https://img.shields.io/pypi/pyversions/rootfig.svg" alt="Python"></a>
|
|
@@ -150,6 +151,45 @@ Everything you get back is a standard object: `p.fig` and `p.ax` are
|
|
|
150
151
|
matplotlib `Figure`/`Axes`, `p.hists` are `hist.Hist` objects, and
|
|
151
152
|
`rf.load(...)` returns Awkward arrays.
|
|
152
153
|
|
|
154
|
+
Samples that belong to one physics category are drawn as one histogram with
|
|
155
|
+
`rf.Group`. Each keeps its own files, weights, cross section and systematics;
|
|
156
|
+
they are summed only after filling:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
ww = rf.Sample("ww.root", tree="events", label="WW", weight="mc_weight")
|
|
160
|
+
zz = rf.Sample("zz.root", tree="events", label="ZZ", weight="mc_weight")
|
|
161
|
+
vv = rf.Group([ww, zz], label="VV")
|
|
162
|
+
|
|
163
|
+
rf.plot([vv, signal], pt, observed=data, stack=True, ratio=True, style=style)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Whole sets of plots, every variable under each selection in every drawing
|
|
167
|
+
variant, are one `rf.PlotBook`: it runs that same `rf.plot` call per
|
|
168
|
+
combination and writes deterministically named files:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
book = rf.PlotBook(
|
|
172
|
+
[vv, signal],
|
|
173
|
+
[pt, rf.Variable("MET", bins=(40, 0, 200), unit="GeV")],
|
|
174
|
+
selections={"baseline": baseline, "sr": baseline & "MET > 50"},
|
|
175
|
+
variants={"lin": {}, "log": {"logy": True}},
|
|
176
|
+
plot_kwargs={"observed": data, "stack": True, "ratio": True, "style": style},
|
|
177
|
+
)
|
|
178
|
+
book.save("plots/", formats=["pdf", "png"]) # plots/Muon_pt__sr__log.pdf, ...
|
|
179
|
+
book.save_pdf("overview.pdf") # one automatically arranged multipage PDF
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`rf.ALL` discovers the variables instead, from the branch types and stored
|
|
183
|
+
histograms of the files (metadata only), filtered by name:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
book = rf.PlotBook(
|
|
187
|
+
[vv, signal],
|
|
188
|
+
variables=rf.ALL,
|
|
189
|
+
exclude=["*_cov", "*Index"],
|
|
190
|
+
)
|
|
191
|
+
```
|
|
192
|
+
|
|
153
193
|
## What you can do
|
|
154
194
|
|
|
155
195
|
- **Select events and objects with readable expressions.** Write cuts such as
|
|
@@ -159,13 +199,20 @@ matplotlib `Figure`/`Axes`, `p.hists` are `hist.Hist` objects, and
|
|
|
159
199
|
ratio panels share binning and propagate histogram uncertainties; bin edges
|
|
160
200
|
and `(n, low, high)` are used as given, while a range inferred from the data
|
|
161
201
|
ignores far outliers, so `-999` sentinels do not set the axis. Normalise to
|
|
162
|
-
unity, density, bin width or luminosity.
|
|
202
|
+
unity, density, bin width or luminosity. Draw several samples as one
|
|
203
|
+
histogram with `rf.Group`, each keeping its own weights, cross section and
|
|
204
|
+
systematics.
|
|
163
205
|
- **Show systematic uncertainties.** Attach weight, branch, file or
|
|
164
206
|
normalisation variations to a sample; stacks and ratio panels draw the
|
|
165
207
|
combined statistical and systematic band, and every component stays
|
|
166
208
|
accessible.
|
|
167
209
|
- **Style figures for your analysis.** Add experiment labels, units, log axes
|
|
168
210
|
and broken axes, then refine the result with matplotlib.
|
|
211
|
+
- **Produce whole sets of plots.** `rf.PlotBook` runs one `rf.plot` call over
|
|
212
|
+
variables × selections × variants, lazily, and saves each under a
|
|
213
|
+
deterministic name or all of them as one multipage PDF; `rf.ALL` discovers
|
|
214
|
+
the variables from the files, and `select()` filters the book down while
|
|
215
|
+
iterating on a plot.
|
|
169
216
|
- **Go beyond 1D plots.** Draw 2D histograms, correlations, efficiencies,
|
|
170
217
|
profiles, resolutions and significance panels; produce cut flows and
|
|
171
218
|
summary statistics from the same inputs.
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
<a href="https://jbeirer.github.io/rootfig/"><img src="https://img.shields.io/badge/docs-online-blue" alt="Documentation"></a>
|
|
21
21
|
<a href="https://doi.org/10.5281/zenodo.22726311"><img src="https://zenodo.org/badge/1366702602.svg" alt="DOI"></a>
|
|
22
22
|
<a href="https://github.com/jbeirer/rootfig/actions/workflows/ci.yml"><img src="https://github.com/jbeirer/rootfig/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
23
|
+
<a href="https://github.com/jbeirer/rootfig/actions/workflows/key4hep.yml"><img src="https://github.com/jbeirer/rootfig/actions/workflows/key4hep.yml/badge.svg" alt="Key4hep"></a>
|
|
23
24
|
<a href="https://codecov.io/gh/jbeirer/rootfig"><img src="https://codecov.io/gh/jbeirer/rootfig/branch/main/graph/badge.svg" alt="codecov"></a>
|
|
24
25
|
<a href="https://pypi.org/project/rootfig/"><img src="https://img.shields.io/pypi/v/rootfig.svg" alt="PyPI"></a>
|
|
25
26
|
<a href="https://pypi.org/project/rootfig/"><img src="https://img.shields.io/pypi/pyversions/rootfig.svg" alt="Python"></a>
|
|
@@ -117,6 +118,45 @@ Everything you get back is a standard object: `p.fig` and `p.ax` are
|
|
|
117
118
|
matplotlib `Figure`/`Axes`, `p.hists` are `hist.Hist` objects, and
|
|
118
119
|
`rf.load(...)` returns Awkward arrays.
|
|
119
120
|
|
|
121
|
+
Samples that belong to one physics category are drawn as one histogram with
|
|
122
|
+
`rf.Group`. Each keeps its own files, weights, cross section and systematics;
|
|
123
|
+
they are summed only after filling:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
ww = rf.Sample("ww.root", tree="events", label="WW", weight="mc_weight")
|
|
127
|
+
zz = rf.Sample("zz.root", tree="events", label="ZZ", weight="mc_weight")
|
|
128
|
+
vv = rf.Group([ww, zz], label="VV")
|
|
129
|
+
|
|
130
|
+
rf.plot([vv, signal], pt, observed=data, stack=True, ratio=True, style=style)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Whole sets of plots, every variable under each selection in every drawing
|
|
134
|
+
variant, are one `rf.PlotBook`: it runs that same `rf.plot` call per
|
|
135
|
+
combination and writes deterministically named files:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
book = rf.PlotBook(
|
|
139
|
+
[vv, signal],
|
|
140
|
+
[pt, rf.Variable("MET", bins=(40, 0, 200), unit="GeV")],
|
|
141
|
+
selections={"baseline": baseline, "sr": baseline & "MET > 50"},
|
|
142
|
+
variants={"lin": {}, "log": {"logy": True}},
|
|
143
|
+
plot_kwargs={"observed": data, "stack": True, "ratio": True, "style": style},
|
|
144
|
+
)
|
|
145
|
+
book.save("plots/", formats=["pdf", "png"]) # plots/Muon_pt__sr__log.pdf, ...
|
|
146
|
+
book.save_pdf("overview.pdf") # one automatically arranged multipage PDF
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`rf.ALL` discovers the variables instead, from the branch types and stored
|
|
150
|
+
histograms of the files (metadata only), filtered by name:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
book = rf.PlotBook(
|
|
154
|
+
[vv, signal],
|
|
155
|
+
variables=rf.ALL,
|
|
156
|
+
exclude=["*_cov", "*Index"],
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
120
160
|
## What you can do
|
|
121
161
|
|
|
122
162
|
- **Select events and objects with readable expressions.** Write cuts such as
|
|
@@ -126,13 +166,20 @@ matplotlib `Figure`/`Axes`, `p.hists` are `hist.Hist` objects, and
|
|
|
126
166
|
ratio panels share binning and propagate histogram uncertainties; bin edges
|
|
127
167
|
and `(n, low, high)` are used as given, while a range inferred from the data
|
|
128
168
|
ignores far outliers, so `-999` sentinels do not set the axis. Normalise to
|
|
129
|
-
unity, density, bin width or luminosity.
|
|
169
|
+
unity, density, bin width or luminosity. Draw several samples as one
|
|
170
|
+
histogram with `rf.Group`, each keeping its own weights, cross section and
|
|
171
|
+
systematics.
|
|
130
172
|
- **Show systematic uncertainties.** Attach weight, branch, file or
|
|
131
173
|
normalisation variations to a sample; stacks and ratio panels draw the
|
|
132
174
|
combined statistical and systematic band, and every component stays
|
|
133
175
|
accessible.
|
|
134
176
|
- **Style figures for your analysis.** Add experiment labels, units, log axes
|
|
135
177
|
and broken axes, then refine the result with matplotlib.
|
|
178
|
+
- **Produce whole sets of plots.** `rf.PlotBook` runs one `rf.plot` call over
|
|
179
|
+
variables × selections × variants, lazily, and saves each under a
|
|
180
|
+
deterministic name or all of them as one multipage PDF; `rf.ALL` discovers
|
|
181
|
+
the variables from the files, and `select()` filters the book down while
|
|
182
|
+
iterating on a plot.
|
|
136
183
|
- **Go beyond 1D plots.** Draw 2D histograms, correlations, efficiencies,
|
|
137
184
|
profiles, resolutions and significance panels; produce cut flows and
|
|
138
185
|
summary statistics from the same inputs.
|
|
@@ -22,11 +22,21 @@
|
|
|
22
22
|
## Descriptions
|
|
23
23
|
|
|
24
24
|
::: rootfig.Sample
|
|
25
|
+
::: rootfig.Group
|
|
25
26
|
::: rootfig.Variable
|
|
26
27
|
::: rootfig.Cut
|
|
27
28
|
::: rootfig.Style
|
|
28
29
|
::: rootfig.Systematic
|
|
29
30
|
|
|
31
|
+
## Batch plotting
|
|
32
|
+
|
|
33
|
+
::: rootfig.PlotBook
|
|
34
|
+
::: rootfig.PlotTask
|
|
35
|
+
::: rootfig.ALL
|
|
36
|
+
::: rootfig.discover_variables
|
|
37
|
+
::: rootfig.model.check_file_stem
|
|
38
|
+
::: rootfig.model.safe_file_stem
|
|
39
|
+
|
|
30
40
|
## Results
|
|
31
41
|
|
|
32
42
|
::: rootfig.Plot
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
# Batch plotting
|
|
2
|
+
|
|
3
|
+
An analysis rarely needs one plot. It needs every variable, under each
|
|
4
|
+
selection, drawn linear and logarithmic. `rf.PlotBook` describes that set once
|
|
5
|
+
and runs [`rf.plot`][rootfig.plot] for each member: it is repeated
|
|
6
|
+
`rf.plot(...)`, not a second plotting engine.
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
import rootfig as rf
|
|
10
|
+
|
|
11
|
+
ww = rf.Sample("ww.root", tree="events", label="WW", weight="mc_weight")
|
|
12
|
+
zz = rf.Sample("zz.root", tree="events", label="ZZ", weight="mc_weight")
|
|
13
|
+
signal = rf.Sample("signal.root", tree="events", label="Signal", weight="mc_weight")
|
|
14
|
+
|
|
15
|
+
book = rf.PlotBook(
|
|
16
|
+
[rf.Group([ww, zz], label="VV"), signal],
|
|
17
|
+
[
|
|
18
|
+
rf.Variable("mass", bins=(50, 100, 150), unit="GeV"),
|
|
19
|
+
rf.Variable("pt", bins=(50, 0, 200), unit="GeV"),
|
|
20
|
+
],
|
|
21
|
+
selections={
|
|
22
|
+
"baseline": rf.Cut("nMuon >= 2"),
|
|
23
|
+
"sr": rf.Cut("nMuon >= 2") & "recoil_mass > 120",
|
|
24
|
+
},
|
|
25
|
+
variants={
|
|
26
|
+
"lin": {},
|
|
27
|
+
"log": {"logy": True},
|
|
28
|
+
},
|
|
29
|
+
plot_kwargs={"stack": True, "style": rf.Style(experiment="ATLAS", status="Internal")},
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
book.save("plots", formats=["pdf", "png"])
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This writes eight plots in two formats, `plots/mass__baseline__lin.pdf`,
|
|
36
|
+
`plots/mass__baseline__lin.png`, ... `plots/pt__sr__log.png`.
|
|
37
|
+
|
|
38
|
+
## The three axes
|
|
39
|
+
|
|
40
|
+
The tasks of a book are the Cartesian product
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
variables × selections × variants
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
in insertion order, variables outermost and variants innermost. Two variables,
|
|
47
|
+
three selections and two variants give twelve tasks, and `book.tasks()` lists
|
|
48
|
+
them without reading or drawing anything.
|
|
49
|
+
|
|
50
|
+
**Variables** are names, expressions or [`Variable`](composable.md#variable)
|
|
51
|
+
objects; one may be given bare (`rf.PlotBook(data, "mass")`).
|
|
52
|
+
`Variable.safe_name` (the `name=`, else
|
|
53
|
+
[`safe_file_stem`][rootfig.model.safe_file_stem] of the expression) identifies a
|
|
54
|
+
variable and names its files, so
|
|
55
|
+
two variables of one book must not share it: `Variable("Muon_pt", name="pt")`
|
|
56
|
+
next to `Variable("Electron_pt", name="pt")` is rejected.
|
|
57
|
+
|
|
58
|
+
**Selections** are a mapping from a name to a cut (a string, a
|
|
59
|
+
[`Cut`](composable.md#cut) or `None` for "no cut"). Names are the file name
|
|
60
|
+
component, never derived from the expression or the cut's label.
|
|
61
|
+
|
|
62
|
+
**Variants** are a mapping from a name to `plot()` keywords. A variant's
|
|
63
|
+
keywords override the common `plot_kwargs` for its tasks, so
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
book = rf.PlotBook(
|
|
67
|
+
data,
|
|
68
|
+
["mass"],
|
|
69
|
+
plot_kwargs={"stack": True, "logy": False},
|
|
70
|
+
variants={"lin": {}, "log": {"logy": True}},
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
draws `lin` with `stack=True, logy=False` and `log` with `stack=True, logy=True`.
|
|
75
|
+
|
|
76
|
+
Every other keyword of `rf.plot` (`observed=`, `normalize=`, `ratio=`,
|
|
77
|
+
`systematics=`, `style=`, ...) goes into `plot_kwargs` or a variant. The keyword
|
|
78
|
+
names are checked against `rf.plot`'s signature when the book is built, so a
|
|
79
|
+
misspelt `log_y` is reported, with `logy` as the suggestion, before anything is
|
|
80
|
+
drawn rather than after the first tasks have written their files; the values are
|
|
81
|
+
validated by `rf.plot` itself. Five keywords are the book's own and are rejected:
|
|
82
|
+
`data`, `variable` and `selection` come from the task, `save` from
|
|
83
|
+
`PlotBook.save()`, and `ax` because every task draws its own figure.
|
|
84
|
+
|
|
85
|
+
A reserved keyword, an empty `selections=` or `variants=`, an unusable name and a
|
|
86
|
+
pair of tasks whose files would collide all raise `ValueError` when the book is
|
|
87
|
+
built. A `selections=`, `variants=` or `plot_kwargs=` that is not a mapping, a
|
|
88
|
+
name that is not a string, and a keyword `rf.plot` does not take, raise
|
|
89
|
+
`TypeError`.
|
|
90
|
+
|
|
91
|
+
Selection and variant names become file name components and are checked, like
|
|
92
|
+
`Variable.name`, for every platform: more than dots and spaces, no trailing dot
|
|
93
|
+
or space, no slash, backslash, control character or `<>:"|?*`, and not a Windows
|
|
94
|
+
device name (`CON`, `NUL`, `COM1`, ...). The check is
|
|
95
|
+
[`check_file_stem`][rootfig.model.check_file_stem]; its message offers a
|
|
96
|
+
spelling that works.
|
|
97
|
+
|
|
98
|
+
## Automatic variable discovery
|
|
99
|
+
|
|
100
|
+
`rf.ALL` in place of the variable list asks the book to find the variables
|
|
101
|
+
itself:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
book = rf.PlotBook(
|
|
105
|
+
samples,
|
|
106
|
+
variables=rf.ALL,
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Discovery reads metadata only. For a `TTree` or `RNTuple` it is the schema:
|
|
111
|
+
every branch (or nested field, `Muon.pt`) whose values are numbers or booleans,
|
|
112
|
+
lists and fixed-size arrays of them included, becomes a variable; strings,
|
|
113
|
+
records and other objects are left out. For a ROOT file read without a tree it
|
|
114
|
+
is the object list: every stored `TH1` becomes a variable; `TH2`, `TProfile`,
|
|
115
|
+
`TParameter` and the like do not, and a histogram inside a directory is named by
|
|
116
|
+
its path (`sel/mz`). A file holding both is treated as `rf.plot` treats it: the
|
|
117
|
+
branches of its one tree plus the stored `TH1`s no branch shadows; an explicit
|
|
118
|
+
`tree=` means branches only, and several trees without `tree=` raise, as they
|
|
119
|
+
do for any plot. In-memory arrays contribute their numeric and boolean columns.
|
|
120
|
+
No event array and no bin content is read, and the first file of a sample
|
|
121
|
+
stands for all of them, as everywhere in rootfig.
|
|
122
|
+
|
|
123
|
+
With several samples, groups (through their leaf samples) and `observed=`, a
|
|
124
|
+
variable must be present in every one of them, the same way: a name that is a
|
|
125
|
+
branch in one sample and a stored histogram in another is not a common
|
|
126
|
+
variable. Variants that change how the histograms are prepared (`tree`,
|
|
127
|
+
`observed`, `weight`, ...) must all be able to plot it too, so the discovered
|
|
128
|
+
set is the intersection over the effective configurations. The files or arrays
|
|
129
|
+
a `Systematic.samples` variation fills or reads from are surveyed like a sample
|
|
130
|
+
of their own and take part in the intersection, in the mode of the sample they
|
|
131
|
+
vary; a variation that cannot be built or surveyed is left to the task, which
|
|
132
|
+
reports it whatever the variable. A branch that a branch-replacement systematic
|
|
133
|
+
replaces is kept only when its replacement is a plottable branch of the sample,
|
|
134
|
+
since the replacement is read whenever the branch is. Stored histograms
|
|
135
|
+
are left out whenever the book is bound to refuse them: a selection, a
|
|
136
|
+
`weight`, `nonfinite="error"`, a `stats` box, a `range` without `bins`, or a
|
|
137
|
+
systematic varying the weight or branches that applies to a sample (the plot's
|
|
138
|
+
unless the sample's own source of that name replaces it, none for observed
|
|
139
|
+
data), in the book's keywords or in any variant's. A file of stored histograms
|
|
140
|
+
only then fails when the book is built, saying why, rather than at its first
|
|
141
|
+
task; the message lists what each sample holds, what was left out and why, and
|
|
142
|
+
which names the samples do not share.
|
|
143
|
+
|
|
144
|
+
`include=` and `exclude=` narrow the set with case-sensitive shell patterns
|
|
145
|
+
(`*`, `?`, `[...]`, as `fnmatch` reads them), one or a sequence:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
book = rf.PlotBook(
|
|
149
|
+
samples,
|
|
150
|
+
variables=rf.ALL,
|
|
151
|
+
include=["Muon_*", "Electron_*", "MET*"],
|
|
152
|
+
exclude=["*_cov", "*Index"],
|
|
153
|
+
)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
A variable is kept when it matches one `include` pattern (all do when `include`
|
|
157
|
+
is not given) and no `exclude` pattern. Patterns match the source name, `sel/mz`
|
|
158
|
+
or `jet1_b-tag`, not the file name component `sel_mz` made from it. They are
|
|
159
|
+
only valid with `rf.ALL`: an explicit list is used as it is, and `include=` or
|
|
160
|
+
`exclude=` next to one raises. So does a filter that leaves nothing; a pattern
|
|
161
|
+
that matches nothing is fine as long as others do.
|
|
162
|
+
|
|
163
|
+
The result is an ordinary tuple of `Variable` objects, sorted by source name,
|
|
164
|
+
each addressing exactly its branch or histogram (`` `jet1_b-tag` `` and
|
|
165
|
+
`` `sel/mz` `` in backticks). `book.variables` shows exactly what was
|
|
166
|
+
discovered, and from there on nothing distinguishes the book from one built
|
|
167
|
+
with that list: the same file names (two names that sanitise to one component,
|
|
168
|
+
`a-b` and `a_b`, are rejected as for an explicit list; exclude one or name
|
|
169
|
+
them), the same `tasks()`, `select()` (which keeps the discovered variables
|
|
170
|
+
rather than discovering again) and the same batched execution described below.
|
|
171
|
+
|
|
172
|
+
`rf.discover_variables(data, ...)` runs the same discovery without building a
|
|
173
|
+
book, under the same `selections=`, `variants=`, `plot_kwargs=`, `include=` and
|
|
174
|
+
`exclude=` keywords, and returns the variables. It does not check output file
|
|
175
|
+
names, so two names that sanitise to one component are both returned; tell them
|
|
176
|
+
apart with `Variable.replace(name=...)` and pass the list to `PlotBook`:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
variables = rf.discover_variables(samples, include="jet*")
|
|
180
|
+
renamed = [v.replace(name="jet1_btag") if v.expression == "`jet1_b-tag`" else v for v in variables]
|
|
181
|
+
book = rf.PlotBook(samples, renamed)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Data passes through unchanged
|
|
185
|
+
|
|
186
|
+
`data` is stored as given and handed to `rf.plot` as is, never copied or
|
|
187
|
+
flattened; with an explicit variable list it is not inspected either, and
|
|
188
|
+
`rf.ALL` reads its metadata only. Every form of `data` that `rf.plot` accepts
|
|
189
|
+
can therefore be used in a book: file paths and globs, `Sample` objects,
|
|
190
|
+
`Group` objects (drawn as one histogram), a `Group` as `observed=`, variables
|
|
191
|
+
that name a `TH1` stored in the files, in-memory arrays and `hist.Hist` or
|
|
192
|
+
`Histogram` objects. The rules of `rf.plot` apply unchanged: a book always
|
|
193
|
+
names at least one variable (`rf.ALL` needs inputs with names to discover, so
|
|
194
|
+
histogram objects take an explicit one), and a `selection` or `weight` raises
|
|
195
|
+
for a ready-made histogram, which is drawn as it is.
|
|
196
|
+
|
|
197
|
+
The book copies the mappings it is configured with (the variable list,
|
|
198
|
+
`selections`, `variants` and each keyword mapping) into read-only copies, so
|
|
199
|
+
adding to or replacing entries of those dictionaries afterwards does not change
|
|
200
|
+
the book. The values inside them are shared, not copied: a `Style`, a `Group` or
|
|
201
|
+
a `systematics=` mapping given in `plot_kwargs` is the caller's object, and
|
|
202
|
+
changing it changes what the book draws.
|
|
203
|
+
|
|
204
|
+
## File names
|
|
205
|
+
|
|
206
|
+
`book.save(directory, formats=("pdf",), **savefig_kwargs)` creates the directory
|
|
207
|
+
if needed and writes one file per task and format, named
|
|
208
|
+
|
|
209
|
+
```text
|
|
210
|
+
<variable>[__<selection>][__<variant>].<format>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
A component appears whenever its axis was given explicitly, whatever the name:
|
|
214
|
+
|
|
215
|
+
| Book | File |
|
|
216
|
+
| --- | --- |
|
|
217
|
+
| `rf.PlotBook(data, ["mass"])` | `mass.pdf` |
|
|
218
|
+
| `rf.PlotBook(data, ["mass"], selections={"all": cut})` | `mass__all.pdf` |
|
|
219
|
+
| `rf.PlotBook(data, ["mass"], variants={"default": {"logy": True}})` | `mass__default.pdf` |
|
|
220
|
+
| both explicit | `mass__sr__log.pdf` |
|
|
221
|
+
|
|
222
|
+
One format may be given bare, `formats="png"`, a leading dot is accepted and
|
|
223
|
+
duplicates are dropped. A format matplotlib cannot write raises `ValueError`
|
|
224
|
+
before the first figure is drawn, rather than part way through the batch. The
|
|
225
|
+
remaining `savefig_kwargs` go to [`Plot.save`][rootfig.Plot.save]; `format` and
|
|
226
|
+
`fname` are refused, because the file names come from `formats` and the task.
|
|
227
|
+
|
|
228
|
+
Two tasks that would share a name are rejected when the book is built, not
|
|
229
|
+
when the second file overwrites the first. Names are compared ignoring case and
|
|
230
|
+
Unicode normalisation, since `lin` and `LIN` are one file on the case-insensitive
|
|
231
|
+
file systems of macOS and Windows; the message lists the spellings that clash.
|
|
232
|
+
`save()` returns the written paths in task order, then format order, and closes
|
|
233
|
+
every figure after writing it, also when writing fails, so memory stays bounded
|
|
234
|
+
however large the book is.
|
|
235
|
+
|
|
236
|
+
## One multipage PDF
|
|
237
|
+
|
|
238
|
+
`book.save_pdf(path)` writes every task into a single PDF, several plots per
|
|
239
|
+
page, arranged automatically:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
book.save_pdf("overview.pdf")
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The two output modes differ only in where the plots land:
|
|
246
|
+
|
|
247
|
+
```text
|
|
248
|
+
book.save("plots/") -> one file per task
|
|
249
|
+
book.save_pdf("plots.pdf") -> one multipage PDF
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The plots follow `book.tasks()` order and fill each page row by row, and each
|
|
253
|
+
is drawn as `rf.plot` would draw it on a figure of its own: the same panels,
|
|
254
|
+
broken axis, style, labels and legend, inside one cell of the page. Nothing is
|
|
255
|
+
added to a cell; a `title=` in `plot_kwargs` or a variant appears as it would on
|
|
256
|
+
a standalone figure.
|
|
257
|
+
|
|
258
|
+
By default the grid of a page follows the plots it holds. Plots without a lower
|
|
259
|
+
panel or broken axis go up to `2 × 3` on a page; when one of the plots that
|
|
260
|
+
would land on a page has a `ratio=` panel or an `xbreak=`, which need the room,
|
|
261
|
+
the page stops at `2 × 2`. The last page adapts to what is left rather than
|
|
262
|
+
leaving a lone plot in the corner of a full grid: seven plain plots are a
|
|
263
|
+
`2 × 3` page followed by a `1 × 1` one, eight a `2 × 3` page and a `1 × 2` one.
|
|
264
|
+
`layout=(rows, columns)` fixes the grid of every page instead, the last one
|
|
265
|
+
included; its unused cells stay empty:
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
book.save_pdf("overview.pdf", layout=(2, 3))
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
A page is as large as the grid of figures the plots would have on their own, so
|
|
272
|
+
a plot in a cell keeps its usual size: every cell of the document is as large as
|
|
273
|
+
the largest figure any plot of the book would draw alone (a ratio panel makes it
|
|
274
|
+
taller, a `Style` with a `figsize` sets its own). `figsize=` on `save_pdf()`
|
|
275
|
+
sets the size of the whole page instead:
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
book.save_pdf("overview.pdf", figsize=(16, 10))
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Each cell carries its own style, so plots with different fonts, sizes or colours
|
|
282
|
+
sit side by side. The one thing a cell cannot have of its own is the *page*
|
|
283
|
+
background, which belongs to the figure the whole page is drawn on: plots sharing
|
|
284
|
+
a page whose styles ask for different backgrounds are refused before anything is
|
|
285
|
+
drawn, since the labels and legend of a cell are drawn outside its axes and would
|
|
286
|
+
land on the wrong background. Give them pages of their own to keep one document,
|
|
287
|
+
or write a document per style:
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
book.save_pdf("overview.pdf", layout=(1, 1)) # one plot per page
|
|
291
|
+
for name in ("light", "dark"): # or one file per style
|
|
292
|
+
book.select(variants=name).save_pdf(f"overview-{name}.pdf")
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
A `figsize` in `plot_kwargs` or a variant has no meaning inside a shared page and
|
|
296
|
+
is rejected before anything is read; pass it to `save_pdf()`. What a task runs
|
|
297
|
+
with decides: a `figsize` in `plot_kwargs` that every variant overrides with
|
|
298
|
+
`None` leaves no task with one and is accepted.
|
|
299
|
+
|
|
300
|
+
`.pdf` is appended to a path without a suffix (`"overview"` writes
|
|
301
|
+
`overview.pdf`), parent directories are created, another suffix raises
|
|
302
|
+
`ValueError` and an existing directory `IsADirectoryError`. The remaining
|
|
303
|
+
keywords go to `PdfPages.savefig` for every page (`dpi=` for rasterised parts),
|
|
304
|
+
except `metadata=`, the document information dictionary (`{"Title": ...}`), which
|
|
305
|
+
describes the whole PDF and is applied to it; `format`, `fname`, `figure` and
|
|
306
|
+
`backend` are refused. The document is written to a temporary file next to its
|
|
307
|
+
final name and renamed onto it once every page is done, so a failure part way
|
|
308
|
+
leaves an existing file as it was and no half-written PDF behind. Pages are
|
|
309
|
+
written one after another and each page's figure is closed before the next
|
|
310
|
+
begins, so memory stays bounded however many plots the book holds.
|
|
311
|
+
|
|
312
|
+
The histograms are prepared exactly as for `plots()` and `save()`, with the same
|
|
313
|
+
batched reads and the same reuse of one preparation for the variants that only
|
|
314
|
+
change the drawing (see below), so `rf.ALL` composes naturally:
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
rf.PlotBook(
|
|
318
|
+
"analysis.root",
|
|
319
|
+
rf.ALL,
|
|
320
|
+
exclude=["*_cov", "*Index"],
|
|
321
|
+
).save_pdf("overview.pdf")
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
writes an overview of every plottable branch of the file, and
|
|
325
|
+
`book.select(variables=["Muon_pt", "Muon_eta"]).save_pdf("muons.pdf")` one of
|
|
326
|
+
a subset. Errors keep their type and gain the task's note as for `plots()`; a
|
|
327
|
+
failure while writing a page adds one naming the page and the file.
|
|
328
|
+
|
|
329
|
+
## Running plots yourself
|
|
330
|
+
|
|
331
|
+
`book.plots()` is a lazy iterator of `(task, plot)` pairs: one figure per
|
|
332
|
+
step, nothing drawn ahead of time. The plots are ordinary
|
|
333
|
+
[`Plot`][rootfig.Plot] objects, so this is the place to adjust a figure
|
|
334
|
+
or keep it open:
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
for task, p in book.plots():
|
|
338
|
+
print(task.variable.safe_name, task.selection_name, task.variant_name)
|
|
339
|
+
p.ax.axvline(91.2, color="gray", ls="--")
|
|
340
|
+
p.save(f"plots/{task.stem}.pdf")
|
|
341
|
+
p.close()
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
A `PlotTask` carries the `variable`, the `selection` (`Cut` or `None`), the
|
|
345
|
+
`selection_name` and `variant_name` (`None` for an axis the book was built
|
|
346
|
+
without), the merged `kwargs` and the file `stem`. Tasks compare and hash by
|
|
347
|
+
their `stem`, so they work as set members and dictionary keys whatever the
|
|
348
|
+
keyword values hold. Each plot is what
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
rf.plot(book.data, task.variable, selection=task.selection, **task.kwargs)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
returns: the same histograms, binning, systematics, labels and errors.
|
|
355
|
+
|
|
356
|
+
The book only reads less often than that call would. It runs the tasks in
|
|
357
|
+
batches of a few dozen variables: the branches those variables and every
|
|
358
|
+
selection need are read once per sample for the batch, the files a
|
|
359
|
+
`Systematic.samples` variation fills from included, and variants that only
|
|
360
|
+
change the drawing (`logy`, `normalize`, `ratio`, `style`, ...) are drawn from
|
|
361
|
+
one set of prepared histograms, each figure from its own copy. A variant that
|
|
362
|
+
changes how the histograms are prepared (`bins`, `weight`, `observed`,
|
|
363
|
+
`systematics`, ...) is prepared on its own, still from the batch's read.
|
|
364
|
+
Variables that name histograms stored in the files are read with one pass over
|
|
365
|
+
each file per batch. A book with an explicit variable list inspects no input
|
|
366
|
+
when it is built; one built with `rf.ALL` inspects the metadata of its inputs
|
|
367
|
+
then, but never reads event arrays or histogram contents. Neither reads
|
|
368
|
+
anything for `tasks()`; the first batch is read when the first plot is
|
|
369
|
+
requested and the next one when the iteration reaches it.
|
|
370
|
+
|
|
371
|
+
Errors stop the book at the failing task. They keep their type and gain a note
|
|
372
|
+
naming the task, so a traceback for a typo in one expression reads
|
|
373
|
+
|
|
374
|
+
```text
|
|
375
|
+
rootfig.errors.MissingBranchError: unknown name 'recoil_mas' ...
|
|
376
|
+
while running PlotBook task variable='recoil_mas', selection='sr', variant='log'
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
The words `all` and `default` in such messages stand for an axis that was not
|
|
380
|
+
given; they are never used to decide a file name.
|
|
381
|
+
|
|
382
|
+
## Subsets
|
|
383
|
+
|
|
384
|
+
`book.select(variables=..., selections=..., variants=...)` returns a new book
|
|
385
|
+
restricted to the named variables (by `safe_name`), selections and variants
|
|
386
|
+
(by key). Each argument left as `None` keeps its axis, the book's order is kept
|
|
387
|
+
rather than the filter's, and an unknown name raises with the available
|
|
388
|
+
choices:
|
|
389
|
+
|
|
390
|
+
```python
|
|
391
|
+
book.select(variables=["mass"], variants=["log"]).save("plots/debug")
|
|
392
|
+
|
|
393
|
+
book.select(variables=["foo"])
|
|
394
|
+
# ValueError: unknown variable 'foo'; available: ['mass', 'pt']
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
A filter that names nothing, `select(variables=[])`, raises as well, rather than
|
|
398
|
+
quietly producing an empty book.
|
|
399
|
+
|
|
400
|
+
An axis the book was built without has the single choice `"all"` (selections)
|
|
401
|
+
or `"default"` (variants); selecting it is a no-op and the axis stays implicit.
|
|
402
|
+
|
|
403
|
+
## What a book does not do
|
|
404
|
+
|
|
405
|
+
A book runs its tasks one after another in the calling process and keeps the
|
|
406
|
+
arrays of one batch of variables at a time. Its outputs are the individual files
|
|
407
|
+
of `save()` and the compact overview of `save_pdf()`: it has no filename
|
|
408
|
+
template, no parallel execution, and no report generation beyond that grid of
|
|
409
|
+
plots (no captions, tables of contents, headers, HTML or slides). It drives the
|
|
410
|
+
1D `rf.plot` only: for `plot2d`, `efficiency` and `profile` you write the loop
|
|
411
|
+
yourself.
|