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