rootfig 0.3.0__tar.gz → 0.5.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 (301) hide show
  1. {rootfig-0.3.0 → rootfig-0.5.0}/CONTRIBUTING.md +29 -18
  2. {rootfig-0.3.0 → rootfig-0.5.0}/PKG-INFO +26 -6
  3. {rootfig-0.3.0 → rootfig-0.5.0}/README.md +25 -5
  4. {rootfig-0.3.0 → rootfig-0.5.0}/docs/api.md +3 -0
  5. {rootfig-0.3.0 → rootfig-0.5.0}/docs/composable.md +17 -5
  6. rootfig-0.5.0/docs/gallery/index.md +67 -0
  7. rootfig-0.5.0/docs/hooks/gallery.py +251 -0
  8. rootfig-0.5.0/docs/images/gallery/arrays-alice-dark.png +0 -0
  9. rootfig-0.5.0/docs/images/gallery/arrays-alice.png +0 -0
  10. rootfig-0.5.0/docs/images/gallery/arrays-atlas-dark.png +0 -0
  11. rootfig-0.5.0/docs/images/gallery/arrays-atlas.png +0 -0
  12. rootfig-0.5.0/docs/images/gallery/arrays-cms-dark.png +0 -0
  13. rootfig-0.5.0/docs/images/gallery/arrays-cms.png +0 -0
  14. rootfig-0.5.0/docs/images/gallery/arrays-dark.png +0 -0
  15. rootfig-0.5.0/docs/images/gallery/arrays-dune-dark.png +0 -0
  16. rootfig-0.5.0/docs/images/gallery/arrays-dune.png +0 -0
  17. rootfig-0.5.0/docs/images/gallery/arrays-lhcb-dark.png +0 -0
  18. rootfig-0.5.0/docs/images/gallery/arrays-lhcb.png +0 -0
  19. rootfig-0.5.0/docs/images/gallery/correlation-alice-dark.png +0 -0
  20. rootfig-0.5.0/docs/images/gallery/correlation-alice.png +0 -0
  21. rootfig-0.5.0/docs/images/gallery/correlation-atlas-dark.png +0 -0
  22. rootfig-0.5.0/docs/images/gallery/correlation-atlas.png +0 -0
  23. rootfig-0.5.0/docs/images/gallery/correlation-cms-dark.png +0 -0
  24. rootfig-0.5.0/docs/images/gallery/correlation-cms.png +0 -0
  25. rootfig-0.5.0/docs/images/gallery/correlation-dark.png +0 -0
  26. rootfig-0.5.0/docs/images/gallery/correlation-dune-dark.png +0 -0
  27. rootfig-0.5.0/docs/images/gallery/correlation-dune.png +0 -0
  28. rootfig-0.5.0/docs/images/gallery/correlation-lhcb-dark.png +0 -0
  29. rootfig-0.5.0/docs/images/gallery/correlation-lhcb.png +0 -0
  30. rootfig-0.5.0/docs/images/gallery/correlation.png +0 -0
  31. rootfig-0.5.0/docs/images/gallery/density_flow-alice-dark.png +0 -0
  32. rootfig-0.5.0/docs/images/gallery/density_flow-alice.png +0 -0
  33. rootfig-0.5.0/docs/images/gallery/density_flow-atlas-dark.png +0 -0
  34. rootfig-0.5.0/docs/images/gallery/density_flow-atlas.png +0 -0
  35. rootfig-0.5.0/docs/images/gallery/density_flow-cms-dark.png +0 -0
  36. rootfig-0.5.0/docs/images/gallery/density_flow-cms.png +0 -0
  37. rootfig-0.5.0/docs/images/gallery/density_flow-dark.png +0 -0
  38. rootfig-0.5.0/docs/images/gallery/density_flow-dune-dark.png +0 -0
  39. rootfig-0.5.0/docs/images/gallery/density_flow-dune.png +0 -0
  40. rootfig-0.5.0/docs/images/gallery/density_flow-lhcb-dark.png +0 -0
  41. rootfig-0.5.0/docs/images/gallery/density_flow-lhcb.png +0 -0
  42. rootfig-0.5.0/docs/images/gallery/density_flow.png +0 -0
  43. rootfig-0.5.0/docs/images/gallery/efficiency-alice-dark.png +0 -0
  44. rootfig-0.5.0/docs/images/gallery/efficiency-alice.png +0 -0
  45. rootfig-0.5.0/docs/images/gallery/efficiency-atlas-dark.png +0 -0
  46. rootfig-0.5.0/docs/images/gallery/efficiency-atlas.png +0 -0
  47. rootfig-0.5.0/docs/images/gallery/efficiency-cms-dark.png +0 -0
  48. rootfig-0.5.0/docs/images/gallery/efficiency-cms.png +0 -0
  49. rootfig-0.5.0/docs/images/gallery/efficiency-dark.png +0 -0
  50. rootfig-0.5.0/docs/images/gallery/efficiency-dune-dark.png +0 -0
  51. rootfig-0.5.0/docs/images/gallery/efficiency-dune.png +0 -0
  52. rootfig-0.5.0/docs/images/gallery/efficiency-lhcb-dark.png +0 -0
  53. rootfig-0.5.0/docs/images/gallery/efficiency-lhcb.png +0 -0
  54. rootfig-0.5.0/docs/images/gallery/expressions-alice-dark.png +0 -0
  55. rootfig-0.5.0/docs/images/gallery/expressions-alice.png +0 -0
  56. rootfig-0.5.0/docs/images/gallery/expressions-atlas-dark.png +0 -0
  57. rootfig-0.5.0/docs/images/gallery/expressions-atlas.png +0 -0
  58. rootfig-0.5.0/docs/images/gallery/expressions-cms-dark.png +0 -0
  59. rootfig-0.5.0/docs/images/gallery/expressions-cms.png +0 -0
  60. rootfig-0.5.0/docs/images/gallery/expressions-dark.png +0 -0
  61. rootfig-0.5.0/docs/images/gallery/expressions-dune-dark.png +0 -0
  62. rootfig-0.5.0/docs/images/gallery/expressions-dune.png +0 -0
  63. rootfig-0.5.0/docs/images/gallery/expressions-lhcb-dark.png +0 -0
  64. rootfig-0.5.0/docs/images/gallery/expressions-lhcb.png +0 -0
  65. rootfig-0.5.0/docs/images/gallery/fill_stats-alice-dark.png +0 -0
  66. rootfig-0.5.0/docs/images/gallery/fill_stats-alice.png +0 -0
  67. rootfig-0.5.0/docs/images/gallery/fill_stats-atlas-dark.png +0 -0
  68. rootfig-0.5.0/docs/images/gallery/fill_stats-atlas.png +0 -0
  69. rootfig-0.5.0/docs/images/gallery/fill_stats-cms-dark.png +0 -0
  70. rootfig-0.5.0/docs/images/gallery/fill_stats-cms.png +0 -0
  71. rootfig-0.5.0/docs/images/gallery/fill_stats-dark.png +0 -0
  72. rootfig-0.5.0/docs/images/gallery/fill_stats-dune-dark.png +0 -0
  73. rootfig-0.5.0/docs/images/gallery/fill_stats-dune.png +0 -0
  74. rootfig-0.5.0/docs/images/gallery/fill_stats-lhcb-dark.png +0 -0
  75. rootfig-0.5.0/docs/images/gallery/fill_stats-lhcb.png +0 -0
  76. rootfig-0.5.0/docs/images/gallery/hist2d-alice-dark.png +0 -0
  77. rootfig-0.5.0/docs/images/gallery/hist2d-alice.png +0 -0
  78. rootfig-0.5.0/docs/images/gallery/hist2d-atlas-dark.png +0 -0
  79. rootfig-0.5.0/docs/images/gallery/hist2d-atlas.png +0 -0
  80. rootfig-0.5.0/docs/images/gallery/hist2d-cms-dark.png +0 -0
  81. rootfig-0.5.0/docs/images/gallery/hist2d-cms.png +0 -0
  82. rootfig-0.5.0/docs/images/gallery/hist2d-dark.png +0 -0
  83. rootfig-0.5.0/docs/images/gallery/hist2d-dune-dark.png +0 -0
  84. rootfig-0.5.0/docs/images/gallery/hist2d-dune.png +0 -0
  85. rootfig-0.5.0/docs/images/gallery/hist2d-lhcb-dark.png +0 -0
  86. rootfig-0.5.0/docs/images/gallery/hist2d-lhcb.png +0 -0
  87. rootfig-0.5.0/docs/images/gallery/log_axes-alice-dark.png +0 -0
  88. rootfig-0.5.0/docs/images/gallery/log_axes-alice.png +0 -0
  89. rootfig-0.5.0/docs/images/gallery/log_axes-atlas-dark.png +0 -0
  90. rootfig-0.5.0/docs/images/gallery/log_axes-atlas.png +0 -0
  91. rootfig-0.5.0/docs/images/gallery/log_axes-cms-dark.png +0 -0
  92. rootfig-0.5.0/docs/images/gallery/log_axes-cms.png +0 -0
  93. rootfig-0.5.0/docs/images/gallery/log_axes-dark.png +0 -0
  94. rootfig-0.5.0/docs/images/gallery/log_axes-dune-dark.png +0 -0
  95. rootfig-0.5.0/docs/images/gallery/log_axes-dune.png +0 -0
  96. rootfig-0.5.0/docs/images/gallery/log_axes-lhcb-dark.png +0 -0
  97. rootfig-0.5.0/docs/images/gallery/log_axes-lhcb.png +0 -0
  98. rootfig-0.5.0/docs/images/gallery/luminosity-dark.png +0 -0
  99. rootfig-0.5.0/docs/images/gallery/many_plots-dark.png +0 -0
  100. rootfig-0.5.0/docs/images/gallery/many_plots.png +0 -0
  101. rootfig-0.5.0/docs/images/gallery/object_vs_event-alice-dark.png +0 -0
  102. rootfig-0.5.0/docs/images/gallery/object_vs_event-alice.png +0 -0
  103. rootfig-0.5.0/docs/images/gallery/object_vs_event-atlas-dark.png +0 -0
  104. rootfig-0.5.0/docs/images/gallery/object_vs_event-atlas.png +0 -0
  105. rootfig-0.5.0/docs/images/gallery/object_vs_event-cms-dark.png +0 -0
  106. rootfig-0.5.0/docs/images/gallery/object_vs_event-cms.png +0 -0
  107. rootfig-0.5.0/docs/images/gallery/object_vs_event-dark.png +0 -0
  108. rootfig-0.5.0/docs/images/gallery/object_vs_event-dune-dark.png +0 -0
  109. rootfig-0.5.0/docs/images/gallery/object_vs_event-dune.png +0 -0
  110. rootfig-0.5.0/docs/images/gallery/object_vs_event-lhcb-dark.png +0 -0
  111. rootfig-0.5.0/docs/images/gallery/object_vs_event-lhcb.png +0 -0
  112. rootfig-0.5.0/docs/images/gallery/overlay_ratio-alice-dark.png +0 -0
  113. rootfig-0.5.0/docs/images/gallery/overlay_ratio-alice.png +0 -0
  114. rootfig-0.5.0/docs/images/gallery/overlay_ratio-atlas-dark.png +0 -0
  115. rootfig-0.5.0/docs/images/gallery/overlay_ratio-atlas.png +0 -0
  116. rootfig-0.5.0/docs/images/gallery/overlay_ratio-cms-dark.png +0 -0
  117. rootfig-0.5.0/docs/images/gallery/overlay_ratio-cms.png +0 -0
  118. rootfig-0.5.0/docs/images/gallery/overlay_ratio-dark.png +0 -0
  119. rootfig-0.5.0/docs/images/gallery/overlay_ratio-dune-dark.png +0 -0
  120. rootfig-0.5.0/docs/images/gallery/overlay_ratio-dune.png +0 -0
  121. rootfig-0.5.0/docs/images/gallery/overlay_ratio-lhcb-dark.png +0 -0
  122. rootfig-0.5.0/docs/images/gallery/overlay_ratio-lhcb.png +0 -0
  123. rootfig-0.5.0/docs/images/gallery/profile-alice-dark.png +0 -0
  124. rootfig-0.5.0/docs/images/gallery/profile-alice.png +0 -0
  125. rootfig-0.5.0/docs/images/gallery/profile-atlas-dark.png +0 -0
  126. rootfig-0.5.0/docs/images/gallery/profile-atlas.png +0 -0
  127. rootfig-0.5.0/docs/images/gallery/profile-cms-dark.png +0 -0
  128. rootfig-0.5.0/docs/images/gallery/profile-cms.png +0 -0
  129. rootfig-0.5.0/docs/images/gallery/profile-dark.png +0 -0
  130. rootfig-0.5.0/docs/images/gallery/profile-dune-dark.png +0 -0
  131. rootfig-0.5.0/docs/images/gallery/profile-dune.png +0 -0
  132. rootfig-0.5.0/docs/images/gallery/profile-lhcb-dark.png +0 -0
  133. rootfig-0.5.0/docs/images/gallery/profile-lhcb.png +0 -0
  134. rootfig-0.5.0/docs/images/gallery/quick-alice-dark.png +0 -0
  135. rootfig-0.5.0/docs/images/gallery/quick-alice.png +0 -0
  136. rootfig-0.5.0/docs/images/gallery/quick-atlas-dark.png +0 -0
  137. rootfig-0.5.0/docs/images/gallery/quick-atlas.png +0 -0
  138. rootfig-0.5.0/docs/images/gallery/quick-cms-dark.png +0 -0
  139. rootfig-0.5.0/docs/images/gallery/quick-cms.png +0 -0
  140. rootfig-0.5.0/docs/images/gallery/quick-dark.png +0 -0
  141. rootfig-0.5.0/docs/images/gallery/quick-dune-dark.png +0 -0
  142. rootfig-0.5.0/docs/images/gallery/quick-dune.png +0 -0
  143. rootfig-0.5.0/docs/images/gallery/quick-lhcb-dark.png +0 -0
  144. rootfig-0.5.0/docs/images/gallery/quick-lhcb.png +0 -0
  145. rootfig-0.5.0/docs/images/gallery/ratio_reference-alice-dark.png +0 -0
  146. rootfig-0.5.0/docs/images/gallery/ratio_reference-alice.png +0 -0
  147. rootfig-0.5.0/docs/images/gallery/ratio_reference-atlas-dark.png +0 -0
  148. rootfig-0.5.0/docs/images/gallery/ratio_reference-atlas.png +0 -0
  149. rootfig-0.5.0/docs/images/gallery/ratio_reference-cms-dark.png +0 -0
  150. rootfig-0.5.0/docs/images/gallery/ratio_reference-cms.png +0 -0
  151. rootfig-0.5.0/docs/images/gallery/ratio_reference-dark.png +0 -0
  152. rootfig-0.5.0/docs/images/gallery/ratio_reference-dune-dark.png +0 -0
  153. rootfig-0.5.0/docs/images/gallery/ratio_reference-dune.png +0 -0
  154. rootfig-0.5.0/docs/images/gallery/ratio_reference-lhcb-dark.png +0 -0
  155. rootfig-0.5.0/docs/images/gallery/ratio_reference-lhcb.png +0 -0
  156. rootfig-0.5.0/docs/images/gallery/robust_range-dark.png +0 -0
  157. rootfig-0.5.0/docs/images/gallery/stack_data-alice-dark.png +0 -0
  158. rootfig-0.5.0/docs/images/gallery/stack_data-alice.png +0 -0
  159. rootfig-0.5.0/docs/images/gallery/stack_data-atlas-dark.png +0 -0
  160. rootfig-0.5.0/docs/images/gallery/stack_data-cms-dark.png +0 -0
  161. rootfig-0.5.0/docs/images/gallery/stack_data-cms.png +0 -0
  162. rootfig-0.5.0/docs/images/gallery/stack_data-dark.png +0 -0
  163. rootfig-0.5.0/docs/images/gallery/stack_data-dune-dark.png +0 -0
  164. rootfig-0.5.0/docs/images/gallery/stack_data-dune.png +0 -0
  165. rootfig-0.5.0/docs/images/gallery/stack_data-lhcb-dark.png +0 -0
  166. rootfig-0.5.0/docs/images/gallery/stack_data-lhcb.png +0 -0
  167. rootfig-0.5.0/docs/images/gallery/stack_data.png +0 -0
  168. rootfig-0.5.0/docs/images/gallery/style_colors-dark.png +0 -0
  169. rootfig-0.5.0/docs/images/gallery/systematics-alice-dark.png +0 -0
  170. rootfig-0.5.0/docs/images/gallery/systematics-alice.png +0 -0
  171. rootfig-0.5.0/docs/images/gallery/systematics-atlas-dark.png +0 -0
  172. rootfig-0.5.0/docs/images/gallery/systematics-atlas.png +0 -0
  173. rootfig-0.5.0/docs/images/gallery/systematics-cms-dark.png +0 -0
  174. rootfig-0.5.0/docs/images/gallery/systematics-cms.png +0 -0
  175. rootfig-0.5.0/docs/images/gallery/systematics-dark.png +0 -0
  176. rootfig-0.5.0/docs/images/gallery/systematics-dune-dark.png +0 -0
  177. rootfig-0.5.0/docs/images/gallery/systematics-dune.png +0 -0
  178. rootfig-0.5.0/docs/images/gallery/systematics-lhcb-dark.png +0 -0
  179. rootfig-0.5.0/docs/images/gallery/systematics-lhcb.png +0 -0
  180. rootfig-0.5.0/docs/images/gallery/systematics.png +0 -0
  181. rootfig-0.5.0/docs/images/gallery/variable_bins-alice-dark.png +0 -0
  182. rootfig-0.5.0/docs/images/gallery/variable_bins-alice.png +0 -0
  183. rootfig-0.5.0/docs/images/gallery/variable_bins-atlas-dark.png +0 -0
  184. rootfig-0.5.0/docs/images/gallery/variable_bins-atlas.png +0 -0
  185. rootfig-0.5.0/docs/images/gallery/variable_bins-cms-dark.png +0 -0
  186. rootfig-0.5.0/docs/images/gallery/variable_bins-cms.png +0 -0
  187. rootfig-0.5.0/docs/images/gallery/variable_bins-dark.png +0 -0
  188. rootfig-0.5.0/docs/images/gallery/variable_bins-dune-dark.png +0 -0
  189. rootfig-0.5.0/docs/images/gallery/variable_bins-dune.png +0 -0
  190. rootfig-0.5.0/docs/images/gallery/variable_bins-lhcb-dark.png +0 -0
  191. rootfig-0.5.0/docs/images/gallery/variable_bins-lhcb.png +0 -0
  192. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-alice-dark.png +0 -0
  193. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-alice.png +0 -0
  194. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-atlas-dark.png +0 -0
  195. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-atlas.png +0 -0
  196. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-cms-dark.png +0 -0
  197. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-cms.png +0 -0
  198. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-dark.png +0 -0
  199. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-dune-dark.png +0 -0
  200. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-dune.png +0 -0
  201. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-lhcb-dark.png +0 -0
  202. rootfig-0.5.0/docs/images/gallery/xbreak_ratio-lhcb.png +0 -0
  203. rootfig-0.5.0/docs/images/gallery/xbreak_ratio.png +0 -0
  204. {rootfig-0.3.0 → rootfig-0.5.0}/docs/index.md +4 -2
  205. {rootfig-0.3.0 → rootfig-0.5.0}/docs/plotting.md +140 -7
  206. rootfig-0.5.0/docs/stylesheets/gallery.css +58 -0
  207. {rootfig-0.3.0 → rootfig-0.5.0}/examples/gallery/__init__.py +298 -186
  208. {rootfig-0.3.0 → rootfig-0.5.0}/examples/gallery/__main__.py +8 -4
  209. {rootfig-0.3.0 → rootfig-0.5.0}/examples/gallery/data.py +12 -2
  210. {rootfig-0.3.0 → rootfig-0.5.0}/examples/gallery/registry.py +56 -11
  211. {rootfig-0.3.0 → rootfig-0.5.0}/mkdocs.yml +17 -1
  212. {rootfig-0.3.0 → rootfig-0.5.0}/pyproject.toml +2 -1
  213. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/__init__.py +9 -3
  214. rootfig-0.5.0/src/rootfig/_mapping.py +43 -0
  215. rootfig-0.5.0/src/rootfig/api/__init__.py +28 -0
  216. rootfig-0.5.0/src/rootfig/api/_common.py +59 -0
  217. rootfig-0.5.0/src/rootfig/api/data.py +186 -0
  218. rootfig-0.5.0/src/rootfig/api/measures.py +302 -0
  219. rootfig-0.5.0/src/rootfig/api/plots1d.py +610 -0
  220. rootfig-0.5.0/src/rootfig/api/plots2d.py +208 -0
  221. rootfig-0.5.0/src/rootfig/api/tables.py +135 -0
  222. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/errors.py +4 -0
  223. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/histograms/__init__.py +4 -0
  224. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/histograms/build.py +127 -10
  225. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/histograms/normalize.py +27 -3
  226. rootfig-0.5.0/src/rootfig/histograms/pipeline.py +476 -0
  227. rootfig-0.5.0/src/rootfig/histograms/ratio.py +241 -0
  228. rootfig-0.5.0/src/rootfig/histograms/systematics.py +175 -0
  229. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/io/sources.py +4 -0
  230. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/model/__init__.py +10 -0
  231. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/model/samples.py +48 -9
  232. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/model/style.py +2 -2
  233. rootfig-0.5.0/src/rootfig/model/systematics.py +254 -0
  234. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/model/variables.py +2 -2
  235. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/plotting/__init__.py +8 -1
  236. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/plotting/annotations.py +2 -2
  237. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/plotting/correlation.py +14 -1
  238. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/plotting/figure.py +1 -1
  239. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/plotting/hist1d.py +118 -70
  240. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/plotting/ratio.py +73 -30
  241. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/plotting/result.py +52 -2
  242. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/plotting/style.py +289 -58
  243. {rootfig-0.3.0 → rootfig-0.5.0}/tests/test_api.py +455 -1
  244. rootfig-0.5.0/tests/test_gallery.py +266 -0
  245. {rootfig-0.3.0 → rootfig-0.5.0}/tests/test_histograms.py +537 -3
  246. {rootfig-0.3.0 → rootfig-0.5.0}/tests/test_model.py +182 -15
  247. {rootfig-0.3.0 → rootfig-0.5.0}/tests/test_plotting.py +111 -0
  248. rootfig-0.5.0/tests/type_checks/systematics.py +20 -0
  249. rootfig-0.3.0/docs/gallery.md +0 -74
  250. rootfig-0.3.0/docs/hooks/gallery.py +0 -99
  251. rootfig-0.3.0/docs/images/gallery/cms_density.png +0 -0
  252. rootfig-0.3.0/docs/images/gallery/correlation.png +0 -0
  253. rootfig-0.3.0/docs/images/gallery/many_plots.png +0 -0
  254. rootfig-0.3.0/docs/images/gallery/xbreak_ratio.png +0 -0
  255. rootfig-0.3.0/src/rootfig/api.py +0 -1363
  256. rootfig-0.3.0/src/rootfig/histograms/pipeline.py +0 -236
  257. rootfig-0.3.0/src/rootfig/histograms/ratio.py +0 -161
  258. rootfig-0.3.0/tests/test_gallery.py +0 -173
  259. {rootfig-0.3.0 → rootfig-0.5.0}/.gitignore +0 -0
  260. {rootfig-0.3.0 → rootfig-0.5.0}/LICENSE +0 -0
  261. {rootfig-0.3.0 → rootfig-0.5.0}/docs/ecosystem.md +0 -0
  262. {rootfig-0.3.0 → rootfig-0.5.0}/docs/expressions.md +0 -0
  263. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/arrays.png +0 -0
  264. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/efficiency.png +0 -0
  265. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/expressions.png +0 -0
  266. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/fill_stats.png +0 -0
  267. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/hist2d.png +0 -0
  268. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/log_axes.png +0 -0
  269. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/luminosity.png +0 -0
  270. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/object_vs_event.png +0 -0
  271. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/overlay_ratio.png +0 -0
  272. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/profile.png +0 -0
  273. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/quick.png +0 -0
  274. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/ratio_reference.png +0 -0
  275. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/robust_range.png +0 -0
  276. /rootfig-0.3.0/docs/images/gallery/stack_data.png → /rootfig-0.5.0/docs/images/gallery/stack_data-atlas.png +0 -0
  277. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/style_colors.png +0 -0
  278. {rootfig-0.3.0 → rootfig-0.5.0}/docs/images/gallery/variable_bins.png +0 -0
  279. {rootfig-0.3.0 → rootfig-0.5.0}/docs/quickstart.md +0 -0
  280. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/_typing.py +0 -0
  281. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/expressions/__init__.py +0 -0
  282. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/expressions/functions.py +0 -0
  283. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/expressions/parser.py +0 -0
  284. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/histograms/cutflow.py +0 -0
  285. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/histograms/efficiency.py +0 -0
  286. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/histograms/stats.py +0 -0
  287. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/io/__init__.py +0 -0
  288. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/model/binning.py +0 -0
  289. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/model/cuts.py +0 -0
  290. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/model/units.py +0 -0
  291. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/plotting/hist2d.py +0 -0
  292. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/plotting/points.py +0 -0
  293. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/py.typed +0 -0
  294. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/selection/__init__.py +0 -0
  295. {rootfig-0.3.0 → rootfig-0.5.0}/src/rootfig/selection/columns.py +0 -0
  296. {rootfig-0.3.0 → rootfig-0.5.0}/tests/conftest.py +0 -0
  297. {rootfig-0.3.0 → rootfig-0.5.0}/tests/data/split_collection.root +0 -0
  298. {rootfig-0.3.0 → rootfig-0.5.0}/tests/test_expressions.py +0 -0
  299. {rootfig-0.3.0 → rootfig-0.5.0}/tests/test_io.py +0 -0
  300. {rootfig-0.3.0 → rootfig-0.5.0}/tests/test_selection.py +0 -0
  301. {rootfig-0.3.0 → rootfig-0.5.0}/tests/test_tutorials.py +0 -0
