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.
Files changed (296) hide show
  1. {rootfig-0.4.0 → rootfig-0.6.0}/PKG-INFO +4 -3
  2. {rootfig-0.4.0 → rootfig-0.6.0}/README.md +3 -2
  3. {rootfig-0.4.0 → rootfig-0.6.0}/docs/api.md +0 -1
  4. {rootfig-0.4.0 → rootfig-0.6.0}/docs/composable.md +8 -1
  5. {rootfig-0.4.0 → rootfig-0.6.0}/docs/ecosystem.md +3 -1
  6. {rootfig-0.4.0 → rootfig-0.6.0}/docs/plotting.md +121 -6
  7. {rootfig-0.4.0 → rootfig-0.6.0}/examples/gallery/__init__.py +32 -22
  8. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/__init__.py +1 -3
  9. rootfig-0.6.0/src/rootfig/_storage.py +166 -0
  10. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/__init__.py +1 -2
  11. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/_common.py +2 -2
  12. rootfig-0.6.0/src/rootfig/api/_hists.py +137 -0
  13. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/data.py +10 -3
  14. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/measures.py +5 -5
  15. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/plots1d.py +167 -119
  16. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/plots2d.py +90 -11
  17. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/errors.py +16 -1
  18. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/__init__.py +6 -1
  19. rootfig-0.6.0/src/rootfig/histograms/build.py +549 -0
  20. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/normalize.py +2 -2
  21. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/pipeline.py +60 -32
  22. rootfig-0.6.0/src/rootfig/histograms/stored.py +425 -0
  23. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/systematics.py +6 -15
  24. rootfig-0.6.0/src/rootfig/io/objects.py +128 -0
  25. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/io/sources.py +42 -17
  26. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/__init__.py +6 -0
  27. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/binning.py +61 -3
  28. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/samples.py +21 -11
  29. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/style.py +2 -2
  30. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/variables.py +12 -5
  31. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/hist1d.py +24 -0
  32. {rootfig-0.4.0 → rootfig-0.6.0}/tests/conftest.py +98 -0
  33. {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_api.py +330 -30
  34. {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_histograms.py +536 -8
  35. {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_io.py +112 -1
  36. {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_model.py +44 -22
  37. {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_plotting.py +18 -4
  38. rootfig-0.4.0/src/rootfig/histograms/build.py +0 -365
  39. {rootfig-0.4.0 → rootfig-0.6.0}/.gitignore +0 -0
  40. {rootfig-0.4.0 → rootfig-0.6.0}/CONTRIBUTING.md +0 -0
  41. {rootfig-0.4.0 → rootfig-0.6.0}/LICENSE +0 -0
  42. {rootfig-0.4.0 → rootfig-0.6.0}/docs/expressions.md +0 -0
  43. {rootfig-0.4.0 → rootfig-0.6.0}/docs/gallery/index.md +0 -0
  44. {rootfig-0.4.0 → rootfig-0.6.0}/docs/hooks/gallery.py +0 -0
  45. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-alice-dark.png +0 -0
  46. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-alice.png +0 -0
  47. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-atlas-dark.png +0 -0
  48. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-atlas.png +0 -0
  49. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-cms-dark.png +0 -0
  50. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-cms.png +0 -0
  51. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-dark.png +0 -0
  52. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-dune-dark.png +0 -0
  53. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-dune.png +0 -0
  54. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-lhcb-dark.png +0 -0
  55. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays-lhcb.png +0 -0
  56. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/arrays.png +0 -0
  57. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-alice-dark.png +0 -0
  58. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-alice.png +0 -0
  59. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-atlas-dark.png +0 -0
  60. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-atlas.png +0 -0
  61. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-cms-dark.png +0 -0
  62. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-cms.png +0 -0
  63. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-dark.png +0 -0
  64. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-dune-dark.png +0 -0
  65. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-dune.png +0 -0
  66. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-lhcb-dark.png +0 -0
  67. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation-lhcb.png +0 -0
  68. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/correlation.png +0 -0
  69. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-alice-dark.png +0 -0
  70. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-alice.png +0 -0
  71. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-atlas-dark.png +0 -0
  72. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-atlas.png +0 -0
  73. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-cms-dark.png +0 -0
  74. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-cms.png +0 -0
  75. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-dark.png +0 -0
  76. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-dune-dark.png +0 -0
  77. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-dune.png +0 -0
  78. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-lhcb-dark.png +0 -0
  79. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow-lhcb.png +0 -0
  80. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/density_flow.png +0 -0
  81. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-alice-dark.png +0 -0
  82. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-alice.png +0 -0
  83. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-atlas-dark.png +0 -0
  84. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-atlas.png +0 -0
  85. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-cms-dark.png +0 -0
  86. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-cms.png +0 -0
  87. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-dark.png +0 -0
  88. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-dune-dark.png +0 -0
  89. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-dune.png +0 -0
  90. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-lhcb-dark.png +0 -0
  91. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency-lhcb.png +0 -0
  92. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/efficiency.png +0 -0
  93. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-alice-dark.png +0 -0
  94. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-alice.png +0 -0
  95. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-atlas-dark.png +0 -0
  96. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-atlas.png +0 -0
  97. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-cms-dark.png +0 -0
  98. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-cms.png +0 -0
  99. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-dark.png +0 -0
  100. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-dune-dark.png +0 -0
  101. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-dune.png +0 -0
  102. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-lhcb-dark.png +0 -0
  103. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions-lhcb.png +0 -0
  104. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/expressions.png +0 -0
  105. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-alice-dark.png +0 -0
  106. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-alice.png +0 -0
  107. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-atlas-dark.png +0 -0
  108. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-atlas.png +0 -0
  109. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-cms-dark.png +0 -0
  110. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-cms.png +0 -0
  111. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-dark.png +0 -0
  112. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-dune-dark.png +0 -0
  113. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-dune.png +0 -0
  114. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-lhcb-dark.png +0 -0
  115. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats-lhcb.png +0 -0
  116. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/fill_stats.png +0 -0
  117. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-alice-dark.png +0 -0
  118. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-alice.png +0 -0
  119. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-atlas-dark.png +0 -0
  120. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-atlas.png +0 -0
  121. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-cms-dark.png +0 -0
  122. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-cms.png +0 -0
  123. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-dark.png +0 -0
  124. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-dune-dark.png +0 -0
  125. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-dune.png +0 -0
  126. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-lhcb-dark.png +0 -0
  127. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d-lhcb.png +0 -0
  128. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/hist2d.png +0 -0
  129. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-alice-dark.png +0 -0
  130. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-alice.png +0 -0
  131. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-atlas-dark.png +0 -0
  132. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-atlas.png +0 -0
  133. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-cms-dark.png +0 -0
  134. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-cms.png +0 -0
  135. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-dark.png +0 -0
  136. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-dune-dark.png +0 -0
  137. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-dune.png +0 -0
  138. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-lhcb-dark.png +0 -0
  139. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes-lhcb.png +0 -0
  140. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/log_axes.png +0 -0
  141. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/luminosity-dark.png +0 -0
  142. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/luminosity.png +0 -0
  143. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/many_plots-dark.png +0 -0
  144. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/many_plots.png +0 -0
  145. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-alice-dark.png +0 -0
  146. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-alice.png +0 -0
  147. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-atlas-dark.png +0 -0
  148. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-atlas.png +0 -0
  149. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-cms-dark.png +0 -0
  150. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-cms.png +0 -0
  151. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-dark.png +0 -0
  152. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-dune-dark.png +0 -0
  153. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-dune.png +0 -0
  154. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-lhcb-dark.png +0 -0
  155. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event-lhcb.png +0 -0
  156. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/object_vs_event.png +0 -0
  157. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-alice-dark.png +0 -0
  158. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-alice.png +0 -0
  159. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-atlas-dark.png +0 -0
  160. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-atlas.png +0 -0
  161. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-cms-dark.png +0 -0
  162. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-cms.png +0 -0
  163. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-dark.png +0 -0
  164. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-dune-dark.png +0 -0
  165. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-dune.png +0 -0
  166. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-lhcb-dark.png +0 -0
  167. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio-lhcb.png +0 -0
  168. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/overlay_ratio.png +0 -0
  169. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-alice-dark.png +0 -0
  170. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-alice.png +0 -0
  171. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-atlas-dark.png +0 -0
  172. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-atlas.png +0 -0
  173. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-cms-dark.png +0 -0
  174. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-cms.png +0 -0
  175. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-dark.png +0 -0
  176. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-dune-dark.png +0 -0
  177. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-dune.png +0 -0
  178. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-lhcb-dark.png +0 -0
  179. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile-lhcb.png +0 -0
  180. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/profile.png +0 -0
  181. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-alice-dark.png +0 -0
  182. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-alice.png +0 -0
  183. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-atlas-dark.png +0 -0
  184. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-atlas.png +0 -0
  185. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-cms-dark.png +0 -0
  186. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-cms.png +0 -0
  187. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-dark.png +0 -0
  188. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-dune-dark.png +0 -0
  189. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-dune.png +0 -0
  190. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-lhcb-dark.png +0 -0
  191. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick-lhcb.png +0 -0
  192. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/quick.png +0 -0
  193. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-alice-dark.png +0 -0
  194. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-alice.png +0 -0
  195. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-atlas-dark.png +0 -0
  196. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-atlas.png +0 -0
  197. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-cms-dark.png +0 -0
  198. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-cms.png +0 -0
  199. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-dark.png +0 -0
  200. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-dune-dark.png +0 -0
  201. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-dune.png +0 -0
  202. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-lhcb-dark.png +0 -0
  203. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference-lhcb.png +0 -0
  204. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/ratio_reference.png +0 -0
  205. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/robust_range-dark.png +0 -0
  206. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/robust_range.png +0 -0
  207. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-alice-dark.png +0 -0
  208. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-alice.png +0 -0
  209. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-atlas-dark.png +0 -0
  210. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-atlas.png +0 -0
  211. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-cms-dark.png +0 -0
  212. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-cms.png +0 -0
  213. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-dark.png +0 -0
  214. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-dune-dark.png +0 -0
  215. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-dune.png +0 -0
  216. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-lhcb-dark.png +0 -0
  217. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data-lhcb.png +0 -0
  218. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/stack_data.png +0 -0
  219. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/style_colors-dark.png +0 -0
  220. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/style_colors.png +0 -0
  221. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-alice-dark.png +0 -0
  222. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-alice.png +0 -0
  223. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-atlas-dark.png +0 -0
  224. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-atlas.png +0 -0
  225. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-cms-dark.png +0 -0
  226. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-cms.png +0 -0
  227. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-dark.png +0 -0
  228. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-dune-dark.png +0 -0
  229. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-dune.png +0 -0
  230. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-lhcb-dark.png +0 -0
  231. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics-lhcb.png +0 -0
  232. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/systematics.png +0 -0
  233. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-alice-dark.png +0 -0
  234. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-alice.png +0 -0
  235. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-atlas-dark.png +0 -0
  236. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-atlas.png +0 -0
  237. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-cms-dark.png +0 -0
  238. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-cms.png +0 -0
  239. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-dark.png +0 -0
  240. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-dune-dark.png +0 -0
  241. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-dune.png +0 -0
  242. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-lhcb-dark.png +0 -0
  243. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins-lhcb.png +0 -0
  244. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/variable_bins.png +0 -0
  245. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-alice-dark.png +0 -0
  246. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-alice.png +0 -0
  247. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-atlas-dark.png +0 -0
  248. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-atlas.png +0 -0
  249. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-cms-dark.png +0 -0
  250. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-cms.png +0 -0
  251. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-dark.png +0 -0
  252. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-dune-dark.png +0 -0
  253. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-dune.png +0 -0
  254. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-lhcb-dark.png +0 -0
  255. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio-lhcb.png +0 -0
  256. {rootfig-0.4.0 → rootfig-0.6.0}/docs/images/gallery/xbreak_ratio.png +0 -0
  257. {rootfig-0.4.0 → rootfig-0.6.0}/docs/index.md +0 -0
  258. {rootfig-0.4.0 → rootfig-0.6.0}/docs/quickstart.md +0 -0
  259. {rootfig-0.4.0 → rootfig-0.6.0}/docs/stylesheets/gallery.css +0 -0
  260. {rootfig-0.4.0 → rootfig-0.6.0}/examples/gallery/__main__.py +0 -0
  261. {rootfig-0.4.0 → rootfig-0.6.0}/examples/gallery/data.py +0 -0
  262. {rootfig-0.4.0 → rootfig-0.6.0}/examples/gallery/registry.py +0 -0
  263. {rootfig-0.4.0 → rootfig-0.6.0}/mkdocs.yml +0 -0
  264. {rootfig-0.4.0 → rootfig-0.6.0}/pyproject.toml +0 -0
  265. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/_mapping.py +0 -0
  266. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/_typing.py +0 -0
  267. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/api/tables.py +0 -0
  268. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/expressions/__init__.py +0 -0
  269. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/expressions/functions.py +0 -0
  270. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/expressions/parser.py +0 -0
  271. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/cutflow.py +0 -0
  272. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/efficiency.py +0 -0
  273. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/ratio.py +0 -0
  274. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/histograms/stats.py +0 -0
  275. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/io/__init__.py +0 -0
  276. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/cuts.py +0 -0
  277. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/systematics.py +0 -0
  278. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/model/units.py +0 -0
  279. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/__init__.py +0 -0
  280. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/annotations.py +0 -0
  281. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/correlation.py +0 -0
  282. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/figure.py +0 -0
  283. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/hist2d.py +0 -0
  284. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/points.py +0 -0
  285. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/ratio.py +0 -0
  286. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/result.py +0 -0
  287. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/plotting/style.py +0 -0
  288. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/py.typed +0 -0
  289. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/selection/__init__.py +0 -0
  290. {rootfig-0.4.0 → rootfig-0.6.0}/src/rootfig/selection/columns.py +0 -0
  291. {rootfig-0.4.0 → rootfig-0.6.0}/tests/data/split_collection.root +0 -0
  292. {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_expressions.py +0 -0
  293. {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_gallery.py +0 -0
  294. {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_selection.py +0 -0
  295. {rootfig-0.4.0 → rootfig-0.6.0}/tests/test_tutorials.py +0 -0
  296. {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.4.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 you already have `hist.Hist` objects, `rf.plot_histograms` draws them with
201
- the same options. If you want the arrays, `rf.load` returns them. See
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 you already have `hist.Hist` objects, `rf.plot_histograms` draws them with
168
- the same options. If you want the arrays, `rf.load` returns them. See
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
@@ -3,7 +3,6 @@
3
3
  ## Top level
4
4
 
5
5
  ::: rootfig.plot
6
- ::: rootfig.plot_histograms
7
6
  ::: rootfig.plot2d
8
7
  ::: rootfig.histogram
9
8
  ::: rootfig.histograms
@@ -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.with_(label="...")` returns a modified copy; replacement values are
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] (and of
4
- [`rf.plot_histograms`][rootfig.plot_histograms], which draws existing
5
- `hist.Hist` objects with the same options).
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
- `plot_histograms`; [`uncertainty`][rootfig.histograms.uncertainty] and
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; change them with `with_(...)`.
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
- `plot_histograms` are converted to `Weight` storage first. If such a histogram
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(mc: list[rf.Sample], data: rf.Sample, met: rf.Variable, style: rf.Style) -> rf.Plot:
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
- varied = [
245
- zjets.with_(systematics={"pileup": ("weight_pu_up", "weight_pu_down"), "jes": jes}),
246
- diboson.with_(systematics={"jes": jes, "xsec": 0.10}),
247
- signal,
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
- varied,
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.with_(color=plt.rcParams["text.color"], histtype="errorbar"),
276
- diboson.with_(color="#d95f02"),
277
- signal.with_(color="#1b9e77", histtype="fill"),
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.with_`` derives variants of a sample."""
425
+ their muons, soft ones included. ``Sample.replace`` derives variants of a sample."""
417
426
  return rf.plot(
418
427
  [
419
- signal.with_(label="All muons"),
420
- signal.with_(label="Muon_pt > 100", selection="Muon_pt > 100"),
421
- signal.with_(label="any(Muon_pt > 100)", selection="any(Muon_pt > 100)"),
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.plot_histograms`` draws any collection
434
- of them with the usual options."""
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.with_(expression="first(Muon_pt)"))
437
- momentum = rf.histogram(signal, pt.with_(expression="Muon_pt * cosh(Muon_eta)"))
438
- return rf.plot_histograms(
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
- labels=["All muons", "Leading muon", r"Muon $|\vec{p}|$"],
441
- variable=pt.with_(label=r"$p_T^{\mu}$ or $|\vec{p}^{\,\mu}|$"),
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.4.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, plot_histograms
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.with_(lumi=lumi)
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.with_(text=[*existing, *extra])
48
+ return resolved.replace(text=[*existing, *extra])
49
49
 
50
50
 
51
51
  def normalize_for_plot(histogram_: Histogram, spec: NormalizeSpec) -> Histogram: