rootfig 0.4.0__tar.gz → 0.6.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.4.0 → rootfig-0.6.0}/PKG-INFO +4 -3
- {rootfig-0.4.0 → rootfig-0.6.0}/README.md +3 -2
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/api.md +0 -1
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/composable.md +8 -1
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/ecosystem.md +3 -1
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/plotting.md +121 -6
- {rootfig-0.4.0 → rootfig-0.6.0}/examples/gallery/__init__.py +32 -22
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/__init__.py +1 -3
- rootfig-0.6.0/src/rootfig/_storage.py +166 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/__init__.py +1 -2
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/_common.py +2 -2
- rootfig-0.6.0/src/rootfig/api/_hists.py +137 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/data.py +10 -3
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/measures.py +5 -5
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/plots1d.py +167 -119
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/plots2d.py +90 -11
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/errors.py +16 -1
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/__init__.py +6 -1
- rootfig-0.6.0/src/rootfig/histograms/build.py +549 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/normalize.py +2 -2
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/pipeline.py +60 -32
- rootfig-0.6.0/src/rootfig/histograms/stored.py +425 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/systematics.py +6 -15
- rootfig-0.6.0/src/rootfig/io/objects.py +128 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/io/sources.py +42 -17
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/__init__.py +6 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/binning.py +61 -3
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/samples.py +21 -11
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/style.py +2 -2
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/variables.py +12 -5
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/hist1d.py +24 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/conftest.py +98 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_api.py +330 -30
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_histograms.py +536 -8
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_io.py +112 -1
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_model.py +44 -22
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_plotting.py +18 -4
- rootfig-0.4.0/src/rootfig/histograms/build.py +0 -365
- {rootfig-0.4.0 → rootfig-0.6.0}/.gitignore +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/CONTRIBUTING.md +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/LICENSE +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/expressions.md +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/gallery/index.md +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/hooks/gallery.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/luminosity-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/luminosity.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/many_plots-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/many_plots.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/robust_range-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/robust_range.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/style_colors-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/style_colors.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-alice-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-alice.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-atlas-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-atlas.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-cms-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-cms.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-dune-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-dune.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-lhcb-dark.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-lhcb.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio.png +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/index.md +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/quickstart.md +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/docs/stylesheets/gallery.css +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/examples/gallery/__main__.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/examples/gallery/data.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/examples/gallery/registry.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/mkdocs.yml +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/pyproject.toml +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/_mapping.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/_typing.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/tables.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/expressions/__init__.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/expressions/functions.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/expressions/parser.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/cutflow.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/efficiency.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/ratio.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/stats.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/io/__init__.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/cuts.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/systematics.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/units.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/__init__.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/annotations.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/correlation.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/figure.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/hist2d.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/points.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/ratio.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/result.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/style.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/py.typed +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/selection/__init__.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/selection/columns.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/data/split_collection.root +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_expressions.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_gallery.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_selection.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_tutorials.py +0 -0
- {rootfig-0.4.0 → rootfig-0.6.0}/tests/type_checks/systematics.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: rootfig
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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/
|
|
@@ -197,8 +197,9 @@ stack, building on familiar libraries:
|
|
|
197
197
|
| Histograms | [hist](https://github.com/scikit-hep/hist) / boost-histogram | shared binning, robust automatic ranges, normalisation, ratios |
|
|
198
198
|
| Drawing | [mplhep](https://github.com/scikit-hep/mplhep) + matplotlib | overlays, stacks, ratio panels, labels and legends with good defaults |
|
|
199
199
|
|
|
200
|
-
If
|
|
201
|
-
|
|
200
|
+
If your histograms already exist, `rf.plot` draws them too: name a `TH1`
|
|
201
|
+
stored in the file instead of a branch (`rf.plot("zh_histo.root", "m_recoil")`),
|
|
202
|
+
or pass `hist.Hist` objects directly. If you want the arrays, `rf.load` returns them. See
|
|
202
203
|
[the ecosystem guide](https://jbeirer.github.io/rootfig/ecosystem/) for details.
|
|
203
204
|
|
|
204
205
|
## Development
|
|
@@ -164,8 +164,9 @@ stack, building on familiar libraries:
|
|
|
164
164
|
| Histograms | [hist](https://github.com/scikit-hep/hist) / boost-histogram | shared binning, robust automatic ranges, normalisation, ratios |
|
|
165
165
|
| Drawing | [mplhep](https://github.com/scikit-hep/mplhep) + matplotlib | overlays, stacks, ratio panels, labels and legends with good defaults |
|
|
166
166
|
|
|
167
|
-
If
|
|
168
|
-
|
|
167
|
+
If your histograms already exist, `rf.plot` draws them too: name a `TH1`
|
|
168
|
+
stored in the file instead of a branch (`rf.plot("zh_histo.root", "m_recoil")`),
|
|
169
|
+
or pass `hist.Hist` objects directly. If you want the arrays, `rf.load` returns them. See
|
|
169
170
|
[the ecosystem guide](https://jbeirer.github.io/rootfig/ecosystem/) for details.
|
|
170
171
|
|
|
171
172
|
## Development
|
|
@@ -45,6 +45,9 @@ mem = rf.Sample({"x": awkward_array, "w": weights}, label="in memory")
|
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
Without a luminosity such samples raise a [`LuminosityError`][rootfig.LuminosityError].
|
|
48
|
+
The same samples can point at files of ready-made histograms instead of
|
|
49
|
+
trees; naming the histogram in place of the branch reads it (see
|
|
50
|
+
[stored histograms](plotting.md#histograms-already-in-root-files)).
|
|
48
51
|
- `entry_start`/`entry_stop` restrict reading for quick looks at large files
|
|
49
52
|
(a plot reads every needed branch of every file into memory at once). For a
|
|
50
53
|
ready-made `FileSource`, give the range to the source itself; passing it to
|
|
@@ -56,7 +59,7 @@ mem = rf.Sample({"x": awkward_array, "w": weights}, label="in memory")
|
|
|
56
59
|
([`Systematic.samples`][rootfig.Systematic]). Sources with
|
|
57
60
|
the same name are correlated across samples; see
|
|
58
61
|
[Systematic uncertainties](plotting.md#systematic-uncertainties).
|
|
59
|
-
- `sample.
|
|
62
|
+
- `sample.replace(label="...")` returns a copy with the given fields changed; new values are
|
|
60
63
|
validated like constructor arguments.
|
|
61
64
|
|
|
62
65
|
Passing a list of files to `plot()` creates one sample per file. To merge
|
|
@@ -75,6 +78,10 @@ met = rf.Variable(
|
|
|
75
78
|
|
|
76
79
|
- `bins`: an `int` (range inferred from the data), `(n, low, high)`, a sequence
|
|
77
80
|
of edges (e.g. `rf.log_bins(30, 1, 1000)`), or a `hist.axis.Regular`/`Variable`.
|
|
81
|
+
Applied to a histogram that already exists (stored in a file, or filled
|
|
82
|
+
earlier and passed back with the variable), an `int` merges its bins down to
|
|
83
|
+
that count and explicit edges must coincide with its own; see
|
|
84
|
+
[Histograms that already exist](plotting.md#histograms-that-already-exist).
|
|
78
85
|
- `range`: `(low, high)`, `"robust"` (the default: ignores far outliers such as
|
|
79
86
|
`-999` sentinels and cuts a thin tail, both of which then land in the
|
|
80
87
|
under/overflow) or `"auto"` (the finite min/max over all samples). See
|
|
@@ -31,7 +31,9 @@ fills `hist.Hist` with `Weight` storage (sum of weights and sum of squared
|
|
|
31
31
|
weights per bin), including under/overflow bins, and returns them unchanged:
|
|
32
32
|
`rf.histogram` gives you the `hist.Hist`, `Plot.hists` lists them. Rebinning,
|
|
33
33
|
projecting, slicing, saving to ROOT files with uproot: all of that is hist
|
|
34
|
-
functionality and works directly on the returned objects.
|
|
34
|
+
functionality and works directly on the returned objects. Histograms stored
|
|
35
|
+
in ROOT files (`TH1`, `TH2`) are converted by uproot's `to_hist()`; rootfig
|
|
36
|
+
adds the summing over files, the scaling and the drawing.
|
|
35
37
|
|
|
36
38
|
## mplhep
|
|
37
39
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Plotting options
|
|
2
2
|
|
|
3
|
-
All options below are keyword arguments of [`rf.plot`][rootfig.plot]
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
All options below are keyword arguments of [`rf.plot`][rootfig.plot], whether
|
|
4
|
+
it fills from a tree, reads a histogram stored in the file or draws histogram
|
|
5
|
+
objects you already have (see [the end of this page](#histograms-that-already-exist)).
|
|
6
6
|
|
|
7
7
|
## Overlays, stacks and data
|
|
8
8
|
|
|
@@ -109,11 +109,12 @@ p.ratios[0].syst_band # relative (down, up) band of the reference
|
|
|
109
109
|
|
|
110
110
|
Pre-filled histograms take variations directly and are drawn the same way:
|
|
111
111
|
`rf.Histogram(h, label="MC", variations={"jes": (h_up, h_down)})` passed to
|
|
112
|
-
`
|
|
112
|
+
`plot`; [`uncertainty`][rootfig.histograms.uncertainty] and
|
|
113
113
|
[`sum_histograms`][rootfig.histograms.sum_histograms] work on them too.
|
|
114
114
|
|
|
115
115
|
Use `(h_up, None)` for a mirrored variation. `Sample.systematics` and
|
|
116
|
-
`Histogram.variations` are read-only;
|
|
116
|
+
`Histogram.variations` are read-only; `sample.replace(systematics=...)` and
|
|
117
|
+
`histogram.replace(variations=...)` return copies with other ones.
|
|
117
118
|
|
|
118
119
|
## Luminosity
|
|
119
120
|
|
|
@@ -142,7 +143,7 @@ dataset scaled to the full one, for instance).
|
|
|
142
143
|
Variances are scaled consistently. Flow bins scale with the same factor; for
|
|
143
144
|
`"width"` and `"density"` they are divided by the width of the neighbouring
|
|
144
145
|
visible bin. Plain `hist.Hist` objects with a count storage passed to
|
|
145
|
-
`
|
|
146
|
+
`plot` are converted to `Weight` storage first. If such a histogram
|
|
146
147
|
was filled with weights (or rescaled) its sum of squared weights is lost, and
|
|
147
148
|
rootfig refuses it with a `ValueError` rather than invent uncertainties; pass
|
|
148
149
|
`assume_poisson=True` to use the absolute bin contents as variances (with a
|
|
@@ -450,3 +451,117 @@ table = rf.summarize([sig, bkg], ["MET", "Muon_pt"], selection="nMuon > 0", weig
|
|
|
450
451
|
print(table) # aligned text table
|
|
451
452
|
table.get("MET", "Signal").mean # a Summary: entries, mean, std, sem, skewness, min, max
|
|
452
453
|
```
|
|
454
|
+
|
|
455
|
+
## Histograms that already exist
|
|
456
|
+
|
|
457
|
+
`rf.plot`, `rf.histogram(s)` and `rf.plot2d` read histograms stored in ROOT
|
|
458
|
+
files; `rf.plot` and `rf.plot2d` also draw histogram objects you already have.
|
|
459
|
+
Each keeps its usual drawing options where they apply: the 1D options above
|
|
460
|
+
for `rf.plot` (stacks, ratios, `flow`, ...), those of the
|
|
461
|
+
[2D section](#2d-histograms-and-correlations) for `rf.plot2d`. Options that
|
|
462
|
+
need information a ready-made histogram no longer has (a selection, a weight,
|
|
463
|
+
`stats` on one without statistics) are refused.
|
|
464
|
+
|
|
465
|
+
### Histograms already in ROOT files
|
|
466
|
+
|
|
467
|
+
Analysis frameworks often write their selections out as `TH1`/`TH2` objects,
|
|
468
|
+
one file per process (FCCAnalyses' `<process>_<selection>_histo.root`, for
|
|
469
|
+
instance). Name the histogram where a branch would go:
|
|
470
|
+
|
|
471
|
+
```python
|
|
472
|
+
samples = {
|
|
473
|
+
"ZH": rf.Sample("final/p8_ee_ZH_ecm240_sel1_histo.root", color="C3", scale=10),
|
|
474
|
+
"VV": ["final/p8_ee_WW_ecm240_sel1_histo.root", "final/p8_ee_ZZ_ecm240_sel1_histo.root"],
|
|
475
|
+
}
|
|
476
|
+
rf.plot(
|
|
477
|
+
samples,
|
|
478
|
+
"leptonic_recoil_m",
|
|
479
|
+
bins=50,
|
|
480
|
+
stack=True,
|
|
481
|
+
logy=True,
|
|
482
|
+
xlabel="Z leptonic recoil",
|
|
483
|
+
unit="GeV",
|
|
484
|
+
style=rf.Style(com=240, com_unit="GeV", lumi=5, lumi_unit="ab^{-1}"),
|
|
485
|
+
)
|
|
486
|
+
h = rf.histogram("final/p8_ee_ZH_ecm240_sel0_histo.root", "mz") # a hist.Hist
|
|
487
|
+
rf.plot2d("final/p8_ee_ZH_ecm240_sel0_histo.root", "mz_recoil_2D") # a stored TH2
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
The decision is made per call and is deterministic. A variable that is a bare
|
|
491
|
+
name is read as a stored histogram when every sample reads files without an
|
|
492
|
+
explicit `tree=` or entry range, the first file of each sample holds a `TH1` or
|
|
493
|
+
`TH2` of that name, and the file has no tree or its only tree has no branch of
|
|
494
|
+
that name. Anything else fills from the tree as usual: an explicit
|
|
495
|
+
`tree=` always means a branch, a branch of the same name wins over a histogram,
|
|
496
|
+
a file with several trees next to the histogram raises (pass `tree=` for a
|
|
497
|
+
branch, or read the histogram with `FileSource.read_histogram`), and samples
|
|
498
|
+
that disagree raise with the reason per sample. A histogram inside a directory
|
|
499
|
+
is named by its path in backticks, like a branch with odd characters:
|
|
500
|
+
`` rf.plot("histo.root", "`selection/mz`") ``.
|
|
501
|
+
|
|
502
|
+
What a stored histogram supports:
|
|
503
|
+
|
|
504
|
+
- The files of one `Sample` are summed (they must agree on the binning);
|
|
505
|
+
`scale`, `color`, `is_data`, `histtype` and the luminosity scaling
|
|
506
|
+
(`Sample(xsec=..., ngen=...)` with `lumi=`, `ngen` may name a `TParameter`
|
|
507
|
+
or a sum-of-weights histogram in the same file) apply as for trees. A file
|
|
508
|
+
without a tree has no entries to count, so it needs `ngen`.
|
|
509
|
+
- The stored axis title is the x label unless `xlabel=`/a `Variable` label is
|
|
510
|
+
given; a `unit=` is appended to it and, as for trees, feeds the bin-width y
|
|
511
|
+
label (`Events / 5 GeV`). A title that ends in `[unit]` already supplies it.
|
|
512
|
+
A placeholder title (none, ROOT's `xaxis`, uproot's `Axis 0`) gives way to the
|
|
513
|
+
variable's name. The y axis of a `TH2` named by a single variable gets no
|
|
514
|
+
label from that name, which describes the histogram rather than the axis; as
|
|
515
|
+
for any unlabelled axis, hist shows the axis name (`mz_recoil_2D_y`) instead.
|
|
516
|
+
To label it, pass a `Variable` for `y` that names the same histogram, since
|
|
517
|
+
both axes must resolve to one stored object:
|
|
518
|
+
`` rf.plot2d(f, "my_hist", rf.Variable("my_hist", name="recoil", label="Recoil")) ``.
|
|
519
|
+
A `Variable` with another expression asks for a branch instead.
|
|
520
|
+
- `bins=` merges the stored bins: an integer count (it must divide the stored
|
|
521
|
+
count), or edges that coincide with the stored ones (`(n, low, high)`, a
|
|
522
|
+
sequence of edges, or an `int` with `range=(low, high)`) and merge the bins
|
|
523
|
+
between them. The `Variable` written for the tree therefore also describes
|
|
524
|
+
the histogram filled from it, as its own edges or a coarser aligned set. With
|
|
525
|
+
`bins=None` the stored binning is kept. Other edges cannot be made, and an
|
|
526
|
+
explicit `(low, high)` range without a bin count cannot be applied: use
|
|
527
|
+
`xlim=` to zoom. `range="auto"` and `"robust"` have no effect on a histogram
|
|
528
|
+
that is already filled.
|
|
529
|
+
- Normalisation, stacks, ratios, `flow` and the other drawing options work
|
|
530
|
+
unchanged. Systematics of the normalisation kind (`{"lumi": 0.02}`) and
|
|
531
|
+
`Systematic.samples(other_files)` (the same histogram read from other files)
|
|
532
|
+
are supported by `plot` and `histograms`; `plot2d` ignores systematics, for
|
|
533
|
+
stored histograms as for trees.
|
|
534
|
+
- `selection=`, `weight=` (on the call or the `Sample`), `nonfinite="error"`,
|
|
535
|
+
an explicit `(low, high)` range without a bin count, weight and
|
|
536
|
+
branch-replacement systematics and `stats=` need event data and raise with a
|
|
537
|
+
message that says so.
|
|
538
|
+
- A `TH1` written with `Sumw2` keeps its uncertainties; one without it arrives
|
|
539
|
+
with its bin contents as variances (uproot cannot know the weights). Negative
|
|
540
|
+
contents without `Sumw2` leave no usable variances: rootfig refuses them unless
|
|
541
|
+
`assume_poisson=True` takes the absolute contents, as for histogram objects.
|
|
542
|
+
The files of one sample may mix both kinds and may differ in their axis
|
|
543
|
+
titles; the sum has `Weight` storage and the first file's titles.
|
|
544
|
+
|
|
545
|
+
### Histogram objects
|
|
546
|
+
|
|
547
|
+
`hist.Hist` or [`Histogram`][rootfig.Histogram] objects, one or a list, are
|
|
548
|
+
drawn as they are: `rf.plot([h_sig, h_bkg], label=["Signal", "Background"],
|
|
549
|
+
ratio=True)`. `label=` names plain `hist.Hist` objects (otherwise their first
|
|
550
|
+
axis name is used), `observed=` takes histogram objects for the data,
|
|
551
|
+
`variable=` optionally supplies the axis label, unit and `log` flag, and
|
|
552
|
+
`rf.plot2d(h2)` draws a 2D one, with `x` and `y` `Variable`s optionally
|
|
553
|
+
describing its axes the same way (`rf.plot2d(h2, rf.Variable("mass",
|
|
554
|
+
label="Mass", unit="GeV"), rf.Variable("recoil", bins=6))`; a `name=` renames
|
|
555
|
+
the axis, the histogram you passed is left untouched). As for stored histograms, `bins=` (given
|
|
556
|
+
directly or on the `Variable`) merges bins: an integer count, or edges that
|
|
557
|
+
coincide with the existing ones, so the `Variable` a histogram was filled with
|
|
558
|
+
can be passed along with it (`rf.plot(rf.histogram(sample, pt), pt)`), also
|
|
559
|
+
after normalising it, as long as it asks for the bins the histogram has. An
|
|
560
|
+
explicit `(low, high)` range without a bin count and the options that fill
|
|
561
|
+
from event data (`tree`, `selection`, `weight`, `lumi`, `systematics`) raise;
|
|
562
|
+
`range="auto"`/`"robust"` are no-ops. `Histogram.variations` carries
|
|
563
|
+
systematics instead. Stacks, sums and ratios of histograms with category axes
|
|
564
|
+
(ROOT bin labels) require the same categories in the same order; the flow bins
|
|
565
|
+
of such an axis hold entries of categories it does not list, which
|
|
566
|
+
`flow="hint"` marks with an arrow and `flow="show"`/`flow="sum"` refuse, since
|
|
567
|
+
they have no bin beyond the last category.
|
|
@@ -231,7 +231,7 @@ def stack_data(mc: list[rf.Sample], data: rf.Sample, pt: rf.Variable, style: rf.
|
|
|
231
231
|
"Systematic uncertainties in a stack and its ratio panel",
|
|
232
232
|
section=SIMULATION_AND_DATA,
|
|
233
233
|
)
|
|
234
|
-
def systematics(
|
|
234
|
+
def systematics(signal: rf.Sample, data: rf.Sample, met: rf.Variable, style: rf.Style) -> rf.Plot:
|
|
235
235
|
"""A sample lists its sources of systematic uncertainty by name: a pair of weight
|
|
236
236
|
expressions, a relative normalisation uncertainty, or a mapping of shifted branches,
|
|
237
237
|
which also move the selection. ``systematics=`` on the plot adds a
|
|
@@ -239,15 +239,24 @@ def systematics(mc: list[rf.Sample], data: rf.Sample, met: rf.Variable, style: r
|
|
|
239
239
|
statistical and systematic uncertainties; a source with the same name in several
|
|
240
240
|
samples is correlated, different sources add in quadrature. ``p.uncertainty()``
|
|
241
241
|
returns every component."""
|
|
242
|
-
zjets, diboson, signal = mc
|
|
243
242
|
jes = {"MET": ("MET_jesUp", "MET_jesDown")}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
243
|
+
zjets = rf.Sample(
|
|
244
|
+
"background.root",
|
|
245
|
+
tree="events",
|
|
246
|
+
label="Z + jets",
|
|
247
|
+
weight="weight",
|
|
248
|
+
systematics={"pileup": ("weight_pu_up", "weight_pu_down"), "jes": jes},
|
|
249
|
+
)
|
|
250
|
+
diboson = rf.Sample(
|
|
251
|
+
"diboson.root",
|
|
252
|
+
tree="events",
|
|
253
|
+
label="Diboson",
|
|
254
|
+
weight="weight",
|
|
255
|
+
scale=0.15,
|
|
256
|
+
systematics={"jes": jes, "xsec": 0.10},
|
|
257
|
+
)
|
|
249
258
|
return rf.plot(
|
|
250
|
-
|
|
259
|
+
[zjets, diboson, signal],
|
|
251
260
|
met,
|
|
252
261
|
observed=data,
|
|
253
262
|
stack=True,
|
|
@@ -272,9 +281,9 @@ def ratio_reference(
|
|
|
272
281
|
stays visible on light and dark pages."""
|
|
273
282
|
return rf.plot(
|
|
274
283
|
[
|
|
275
|
-
zjets.
|
|
276
|
-
diboson.
|
|
277
|
-
signal.
|
|
284
|
+
zjets.replace(color=plt.rcParams["text.color"], histtype="errorbar"),
|
|
285
|
+
diboson.replace(color="#d95f02"),
|
|
286
|
+
signal.replace(color="#1b9e77", histtype="fill"),
|
|
278
287
|
],
|
|
279
288
|
rf.Variable("nJet", bins=(9, -0.5, 8.5), label="Jet multiplicity"),
|
|
280
289
|
normalize=True,
|
|
@@ -413,12 +422,12 @@ def density_flow(
|
|
|
413
422
|
def object_vs_event(signal: rf.Sample, pt: rf.Variable, style: rf.Style) -> rf.Plot:
|
|
414
423
|
"""``Muon_pt`` is a list per event. A per-object cut such as ``Muon_pt > 100`` masks
|
|
415
424
|
individual muons, while ``any(Muon_pt > 100)`` is per event: it keeps whole events, with all
|
|
416
|
-
their muons, soft ones included. ``Sample.
|
|
425
|
+
their muons, soft ones included. ``Sample.replace`` derives variants of a sample."""
|
|
417
426
|
return rf.plot(
|
|
418
427
|
[
|
|
419
|
-
signal.
|
|
420
|
-
signal.
|
|
421
|
-
signal.
|
|
428
|
+
signal.replace(label="All muons"),
|
|
429
|
+
signal.replace(label="Muon_pt > 100", selection="Muon_pt > 100"),
|
|
430
|
+
signal.replace(label="any(Muon_pt > 100)", selection="any(Muon_pt > 100)"),
|
|
422
431
|
],
|
|
423
432
|
pt,
|
|
424
433
|
logy=True,
|
|
@@ -430,15 +439,16 @@ def object_vs_event(signal: rf.Sample, pt: rf.Variable, style: rf.Style) -> rf.P
|
|
|
430
439
|
def expressions(signal: rf.Sample, pt: rf.Variable, style: rf.Style) -> rf.Plot:
|
|
431
440
|
"""Variables are expressions with NumPy functions and per-event reductions:
|
|
432
441
|
``first(Muon_pt)`` is the leading muon, ``Muon_pt * cosh(Muon_eta)`` the muon momentum.
|
|
433
|
-
``rf.histogram`` returns a plain ``hist.Hist``; ``rf.
|
|
434
|
-
|
|
442
|
+
``rf.histogram`` returns a plain ``hist.Hist``; ``rf.plot`` draws any collection of
|
|
443
|
+
histogram objects with the usual options."""
|
|
435
444
|
all_muons = rf.histogram(signal, pt)
|
|
436
|
-
leading = rf.histogram(signal, pt.
|
|
437
|
-
momentum = rf.histogram(signal, pt.
|
|
438
|
-
return rf.
|
|
445
|
+
leading = rf.histogram(signal, pt.replace(expression="first(Muon_pt)"))
|
|
446
|
+
momentum = rf.histogram(signal, pt.replace(expression="Muon_pt * cosh(Muon_eta)"))
|
|
447
|
+
return rf.plot(
|
|
439
448
|
[all_muons, leading, momentum],
|
|
440
|
-
|
|
441
|
-
|
|
449
|
+
label=["All muons", "Leading muon", r"Muon $|\vec{p}|$"],
|
|
450
|
+
xlabel=r"$p_T^{\mu}$ or $|\vec{p}^{\,\mu}|$",
|
|
451
|
+
unit="GeV",
|
|
442
452
|
logy=True,
|
|
443
453
|
style=style,
|
|
444
454
|
)
|
|
@@ -22,7 +22,6 @@ from rootfig.api import (
|
|
|
22
22
|
load,
|
|
23
23
|
plot,
|
|
24
24
|
plot2d,
|
|
25
|
-
plot_histograms,
|
|
26
25
|
profile,
|
|
27
26
|
summarize,
|
|
28
27
|
)
|
|
@@ -54,7 +53,7 @@ from rootfig.histograms import (
|
|
|
54
53
|
from rootfig.model import Cut, Sample, Style, Systematic, Variable, log_bins
|
|
55
54
|
from rootfig.plotting import Plot, dark_theme, use_style
|
|
56
55
|
|
|
57
|
-
__version__ = "0.
|
|
56
|
+
__version__ = "0.6.0"
|
|
58
57
|
|
|
59
58
|
__all__ = [
|
|
60
59
|
"BinningError",
|
|
@@ -94,7 +93,6 @@ __all__ = [
|
|
|
94
93
|
"log_bins",
|
|
95
94
|
"plot",
|
|
96
95
|
"plot2d",
|
|
97
|
-
"plot_histograms",
|
|
98
96
|
"profile",
|
|
99
97
|
"ratio",
|
|
100
98
|
"significance",
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""``Weight``-storage conversion, binning comparison and bin-wise addition of ``hist`` objects.
|
|
2
|
+
|
|
3
|
+
Shared by :mod:`rootfig.io` (histograms read from files) and
|
|
4
|
+
:mod:`rootfig.histograms`, so both apply one policy for uncertainties and for
|
|
5
|
+
what counts as the same binning.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import warnings
|
|
11
|
+
from collections.abc import Iterable
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
import hist
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
from rootfig._typing import Hist
|
|
18
|
+
from rootfig.errors import RootfigWarning
|
|
19
|
+
|
|
20
|
+
__all__ = ["add_hists", "add_into", "as_weight_storage", "is_category", "same_axis", "same_binning"]
|
|
21
|
+
|
|
22
|
+
_COUNT_STORAGES = (
|
|
23
|
+
hist.storage.Double,
|
|
24
|
+
hist.storage.Int64,
|
|
25
|
+
hist.storage.AtomicInt64,
|
|
26
|
+
hist.storage.Unlimited,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def as_weight_storage(histogram: Hist, *, assume_poisson: bool = False) -> Hist:
|
|
31
|
+
"""Return ``histogram`` with ``Weight`` storage (a copy if it had another storage).
|
|
32
|
+
|
|
33
|
+
Plain count storages (``Double``, ``Int64``, ...) carry no sum of squared
|
|
34
|
+
weights; their variances are taken as ``hist`` reports them, i.e. the
|
|
35
|
+
counts (Poisson) for unweighted fills. Two cases leave no usable variances:
|
|
36
|
+
after a weighted fill or arithmetic on such a storage ``hist`` reports none
|
|
37
|
+
at all, and a count storage with negative contents reports negative ones
|
|
38
|
+
(the counts). Either is an error unless ``assume_poisson=True``, which uses
|
|
39
|
+
the absolute bin contents as variances (the Poisson guess; a
|
|
40
|
+
:class:`~rootfig.errors.RootfigWarning` says so). A ``Weight`` storage is
|
|
41
|
+
returned as it is unless it reports negative variances, which get the same
|
|
42
|
+
treatment.
|
|
43
|
+
|
|
44
|
+
Raises
|
|
45
|
+
------
|
|
46
|
+
TypeError
|
|
47
|
+
If the storage is not a count or ``Weight`` storage (``Mean``, ...).
|
|
48
|
+
ValueError
|
|
49
|
+
If the histogram has no usable variances and ``assume_poisson`` is False.
|
|
50
|
+
"""
|
|
51
|
+
if histogram.storage_type is hist.storage.Weight:
|
|
52
|
+
weight_variances = np.asarray(histogram.variances(flow=True), dtype=float)
|
|
53
|
+
if not np.any(weight_variances < 0):
|
|
54
|
+
return histogram
|
|
55
|
+
what = "histogram with Weight storage reports negative variances"
|
|
56
|
+
reported: Any = None
|
|
57
|
+
else:
|
|
58
|
+
if histogram.ndim and histogram.storage_type not in _COUNT_STORAGES:
|
|
59
|
+
msg = (
|
|
60
|
+
f"histograms with {histogram.storage_type.__name__} storage are not supported; "
|
|
61
|
+
"use Weight (or a plain count) storage"
|
|
62
|
+
)
|
|
63
|
+
raise TypeError(msg)
|
|
64
|
+
reported = histogram.variances(flow=True)
|
|
65
|
+
if reported is None:
|
|
66
|
+
what = (
|
|
67
|
+
f"histogram with {histogram.storage_type.__name__} storage was filled with "
|
|
68
|
+
"weights or rescaled, so hist reports no variances (the sum of squared weights "
|
|
69
|
+
"is lost)"
|
|
70
|
+
)
|
|
71
|
+
elif np.any(np.asarray(reported) < 0):
|
|
72
|
+
what = (
|
|
73
|
+
f"histogram with {histogram.storage_type.__name__} storage has negative bin "
|
|
74
|
+
"contents, so the counts it reports as variances are negative (the sum of "
|
|
75
|
+
"squared weights is unknown)"
|
|
76
|
+
)
|
|
77
|
+
reported = None
|
|
78
|
+
values = np.asarray(histogram.values(flow=True), dtype=float)
|
|
79
|
+
if reported is None:
|
|
80
|
+
if not assume_poisson:
|
|
81
|
+
msg = (
|
|
82
|
+
f"{what}. Fill it with hist.storage.Weight() to keep the uncertainties, or pass "
|
|
83
|
+
"assume_poisson=True to use the absolute bin contents as variances"
|
|
84
|
+
)
|
|
85
|
+
raise ValueError(msg)
|
|
86
|
+
warnings.warn(
|
|
87
|
+
f"{what}; using the absolute bin contents as variances (Poisson guess)",
|
|
88
|
+
RootfigWarning,
|
|
89
|
+
stacklevel=3,
|
|
90
|
+
)
|
|
91
|
+
variances = np.abs(values) # never a negative variance for signed contents
|
|
92
|
+
else:
|
|
93
|
+
variances = np.asarray(reported, dtype=float)
|
|
94
|
+
result = hist.Hist(*histogram.axes, storage=hist.storage.Weight())
|
|
95
|
+
view: Any = result.view(flow=True)
|
|
96
|
+
view.value = values
|
|
97
|
+
view.variance = variances
|
|
98
|
+
return result
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def same_binning(a: Hist, b: Hist, *, flow: bool = True) -> bool:
|
|
102
|
+
"""Return True if two histograms have the same axes up to their names and labels.
|
|
103
|
+
|
|
104
|
+
Axis by axis as :func:`same_axis`; ``flow`` asks for the same flow bins too.
|
|
105
|
+
"""
|
|
106
|
+
if a.ndim != b.ndim:
|
|
107
|
+
return False
|
|
108
|
+
return all(
|
|
109
|
+
same_axis(axis_a, axis_b, flow=flow) for axis_a, axis_b in zip(a.axes, b.axes, strict=True)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def same_axis(axis_a: Any, axis_b: Any, *, flow: bool = True) -> bool:
|
|
114
|
+
"""Return True if two axes bin the same way, whatever their names and labels.
|
|
115
|
+
|
|
116
|
+
Two category axes agree when they list the same categories in the same
|
|
117
|
+
order; two numeric axes (``Regular``, ``Variable`` and ``Integer``
|
|
118
|
+
interchangeably) when their edges agree to a millionth of the smallest bin
|
|
119
|
+
width, so bins shifted by a whole width at large coordinates are rejected.
|
|
120
|
+
A category and a numeric axis never agree. With ``flow`` the under- and
|
|
121
|
+
overflow bins must be present or absent alike.
|
|
122
|
+
"""
|
|
123
|
+
if flow and (axis_a.traits.underflow, axis_a.traits.overflow) != (
|
|
124
|
+
axis_b.traits.underflow,
|
|
125
|
+
axis_b.traits.overflow,
|
|
126
|
+
):
|
|
127
|
+
return False
|
|
128
|
+
if is_category(axis_a) or is_category(axis_b):
|
|
129
|
+
both = is_category(axis_a) and is_category(axis_b)
|
|
130
|
+
return both and list(axis_a) == list(axis_b)
|
|
131
|
+
edges_a, edges_b = np.asarray(axis_a.edges, dtype=float), np.asarray(axis_b.edges, dtype=float)
|
|
132
|
+
if edges_a.shape != edges_b.shape:
|
|
133
|
+
return False
|
|
134
|
+
tolerance = 1e-6 * float(min(np.diff(edges_a).min(), np.diff(edges_b).min()))
|
|
135
|
+
return bool(np.allclose(edges_a, edges_b, rtol=0.0, atol=tolerance))
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def is_category(axis: Any) -> bool:
|
|
139
|
+
"""Return True for a category axis (``StrCategory``/``IntCategory``: labelled bins)."""
|
|
140
|
+
return isinstance(axis, hist.axis.StrCategory | hist.axis.IntCategory)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def add_into(total: Hist, other: Hist) -> None:
|
|
144
|
+
"""Add ``other`` into ``total`` in place, bin by bin, flow bins included.
|
|
145
|
+
|
|
146
|
+
Both have ``Weight`` storage and the same binning (:func:`same_binning`);
|
|
147
|
+
``total`` keeps its axes (names and labels), which is what ``hist``'s own
|
|
148
|
+
addition refuses when the metadata differ.
|
|
149
|
+
"""
|
|
150
|
+
view: Any = total.view(flow=True)
|
|
151
|
+
view.value += other.values(flow=True)
|
|
152
|
+
view.variance += other.variances(flow=True)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def add_hists(hists: Iterable[Hist]) -> Hist:
|
|
156
|
+
"""Return the bin-wise sum of ``Weight``-storage histograms with the same binning.
|
|
157
|
+
|
|
158
|
+
The first histogram is copied and the others added into the copy one at a
|
|
159
|
+
time (see :func:`add_into`), so an iterator of histograms is summed without
|
|
160
|
+
holding them all.
|
|
161
|
+
"""
|
|
162
|
+
iterator = iter(hists)
|
|
163
|
+
total = next(iterator).copy()
|
|
164
|
+
for other in iterator:
|
|
165
|
+
add_into(total, other)
|
|
166
|
+
return total
|
|
@@ -8,7 +8,7 @@ their own.
|
|
|
8
8
|
|
|
9
9
|
from rootfig.api.data import histogram, histograms, load
|
|
10
10
|
from rootfig.api.measures import efficiency, profile
|
|
11
|
-
from rootfig.api.plots1d import plot
|
|
11
|
+
from rootfig.api.plots1d import plot
|
|
12
12
|
from rootfig.api.plots2d import correlation, plot2d
|
|
13
13
|
from rootfig.api.tables import SummaryTable, cutflow, summarize
|
|
14
14
|
|
|
@@ -22,7 +22,6 @@ __all__ = [
|
|
|
22
22
|
"load",
|
|
23
23
|
"plot",
|
|
24
24
|
"plot2d",
|
|
25
|
-
"plot_histograms",
|
|
26
25
|
"profile",
|
|
27
26
|
"summarize",
|
|
28
27
|
]
|
|
@@ -40,12 +40,12 @@ def style_for(
|
|
|
40
40
|
"""Resolve ``style`` and add free text lines and the luminosity used for scaling."""
|
|
41
41
|
resolved = as_style(style)
|
|
42
42
|
if lumi is not None and resolved.lumi is None:
|
|
43
|
-
resolved = resolved.
|
|
43
|
+
resolved = resolved.replace(lumi=lumi)
|
|
44
44
|
if text is None:
|
|
45
45
|
return resolved
|
|
46
46
|
existing = list(resolved.text_lines)
|
|
47
47
|
extra = [text] if isinstance(text, str) else list(text)
|
|
48
|
-
return resolved.
|
|
48
|
+
return resolved.replace(text=[*existing, *extra])
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
def normalize_for_plot(histogram_: Histogram, spec: NormalizeSpec) -> Histogram:
|