@@ -36,7 +36,7 @@ tests that require ROOT or network access.
36
36
 
37
37
  ```
38
38
  src/rootfig/
39
- api.py plot(), histogram(), load(), ... (orchestration only)
39
+ api/ plot(), histogram(), load(), ... (orchestration only)
40
40
  errors.py exception hierarchy
41
41
  expressions/ parse, validate and evaluate expression strings
42
42
  io/ file and in-memory data sources
@@ -90,32 +90,43 @@ f.Close()
90
90
  ## Figures and the gallery
91
91
 
92
92
  The `examples/gallery` package is both the showcase and the image-regression
93
- suite: `__init__.py` holds the shared `define()` block and the examples, `data.py`
94
- writes the toy files and `registry.py` extracts the source shown in the docs. Each
95
- example is a small function returning a `Plot`; `docs/gallery.md` shows every
96
- figure next to that function's source (a MkDocs hook, `docs/hooks/gallery.py`),
97
- and `tests/test_gallery.py` renders all of them and, with `--mpl`, compares
93
+ suite: `__init__.py` holds the shared `define()` block, the `STYLES` and the
94
+ examples, `data.py` writes the toy files and `registry.py` extracts the source
95
+ shown in the docs. Each example is a small function returning a `Plot`. The
96
+ MkDocs hook `docs/hooks/gallery.py` turns them into the gallery: an overview
97
+ (`docs/gallery/index.md`, a card per example, section by section) and a
98
+ generated page per example with its figure and complete code, one tab per
99
+ style. `tests/test_gallery.py` renders all of them and, with `--mpl`, compares
98
100
  them pixel-wise (pytest-mpl, RMS tolerance 2) against `docs/images/gallery/`.
99
- Those PNGs are therefore the documentation images *and* the baselines.
101
+ An example whose function takes a `style` is rendered in every entry of
102
+ `STYLES` (the neutral default, ATLAS, CMS, LHCb, ALICE, DUNE) as
103
+ `<name>-<style>.png`, `<name>.png` for the default; every rendering is made
104
+ twice, as shown and inside `rf.dark_theme()` (`-dark`), and the docs pick one
105
+ per palette. Those PNGs are therefore the documentation images *and* the
106
+ baselines. The experiment styles are drawn only when images are compared or
107
+ generated, and `-n auto` runs that on every core.
100
108
 
101
109
  ```bash
102
- MPLBACKEND=Agg uv run python examples/gallery # look at examples/out/*.png
103
- uv run pytest tests/test_gallery.py --mpl # compare against the baselines
104
- uv run pytest tests/test_gallery.py --mpl-generate-path=docs/images/gallery # accept changes
110
+ MPLBACKEND=Agg uv run python examples/gallery # look at examples/out/*.png
111
+ MPLBACKEND=Agg uv run python examples/gallery --style CMS # ... in another style
112
+ uv run pytest tests/test_gallery.py --mpl -n auto # compare against the baselines
113
+ uv run pytest tests/test_gallery.py -n auto --mpl-generate-path=docs/images/gallery # accept changes
105
114
  ```
106
115
 
107
116
  After any visual change: regenerate the baselines, open the PNGs and check
108
117
  them by eye, and commit them with the code. CI compares on Linux only (fonts
109
118
  differ elsewhere) and, when a comparison fails, uploads an HTML report with
110
119
  baseline, result and difference images as the `mpl-results-*` artifact. To add
111
- an example, register a function with `@example(name, title)` and give it a
112
- docstring; the test suite fails until its baseline image exists. Its parameters
113
- are attribute names of `Dataset`, it runs inside the directory holding the toy
114
- files (so name them `"signal.root"`, never through a variable), and the hook
115
- prints only the body (blank lines and comments included) — write it as a user
116
- would. Put an object into `define()` — the *Setup* block of the docs page — only
117
- when several examples use it; anything a single example needs belongs in that
118
- example.
120
+ an example, register a function with `@example(name, title, section=...)` and
121
+ give it a docstring; the test suite fails until its baseline images exist. Its
122
+ parameters are attribute names of `Dataset`, plus `style` when the figure should
123
+ be shown in every style (pass it on as `style=style`; leave it out when a style
124
+ is the point of the example or the figure goes into axes of your own). It runs
125
+ inside the directory holding the toy files (so name them `"signal.root"`, never
126
+ through a variable), and the hook prints only the body (blank lines and
127
+ comments included) — write it as a user would. Put an object into `define()` —
128
+ the *Setup* block of the example pages — only when several examples use it;
129
+ anything a single example needs belongs in that example.
119
130
 
120
131
  ## Pull requests
121
132
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: rootfig
3
- Version: 0.3.0
3
+ Version: 0.5.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/
@@ -73,19 +73,20 @@ panels as your analysis grows. Every plot gives you a matplotlib figure to
73
73
  customise and save.
74
74
 
75
75
  <p align="center">
76
- <a href="https://jbeirer.github.io/rootfig/gallery/#logarithmic-axes-with-log-spaced-bins"><img src="docs/images/gallery/log_axes.png" alt="Logarithmic axes with log-spaced bins" width="46%"></a>
76
+ <a href="https://jbeirer.github.io/rootfig/gallery/log_axes/"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/images/gallery/log_axes-dark.png"><img src="docs/images/gallery/log_axes.png" alt="Logarithmic axes with log-spaced bins" width="46%"></picture></a>
77
77
  &nbsp;&nbsp;
78
- <a href="https://jbeirer.github.io/rootfig/gallery/#a-broken-x-axis-peak-and-far-tail-without-the-empty-middle"><img src="docs/images/gallery/xbreak_ratio.png" alt="Broken x axis with a ratio panel" width="46%"></a>
78
+ <a href="https://jbeirer.github.io/rootfig/gallery/hist2d/"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/images/gallery/hist2d-dark.png"><img src="docs/images/gallery/hist2d.png" alt="Two-dimensional histogram" width="46%"></picture></a>
79
79
  </p>
80
80
  <p align="center">
81
- <a href="https://jbeirer.github.io/rootfig/gallery/#a-correlation-matrix"><img src="docs/images/gallery/correlation.png" alt="A correlation matrix" width="46%"></a>
81
+ <a href="https://jbeirer.github.io/rootfig/gallery/xbreak_ratio/#atlas"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/images/gallery/xbreak_ratio-atlas-dark.png"><img src="docs/images/gallery/xbreak_ratio-atlas.png" alt="Broken x axis with a ratio panel" width="46%"></picture></a>
82
82
  &nbsp;&nbsp;
83
- <a href="https://jbeirer.github.io/rootfig/gallery/#a-two-dimensional-histogram"><img src="docs/images/gallery/hist2d.png" alt="Two-dimensional histogram" width="46%"></a>
83
+ <a href="https://jbeirer.github.io/rootfig/gallery/luminosity/"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/images/gallery/luminosity-dark.png"><img src="docs/images/gallery/luminosity.png" alt="FCC-ee stack scaled to luminosity with a significance panel" width="43%"></picture></a>
84
84
  </p>
85
85
 
86
86
  **[Explore the gallery →](https://jbeirer.github.io/rootfig/gallery/)**
87
87
  See each figure alongside the code that makes it, from simple overlays to
88
- stacked data/MC comparisons, broken axes and 2D histograms.
88
+ stacked data/MC comparisons, broken axes and 2D histograms, in the neutral
89
+ style or in that of ATLAS, CMS, LHCb, ALICE or DUNE.
89
90
 
90
91
  ## Installation
91
92
 
@@ -159,6 +160,10 @@ matplotlib `Figure`/`Axes`, `p.hists` are `hist.Hist` objects, and
159
160
  and `(n, low, high)` are used as given, while a range inferred from the data
160
161
  ignores far outliers, so `-999` sentinels do not set the axis. Normalise to
161
162
  unity, density, bin width or luminosity.
163
+ - **Show systematic uncertainties.** Attach weight, branch, file or
164
+ normalisation variations to a sample; stacks and ratio panels draw the
165
+ combined statistical and systematic band, and every component stays
166
+ accessible.
162
167
  - **Style figures for your analysis.** Add experiment labels, units, log axes
163
168
  and broken axes, then refine the result with matplotlib.
164
169
  - **Go beyond 1D plots.** Draw 2D histograms, correlations, efficiencies,
@@ -209,6 +214,21 @@ uv run mypy
209
214
 
210
215
  See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
211
216
 
217
+ ## Citation
218
+
219
+ If `rootfig` is useful in your research, please cite it:
220
+
221
+ ```bibtex
222
+ @software{rootfig,
223
+ author = {Beirer, Joshua Falco},
224
+ doi = {10.5281/zenodo.22726311},
225
+ license = {MIT},
226
+ title = {{rootfig}},
227
+ url = {https://github.com/jbeirer/rootfig},
228
+ year = {2026}
229
+ }
230
+ ```
231
+
212
232
  ## License
213
233
 
214
234
  MIT. See [LICENSE](LICENSE).
@@ -40,19 +40,20 @@ panels as your analysis grows. Every plot gives you a matplotlib figure to
40
40
  customise and save.
41
41
 
42
42
  <p align="center">
43
- <a href="https://jbeirer.github.io/rootfig/gallery/#logarithmic-axes-with-log-spaced-bins"><img src="docs/images/gallery/log_axes.png" alt="Logarithmic axes with log-spaced bins" width="46%"></a>
43
+ <a href="https://jbeirer.github.io/rootfig/gallery/log_axes/"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/images/gallery/log_axes-dark.png"><img src="docs/images/gallery/log_axes.png" alt="Logarithmic axes with log-spaced bins" width="46%"></picture></a>
44
44
  &nbsp;&nbsp;
45
- <a href="https://jbeirer.github.io/rootfig/gallery/#a-broken-x-axis-peak-and-far-tail-without-the-empty-middle"><img src="docs/images/gallery/xbreak_ratio.png" alt="Broken x axis with a ratio panel" width="46%"></a>
45
+ <a href="https://jbeirer.github.io/rootfig/gallery/hist2d/"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/images/gallery/hist2d-dark.png"><img src="docs/images/gallery/hist2d.png" alt="Two-dimensional histogram" width="46%"></picture></a>
46
46
  </p>
47
47
  <p align="center">
48
- <a href="https://jbeirer.github.io/rootfig/gallery/#a-correlation-matrix"><img src="docs/images/gallery/correlation.png" alt="A correlation matrix" width="46%"></a>
48
+ <a href="https://jbeirer.github.io/rootfig/gallery/xbreak_ratio/#atlas"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/images/gallery/xbreak_ratio-atlas-dark.png"><img src="docs/images/gallery/xbreak_ratio-atlas.png" alt="Broken x axis with a ratio panel" width="46%"></picture></a>
49
49
  &nbsp;&nbsp;
50
- <a href="https://jbeirer.github.io/rootfig/gallery/#a-two-dimensional-histogram"><img src="docs/images/gallery/hist2d.png" alt="Two-dimensional histogram" width="46%"></a>
50
+ <a href="https://jbeirer.github.io/rootfig/gallery/luminosity/"><picture><source media="(prefers-color-scheme: dark)" srcset="docs/images/gallery/luminosity-dark.png"><img src="docs/images/gallery/luminosity.png" alt="FCC-ee stack scaled to luminosity with a significance panel" width="43%"></picture></a>
51
51
  </p>
52
52
 
53
53
  **[Explore the gallery →](https://jbeirer.github.io/rootfig/gallery/)**
54
54
  See each figure alongside the code that makes it, from simple overlays to
55
- stacked data/MC comparisons, broken axes and 2D histograms.
55
+ stacked data/MC comparisons, broken axes and 2D histograms, in the neutral
56
+ style or in that of ATLAS, CMS, LHCb, ALICE or DUNE.
56
57
 
57
58
  ## Installation
58
59
 
@@ -126,6 +127,10 @@ matplotlib `Figure`/`Axes`, `p.hists` are `hist.Hist` objects, and
126
127
  and `(n, low, high)` are used as given, while a range inferred from the data
127
128
  ignores far outliers, so `-999` sentinels do not set the axis. Normalise to
128
129
  unity, density, bin width or luminosity.
130
+ - **Show systematic uncertainties.** Attach weight, branch, file or
131
+ normalisation variations to a sample; stacks and ratio panels draw the
132
+ combined statistical and systematic band, and every component stays
133
+ accessible.
129
134
  - **Style figures for your analysis.** Add experiment labels, units, log axes
130
135
  and broken axes, then refine the result with matplotlib.
131
136
  - **Go beyond 1D plots.** Draw 2D histograms, correlations, efficiencies,
@@ -176,6 +181,21 @@ uv run mypy
176
181
 
177
182
  See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
178
183
 
184
+ ## Citation
185
+
186
+ If `rootfig` is useful in your research, please cite it:
187
+
188
+ ```bibtex
189
+ @software{rootfig,
190
+ author = {Beirer, Joshua Falco},
191
+ doi = {10.5281/zenodo.22726311},
192
+ license = {MIT},
193
+ title = {{rootfig}},
194
+ url = {https://github.com/jbeirer/rootfig},
195
+ year = {2026}
196
+ }
197
+ ```
198
+
179
199
  ## License
180
200
 
181
201
  MIT. See [LICENSE](LICENSE).
@@ -18,6 +18,7 @@
18
18
  ::: rootfig.ratio
19
19
  ::: rootfig.log_bins
20
20
  ::: rootfig.use_style
21
+ ::: rootfig.dark_theme
21
22
 
22
23
  ## Descriptions
23
24
 
@@ -25,12 +26,14 @@
25
26
  ::: rootfig.Variable
26
27
  ::: rootfig.Cut
27
28
  ::: rootfig.Style
29
+ ::: rootfig.Systematic
28
30
 
29
31
  ## Results
30
32
 
31
33
  ::: rootfig.Plot
32
34
  ::: rootfig.Histogram
33
35
  ::: rootfig.Ratio
36
+ ::: rootfig.Uncertainty
34
37
  ::: rootfig.Summary
35
38
  ::: rootfig.SummaryTable
36
39
  ::: rootfig.Cutflow
@@ -28,8 +28,9 @@ mem = rf.Sample({"x": awkward_array, "w": weights}, label="in memory")
28
28
  implementing the [`Source`][rootfig.io.Source] protocol.
29
29
  - `selection` and `weight` belong to the sample and combine with the ones
30
30
  given to `plot()` (`&` and `*` respectively).
31
- - `is_data=True` draws black points with error bars, keeps the sample out of
32
- stacks and makes it the numerator of ratios.
31
+ - `is_data=True` draws points with error bars (in the style's text colour
32
+ unless `color` is set), keeps the sample out of stacks and makes it the
33
+ numerator of ratios.
33
34
  - `xsec` and `ngen` describe simulated processes: the cross section (pb, or a
34
35
  string with a unit such as `"1.2 fb"`) and the number of generated events (a
35
36
  number, the name of an object in the file holding it, e.g. FCCAnalyses'
@@ -48,7 +49,14 @@ mem = rf.Sample({"x": awkward_array, "w": weights}, label="in memory")
48
49
  (a plot reads every needed branch of every file into memory at once). For a
49
50
  ready-made `FileSource`, give the range to the source itself; passing it to
50
51
  `Sample` afterwards raises a [`SourceError`][rootfig.SourceError].
51
- - `sample.with_(label="...")` returns a modified copy; replacement values are
52
+ - `systematics={name: variation}` lists the sample's sources of systematic
53
+ uncertainty: weight expressions (`("w_up", "w_down")`), normalisation
54
+ uncertainties (`0.05`, `(1.1, 0.95)`), varied branches
55
+ (`{"Jet_pt": ("Jet_pt_up", "Jet_pt_down")}`) and varied files
56
+ ([`Systematic.samples`][rootfig.Systematic]). Sources with
57
+ the same name are correlated across samples; see
58
+ [Systematic uncertainties](plotting.md#systematic-uncertainties).
59
+ - `sample.replace(label="...")` returns a copy with the given fields changed; new values are
52
60
  validated like constructor arguments.
53
61
 
54
62
  Passing a list of files to `plot()` creates one sample per file. To merge
@@ -113,8 +121,12 @@ neutral = rf.Style(
113
121
  given.
114
122
  - `base` can be any mplhep or matplotlib style name (`"ATLAS"`, `"ggplot"`,
115
123
  ...) or a mapping of rcParams; `rc` adds overrides on top; `colors` replaces
116
- the colour cycle. `label_loc` is mplhep's label position (0 above the axes,
117
- 1 to 4 inside the corners), overriding the experiment's convention.
124
+ the colour cycle. `label_loc` overrides the experiment's convention: 0 puts
125
+ the experiment and secondary text above the frame; 3 puts the experiment
126
+ above and secondary text inside; 1, 2, and 4 put both inside. Luminosity stays
127
+ above for locations 0–3 and inside for 4. 2D histograms and correlation
128
+ matrices default to location 0; only `label_loc=1`, `2`, or `4` moves the
129
+ experiment inside the frame.
118
130
  - The centre-of-mass energy and luminosity appear only when `com`/`lumi` are
119
131
  given; nothing is invented for you.
120
132
  - `legend` is `True`, `False` or a location string; `legend_kwargs` are
@@ -0,0 +1,67 @@
1
+ ---
2
+ hide:
3
+ - toc
4
+ ---
5
+
6
+ # Gallery
7
+
8
+ Every figure below is drawn by the code on its page. Most examples use a toy
9
+ ROOT dataset: three simulated processes and one "observed" sample with muons,
10
+ jets and event-level quantities. The in-memory arrays example generates its
11
+ own NumPy data.
12
+
13
+ Choose a style: examples with style tabs follow your choice here and on their
14
+ individual pages. The other examples keep their own styles.
15
+
16
+ <!-- gallery-overview -->
17
+
18
+ ## Run the examples
19
+
20
+ The examples live in
21
+ [`examples/gallery`](https://github.com/jbeirer/rootfig/blob/main/examples/gallery/__init__.py),
22
+ and one command writes the toy files and every figure in a few seconds:
23
+
24
+ ```bash
25
+ python examples/gallery # everything ends up in examples/out/
26
+ python examples/gallery --style CMS # CMS for examples with style tabs
27
+ ```
28
+
29
+ Each picture is also the reference image the test suite compares against, so
30
+ what you see is what the current release draws.
31
+
32
+ ## Beyond figures
33
+
34
+ The same inputs feed tables and arrays (`mc` and `signal` are the samples from
35
+ the setup block of the example pages):
36
+
37
+ ```python
38
+ # entries, mean, std, sem, skewness, min, max per sample and variable
39
+ print(rf.summarize(mc, ["MET", "Muon_pt"], selection="nMuon > 0"))
40
+
41
+ # a cut flow: yields, raw counts and step efficiencies per sample
42
+ print(
43
+ rf.cutflow(
44
+ mc,
45
+ ["nMuon >= 2", rf.Cut("MET > 50", label="MET > 50 GeV"), "any(Jet_btag > 0.8)"],
46
+ )
47
+ )
48
+
49
+ # evaluated expressions as an Awkward record array
50
+ events = rf.load(signal, ["MET", "count(Muon_pt)", "first(Muon_pt)"], selection="nJet >= 2")
51
+ events["MET"]
52
+
53
+ # a plain hist.Hist to feed into your own code
54
+ h = rf.histogram(signal, "MET", bins=(40, 0, 400), selection="nJet >= 2")
55
+ ```
56
+
57
+ Cuts compose with `&`, `|` and `~`, carry optional labels, and combine with
58
+ selections given to `plot()`:
59
+
60
+ ```python
61
+ base = rf.Cut("nMuon >= 2", label="2 muons")
62
+ signal_region = base & "abs(Muon_eta) < 2.4" & ~rf.Cut("any(Jet_btag > 0.8)")
63
+ control_region = base & rf.Cut("any(Jet_btag > 0.8)") | "MET > 200"
64
+ ```
65
+
66
+ See [Plotting options](../plotting.md) for every keyword and
67
+ [Expressions and selections](../expressions.md) for the per-event/per-object rules.
@@ -0,0 +1,251 @@
1
+ """MkDocs hook that builds the gallery pages from the ``examples/gallery`` package.
2
+
3
+ The gallery is an overview page and one page per example, all made from the
4
+ registry:
5
+
6
+ ``gallery/index.md``
7
+ carries the ``<!-- gallery-overview -->`` marker, replaced by one tab per
8
+ gallery style, each holding the examples section by section as a grid of
9
+ cards (thumbnail and title, linking to the example's page);
10
+ ``gallery/<name>.md``
11
+ generated for every example (``on_files``), with no file in ``docs/``: the
12
+ title, the description, one tab per style the example is drawn in with its
13
+ image (``docs/images/gallery/<name>[-<style>].png``, and ``-dark`` for the
14
+ dark palette, switched by Material's ``#only-light``/``#only-dark``) and the
15
+ complete code, and the setup block the code relies on.
16
+
17
+ The Gallery entry of the navigation, ``gallery/index.md``, becomes a section with
18
+ the overview and the example pages, grouped like the overview (``on_config``).
19
+ Every tab set uses the same labels, so Material's ``content.tabs.link`` switches
20
+ all figures on all pages together. Tabs, cards and the setup block are
21
+ pymdownx blocks, which nest without indentation.
22
+
23
+ The gallery module is imported, never executed, so building the docs needs no
24
+ data and draws nothing. Because the images are also the baselines of
25
+ ``tests/test_gallery.py``, picture and code cannot drift apart.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import importlib.util
31
+ import re
32
+ import sys
33
+ import textwrap
34
+ from collections.abc import Iterable, Sequence
35
+ from pathlib import Path
36
+ from types import ModuleType
37
+ from typing import TYPE_CHECKING, Any
38
+
39
+ if TYPE_CHECKING: # MkDocs runs the hook; the tests import it without MkDocs installed
40
+ from mkdocs.config.defaults import MkDocsConfig
41
+ from mkdocs.structure.files import Files
42
+
43
+ ROOT = Path(__file__).resolve().parents[2]
44
+ GALLERY_DIR = ROOT / "examples" / "gallery"
45
+ OVERVIEW = "gallery/index.md"
46
+ OVERVIEW_MARKER = "<!-- gallery-overview -->"
47
+ IMAGES = "../images/gallery" # relative to the gallery pages
48
+
49
+
50
+ def _load_gallery(name: str = "rootfig_gallery_docs") -> ModuleType:
51
+ """Import ``examples/gallery`` as a fresh package, without ``examples/`` on ``sys.path``.
52
+
53
+ Every call re-executes the package (``mkdocs serve`` rebuilds on each change),
54
+ so its submodules are evicted from ``sys.modules`` first: a cached
55
+ ``registry`` would keep its ``EXAMPLES`` list and the re-run decorators would
56
+ register every example a second time.
57
+ """
58
+ for cached in [m for m in sys.modules if m == name or m.startswith(f"{name}.")]:
59
+ del sys.modules[cached]
60
+ spec = importlib.util.spec_from_file_location(
61
+ name, GALLERY_DIR / "__init__.py", submodule_search_locations=[str(GALLERY_DIR)]
62
+ )
63
+ if spec is None or spec.loader is None:
64
+ msg = f"cannot load {GALLERY_DIR}"
65
+ raise RuntimeError(msg)
66
+ module = importlib.util.module_from_spec(spec)
67
+ sys.modules[name] = module
68
+ spec.loader.exec_module(module)
69
+ return module
70
+
71
+
72
+ # --------------------------------------------------------------------------------------
73
+ # MkDocs events
74
+ # --------------------------------------------------------------------------------------
75
+
76
+
77
+ def on_config(config: MkDocsConfig) -> MkDocsConfig:
78
+ """Turn the navigation's gallery entry into the gallery section."""
79
+ if config.nav is not None:
80
+ config.nav = gallery_nav(config.nav, _load_gallery())
81
+ return config
82
+
83
+
84
+ def on_files(files: Files, config: MkDocsConfig) -> Files:
85
+ """Add a generated page for every example."""
86
+ from mkdocs.structure.files import File # noqa: PLC0415 - only MkDocs calls this
87
+
88
+ gallery = _load_gallery()
89
+ for example in gallery.EXAMPLES:
90
+ content = render_page(gallery, example)
91
+ files.append(File.generated(config, page_path(example), content=content))
92
+ return files
93
+
94
+
95
+ def on_page_markdown(markdown: str, **_: Any) -> str:
96
+ """Expand the overview marker (MkDocs ``on_page_markdown`` event)."""
97
+ if OVERVIEW_MARKER not in markdown:
98
+ return markdown
99
+ return markdown.replace(OVERVIEW_MARKER, render_overview(_load_gallery()))
100
+
101
+
102
+ # --------------------------------------------------------------------------------------
103
+ # Navigation
104
+ # --------------------------------------------------------------------------------------
105
+
106
+
107
+ def page_path(example: Any) -> str:
108
+ """Path of an example's page, relative to ``docs/``."""
109
+ return f"gallery/{example.name}.md"
110
+
111
+
112
+ def sections(examples: Iterable[Any]) -> dict[str, list[Any]]:
113
+ """Group ``examples`` by section, sections in order of their first example."""
114
+ grouped: dict[str, list[Any]] = {}
115
+ for example in examples:
116
+ grouped.setdefault(example.section, []).append(example)
117
+ return grouped
118
+
119
+
120
+ def gallery_nav(nav: Sequence[Any], gallery: ModuleType) -> list[Any]:
121
+ """Return ``nav`` with its ``gallery/index.md`` entry expanded into the gallery section."""
122
+ pages = [
123
+ {title: [page_path(example) for example in examples]}
124
+ for title, examples in sections(gallery.EXAMPLES).items()
125
+ ]
126
+
127
+ def expand(item: Any) -> Any:
128
+ if isinstance(item, dict) and list(item.values()) == [OVERVIEW]:
129
+ return {title: [OVERVIEW, *pages] for title in item}
130
+ return item
131
+
132
+ return [expand(item) for item in nav]
133
+
134
+
135
+ # --------------------------------------------------------------------------------------
136
+ # Markdown
137
+ # --------------------------------------------------------------------------------------
138
+
139
+
140
+ def render_overview(gallery: ModuleType) -> str:
141
+ """One tab per gallery style; each holds every section's cards in that style."""
142
+ grouped = sections(gallery.EXAMPLES)
143
+ tabs = []
144
+ for style in gallery.STYLES:
145
+ content = "\n\n".join(
146
+ _block("html", 'p.gallery-section[role="heading" aria-level="2"]', title, level=5)
147
+ + "\n\n"
148
+ + _block(
149
+ "html", "div.grid.cards.gallery-cards", _cards(gallery, examples, style), level=5
150
+ )
151
+ for title, examples in grouped.items()
152
+ )
153
+ tabs.append((style, content))
154
+ return _block("html", "div.gallery-overview", _tabs(tabs, level=4), level=3)
155
+
156
+
157
+ def _cards(gallery: ModuleType, examples: Iterable[Any], style: str) -> str:
158
+ cards = []
159
+ for example in examples:
160
+ shown = style if style in gallery.styles_of(example) else gallery.DEFAULT_STYLE
161
+ images = _images(example, shown, alt="", width=None)
162
+ link = f"[{example.title}]({page_path(example).removeprefix('gallery/')})"
163
+ card = textwrap.indent(f"{images}\n\n{link}", " ")
164
+ cards.append(f"-{card[1:]}") # the list marker takes the first indent's place
165
+ return "\n\n".join(cards)
166
+
167
+
168
+ def render_page(gallery: ModuleType, example: Any) -> str:
169
+ """The page of one example: title, description, figure and code per style, setup."""
170
+ figures = [
171
+ (style, _figure(gallery, example, style if example.styled else None))
172
+ for style in gallery.styles_of(example)
173
+ ]
174
+ shown = _tabs(figures, level=3) if example.styled else figures[0][1]
175
+ # no table of contents (a page has one heading), so figure and code get its width
176
+ parts = ["---\nhide:\n - toc\n---", f"# {example.title}", example.description, shown]
177
+ setup = _setup(gallery, example)
178
+ return "\n\n".join([*parts, *([setup] if setup else [])]) + "\n"
179
+
180
+
181
+ def _figure(gallery: ModuleType, example: Any, style: str | None) -> str:
182
+ """The image pair and the complete code of ``example`` in ``style`` (``None``: unstyled)."""
183
+ alt = example.title if style is None else f"{example.title}, {style} style"
184
+ images = _images(example, style or gallery.DEFAULT_STYLE, alt=alt, width=example.image_width)
185
+ code, highlighted = example_code(gallery, example, style)
186
+ options = f' hl_lines="{" ".join(map(str, highlighted))}"' if highlighted else ""
187
+ figure = _block("html", "figure.gallery-figure", images, level=4)
188
+ return f"{figure}\n\n```python{options}\n{code}```"
189
+
190
+
191
+ def _images(example: Any, style: str, *, alt: str, width: str | None) -> str:
192
+ """Markdown for the light and the dark image of ``example`` in ``style``, lazily loaded."""
193
+ attributes = f'width="{width}" loading=lazy' if width else "loading=lazy"
194
+ return "\n".join(
195
+ f"![{alt}]({IMAGES}/{example.image(style, dark=dark)}#only-{theme}){{ {attributes} }}"
196
+ for dark, theme in ((False, "light"), (True, "dark"))
197
+ )
198
+
199
+
200
+ def example_code(gallery: ModuleType, example: Any, style: str | None) -> tuple[str, list[int]]:
201
+ """The code shown for ``example``, and the numbers of the lines to highlight.
202
+
203
+ It starts with the imports the body uses and, for a style, the line that makes
204
+ that style; the highlighted lines are that line and those passing it on, so
205
+ switching tabs shows exactly what changes.
206
+ """
207
+ body = gallery.body_source(example.func).splitlines()
208
+ imports = [
209
+ f"import {module} as {alias}"
210
+ for module, alias in (("matplotlib.pyplot", "plt"), ("numpy", "np"))
211
+ if any(re.search(rf"\b{alias}\.", text) for text in body)
212
+ ]
213
+ lines = [*imports, "import rootfig as rf", ""]
214
+ highlighted = []
215
+ if style is not None:
216
+ lines += [f"style = {gallery.style_source(gallery.STYLES[style])}", ""]
217
+ highlighted.append(len(lines) - 1)
218
+ highlighted += [len(lines) + n for n, text in enumerate(body, 1) if "style=style" in text]
219
+ return "\n".join([*lines, *body]) + "\n", highlighted
220
+
221
+
222
+ def _setup(gallery: ModuleType, example: Any) -> str | None:
223
+ """How to run ``example``: where the toy files come from, and the definitions it uses."""
224
+ shared = any(name != "style" for name in example.parameters)
225
+ parts = []
226
+ if example.toy_data:
227
+ parts.append(
228
+ "The code reads the toy dataset. In a checkout of "
229
+ "[rootfig](https://github.com/jbeirer/rootfig), `python examples/gallery` writes "
230
+ "it to `examples/out/` in a few seconds; run the code inside that directory."
231
+ )
232
+ if shared:
233
+ code = gallery.body_source(gallery.define, returns="omit")
234
+ parts.append(
235
+ "It uses these samples and variables, shared by the gallery examples (see "
236
+ f"[Samples, variables, cuts and styles](../composable.md)):\n\n```python\n{code}```"
237
+ )
238
+ if not parts:
239
+ return None
240
+ return _block("details", "Setup", "\n\n".join(parts), level=3, options="type: abstract")
241
+
242
+
243
+ def _tabs(tabs: Iterable[tuple[str, str]], *, level: int) -> str:
244
+ return "\n\n".join(_block("tab", label, content, level=level) for label, content in tabs)
245
+
246
+
247
+ def _block(kind: str, argument: str, content: str, *, level: int, options: str = "") -> str:
248
+ """A pymdownx block; nested blocks need more slashes than the block around them."""
249
+ fence = "/" * level
250
+ header = f"{fence} {kind} | {argument}" + (f"\n {options}" if options else "")
251
+ return f"{header}\n\n{content}\n\n{fence}"