plothist 1.5.0__tar.gz → 1.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. {plothist-1.5.0 → plothist-1.6.0}/.gitignore +1 -0
  2. {plothist-1.5.0 → plothist-1.6.0}/.pre-commit-config.yaml +3 -3
  3. {plothist-1.5.0 → plothist-1.6.0}/PKG-INFO +1 -1
  4. {plothist-1.5.0 → plothist-1.6.0}/codecov.yml +0 -3
  5. {plothist-1.5.0 → plothist-1.6.0}/docs/conf.py +3 -5
  6. plothist-1.6.0/docs/documentation/statistics.rst +21 -0
  7. plothist-1.6.0/docs/img/model_all_comparisons.png +0 -0
  8. plothist-1.6.0/docs/img/model_all_comparisons_no_model_unc.png +0 -0
  9. plothist-1.6.0/docs/img/model_examples_pull.png +0 -0
  10. {plothist-1.5.0 → plothist-1.6.0}/docs/img/model_examples_pull_no_model_unc.png +0 -0
  11. plothist-1.6.0/docs/img/model_examples_stacked.png +0 -0
  12. plothist-1.6.0/docs/img/model_examples_stacked_unstacked.png +0 -0
  13. plothist-1.6.0/docs/img/model_examples_unstacked.png +0 -0
  14. {plothist-1.5.0 → plothist-1.6.0}/docs/img/ratio_data_vs_model_with_stacked_and_unstacked_function_components.png +0 -0
  15. plothist-1.6.0/docs/img/uncertainty_types.png +0 -0
  16. {plothist-1.5.0 → plothist-1.6.0}/pyproject.toml +4 -1
  17. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/_version.py +2 -2
  18. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/comparison.py +61 -17
  19. plothist-1.6.0/src/plothist/examples/utility/uncertainty_types.py +120 -0
  20. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/plothist_style.py +2 -2
  21. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/plotters.py +3 -1
  22. {plothist-1.5.0 → plothist-1.6.0}/tests/test_comparison.py +11 -1
  23. {plothist-1.5.0 → plothist-1.6.0}/tests/test_variable_registry.py +12 -12
  24. plothist-1.5.0/docs/documentation/statistics.rst +0 -14
  25. plothist-1.5.0/docs/img/model_all_comparisons.png +0 -0
  26. plothist-1.5.0/docs/img/model_all_comparisons_no_model_unc.png +0 -0
  27. plothist-1.5.0/docs/img/model_examples_pull.png +0 -0
  28. plothist-1.5.0/docs/img/model_examples_stacked.png +0 -0
  29. plothist-1.5.0/docs/img/model_examples_stacked_unstacked.png +0 -0
  30. plothist-1.5.0/docs/img/model_examples_unstacked.png +0 -0
  31. {plothist-1.5.0 → plothist-1.6.0}/.github/ISSUE_TEMPLATE/user-story.md +0 -0
  32. {plothist-1.5.0 → plothist-1.6.0}/.github/dependabot.yaml +0 -0
  33. {plothist-1.5.0 → plothist-1.6.0}/.github/release.yaml +0 -0
  34. {plothist-1.5.0 → plothist-1.6.0}/.github/workflows/cd.yaml +0 -0
  35. {plothist-1.5.0 → plothist-1.6.0}/.github/workflows/ci.yaml +0 -0
  36. {plothist-1.5.0 → plothist-1.6.0}/.github/workflows/coverage.yaml +0 -0
  37. {plothist-1.5.0 → plothist-1.6.0}/.github/workflows/pr.yaml +0 -0
  38. {plothist-1.5.0 → plothist-1.6.0}/.readthedocs.yaml +0 -0
  39. {plothist-1.5.0 → plothist-1.6.0}/AUTHORS.md +0 -0
  40. {plothist-1.5.0 → plothist-1.6.0}/CITATION.cff +0 -0
  41. {plothist-1.5.0 → plothist-1.6.0}/LICENSE +0 -0
  42. {plothist-1.5.0 → plothist-1.6.0}/README.rst +0 -0
  43. {plothist-1.5.0 → plothist-1.6.0}/docs/CONTRIBUTING.rst +0 -0
  44. {plothist-1.5.0 → plothist-1.6.0}/docs/advanced/model_examples.rst +0 -0
  45. {plothist-1.5.0 → plothist-1.6.0}/docs/advanced/other_advanced.rst +0 -0
  46. {plothist-1.5.0 → plothist-1.6.0}/docs/basics/1d_func.rst +0 -0
  47. {plothist-1.5.0 → plothist-1.6.0}/docs/basics/1d_hist.rst +0 -0
  48. {plothist-1.5.0 → plothist-1.6.0}/docs/basics/2d_hist.rst +0 -0
  49. {plothist-1.5.0 → plothist-1.6.0}/docs/basics/variable_registry.rst +0 -0
  50. {plothist-1.5.0 → plothist-1.6.0}/docs/documentation/documentation.rst +0 -0
  51. {plothist-1.5.0 → plothist-1.6.0}/docs/documentation/troubleshooting.rst +0 -0
  52. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_comparison_advanced.png +0 -0
  53. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_comparison_advanced.svg +0 -0
  54. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_comparison_asymmetry.png +0 -0
  55. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_comparison_difference.png +0 -0
  56. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_comparison_efficiency.png +0 -0
  57. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_comparison_only_efficiency.png +0 -0
  58. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_comparison_pull.png +0 -0
  59. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_comparison_ratio.png +0 -0
  60. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_comparison_relative_difference.png +0 -0
  61. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_comparison_split_ratio.png +0 -0
  62. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_elt1.png +0 -0
  63. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_elt1_stacked.png +0 -0
  64. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_elt2.png +0 -0
  65. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_hist_simple.png +0 -0
  66. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_int_category.png +0 -0
  67. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_profile.png +0 -0
  68. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_side_by_side.png +0 -0
  69. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_side_by_side_with_numbers.png +0 -0
  70. {plothist-1.5.0 → plothist-1.6.0}/docs/img/1d_str_category.png +0 -0
  71. {plothist-1.5.0 → plothist-1.6.0}/docs/img/2d_hist_correlations_0.png +0 -0
  72. {plothist-1.5.0 → plothist-1.6.0}/docs/img/2d_hist_correlations_1.png +0 -0
  73. {plothist-1.5.0 → plothist-1.6.0}/docs/img/2d_hist_correlations_2.png +0 -0
  74. {plothist-1.5.0 → plothist-1.6.0}/docs/img/2d_hist_simple.png +0 -0
  75. {plothist-1.5.0 → plothist-1.6.0}/docs/img/2d_hist_simple_discrete_colormap.png +0 -0
  76. {plothist-1.5.0 → plothist-1.6.0}/docs/img/2d_hist_uneven.png +0 -0
  77. {plothist-1.5.0 → plothist-1.6.0}/docs/img/2d_hist_with_projections.png +0 -0
  78. {plothist-1.5.0 → plothist-1.6.0}/docs/img/2d_hist_with_projections.svg +0 -0
  79. {plothist-1.5.0 → plothist-1.6.0}/docs/img/add_text_example.png +0 -0
  80. {plothist-1.5.0 → plothist-1.6.0}/docs/img/asymmetry_comparison_advanced.png +0 -0
  81. {plothist-1.5.0 → plothist-1.6.0}/docs/img/color_palette_hists.png +0 -0
  82. {plothist-1.5.0 → plothist-1.6.0}/docs/img/fct_1d.png +0 -0
  83. {plothist-1.5.0 → plothist-1.6.0}/docs/img/fct_1d_stacked.png +0 -0
  84. {plothist-1.5.0 → plothist-1.6.0}/docs/img/matplotlib_example.png +0 -0
  85. {plothist-1.5.0 → plothist-1.6.0}/docs/img/model_examples_flatten2D.png +0 -0
  86. {plothist-1.5.0 → plothist-1.6.0}/docs/img/model_examples_stacked.svg +0 -0
  87. {plothist-1.5.0 → plothist-1.6.0}/docs/img/model_with_stacked_and_unstacked_function_components.png +0 -0
  88. {plothist-1.5.0 → plothist-1.6.0}/docs/img/model_with_stacked_and_unstacked_histograms_components.png +0 -0
  89. {plothist-1.5.0 → plothist-1.6.0}/docs/img/plothist_example.png +0 -0
  90. {plothist-1.5.0 → plothist-1.6.0}/docs/img/pyhf_example.svg +0 -0
  91. {plothist-1.5.0 → plothist-1.6.0}/docs/img/savefig_comparisons.gif +0 -0
  92. {plothist-1.5.0 → plothist-1.6.0}/docs/img/usage_YlGnBu_r_palette.png +0 -0
  93. {plothist-1.5.0 → plothist-1.6.0}/docs/img/usage_coolwarm_palette.png +0 -0
  94. {plothist-1.5.0 → plothist-1.6.0}/docs/img/usage_cubehelix.png +0 -0
  95. {plothist-1.5.0 → plothist-1.6.0}/docs/img/usage_style_cycle.png +0 -0
  96. {plothist-1.5.0 → plothist-1.6.0}/docs/img/usage_viridis_palette.png +0 -0
  97. {plothist-1.5.0 → plothist-1.6.0}/docs/index.rst +0 -0
  98. {plothist-1.5.0 → plothist-1.6.0}/docs/usage/installation.rst +0 -0
  99. {plothist-1.5.0 → plothist-1.6.0}/docs/usage/plot_fitting_results.rst +0 -0
  100. {plothist-1.5.0 → plothist-1.6.0}/docs/usage/style.rst +0 -0
  101. {plothist-1.5.0 → plothist-1.6.0}/docs/usage/utilities.rst +0 -0
  102. {plothist-1.5.0 → plothist-1.6.0}/noxfile.py +0 -0
  103. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/__init__.py +5 -5
  104. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/_version.pyi +0 -0
  105. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/default_style.mplstyle +0 -0
  106. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_comparison_asymmetry.py +0 -0
  107. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_comparison_difference.py +0 -0
  108. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_comparison_efficiency.py +0 -0
  109. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_comparison_only_efficiency.py +0 -0
  110. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_comparison_pull.py +0 -0
  111. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_comparison_ratio.py +0 -0
  112. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_comparison_relative_difference.py +0 -0
  113. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_comparison_split_ratio.py +0 -0
  114. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_elt1.py +0 -0
  115. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_elt1_stacked.py +0 -0
  116. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_elt2.py +0 -0
  117. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_hist_simple.py +0 -0
  118. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_int_category.py +0 -0
  119. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_profile.py +0 -0
  120. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_side_by_side.py +0 -0
  121. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/1d_str_category.py +0 -0
  122. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/1d_hist/README.rst +0 -0
  123. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/2d_hist/2d_hist_correlations.py +0 -0
  124. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/2d_hist/2d_hist_simple.py +0 -0
  125. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/2d_hist/2d_hist_simple_discrete_colormap.py +0 -0
  126. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/2d_hist/2d_hist_uneven.py +0 -0
  127. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/2d_hist/2d_hist_with_projections.py +0 -0
  128. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/2d_hist/README.rst +0 -0
  129. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/README.rst +0 -0
  130. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/advanced/1d_comparison_advanced.py +0 -0
  131. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/advanced/1d_side_by_side_with_numbers.py +0 -0
  132. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/advanced/README.rst +0 -0
  133. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/advanced/asymmetry_comparison_advanced.py +0 -0
  134. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/advanced/model_examples_flatten2D.py +0 -0
  135. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/func_1d/README.rst +0 -0
  136. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/func_1d/fct_1d.py +0 -0
  137. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/func_1d/fct_1d_stacked.py +0 -0
  138. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/README.rst +0 -0
  139. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/model_all_comparisons.py +0 -0
  140. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/model_all_comparisons_no_model_unc.py +0 -0
  141. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/model_examples_pull.py +0 -0
  142. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/model_examples_pull_no_model_unc.py +0 -0
  143. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/model_examples_stacked.py +0 -0
  144. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/model_examples_stacked_unstacked.py +0 -0
  145. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/model_examples_unstacked.py +0 -0
  146. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/model_with_stacked_and_unstacked_function_components.py +0 -0
  147. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/model_with_stacked_and_unstacked_histograms_components.py +0 -0
  148. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/model_ex/ratio_data_vs_model_with_stacked_and_unstacked_function_components.py +0 -0
  149. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/utility/README.rst +0 -0
  150. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/utility/add_text_example.py +0 -0
  151. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/utility/color_palette_hists.py +0 -0
  152. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/utility/color_palette_squares.py +0 -0
  153. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/examples/utility/matplotlib_vs_plothist_style.py +0 -0
  154. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/histogramming.py +0 -0
  155. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/test_helpers.py +0 -0
  156. {plothist-1.5.0 → plothist-1.6.0}/src/plothist/variable_registry.py +0 -0
  157. {plothist-1.5.0 → plothist-1.6.0}/tests/test_boost_histogram_version.py +0 -0
  158. {plothist-1.5.0 → plothist-1.6.0}/tests/test_comparison_asymmetry.py +0 -0
  159. {plothist-1.5.0 → plothist-1.6.0}/tests/test_comparison_difference.py +0 -0
  160. {plothist-1.5.0 → plothist-1.6.0}/tests/test_comparison_efficiency.py +0 -0
  161. {plothist-1.5.0 → plothist-1.6.0}/tests/test_comparison_pull.py +0 -0
  162. {plothist-1.5.0 → plothist-1.6.0}/tests/test_comparison_ratio.py +0 -0
  163. {plothist-1.5.0 → plothist-1.6.0}/tests/test_examples_1d_hist.py +0 -0
  164. {plothist-1.5.0 → plothist-1.6.0}/tests/test_examples_2d_hist.py +0 -0
  165. {plothist-1.5.0 → plothist-1.6.0}/tests/test_examples_advanced.py +0 -0
  166. {plothist-1.5.0 → plothist-1.6.0}/tests/test_examples_data_model.py +0 -0
  167. {plothist-1.5.0 → plothist-1.6.0}/tests/test_examples_func_1d.py +0 -0
  168. {plothist-1.5.0 → plothist-1.6.0}/tests/test_examples_utility.py +0 -0
  169. {plothist-1.5.0 → plothist-1.6.0}/tests/test_fonts.py +0 -0
  170. {plothist-1.5.0 → plothist-1.6.0}/tests/test_get_data.py +0 -0
  171. {plothist-1.5.0 → plothist-1.6.0}/tests/test_histogramming.py +0 -0
  172. {plothist-1.5.0 → plothist-1.6.0}/tests/test_plothist_style.py +0 -0
  173. {plothist-1.5.0 → plothist-1.6.0}/tests/test_plotters.py +0 -0
  174. {plothist-1.5.0 → plothist-1.6.0}/tests/test_test_helpers.py +0 -0
  175. {plothist-1.5.0 → plothist-1.6.0}/uv.lock +0 -0
@@ -15,6 +15,7 @@ temp_img/
15
15
  html/
16
16
  sg_execution_times.rst
17
17
  docs/examples/*/*.svg
18
+ src/plothist/examples/*/*.svg
18
19
  example_gallery/
19
20
  .svg_metadata.yaml
20
21
  tests/*.yaml
@@ -30,7 +30,7 @@ repos:
30
30
  args: ["--pytest-test-first"]
31
31
 
32
32
  - repo: https://github.com/astral-sh/ruff-pre-commit
33
- rev: v0.12.2
33
+ rev: v0.12.7
34
34
  hooks:
35
35
  - id: ruff
36
36
  args: ["--fix", "--show-fixes"]
@@ -50,7 +50,7 @@ repos:
50
50
  args: ["--verbose"]
51
51
 
52
52
  - repo: https://github.com/pre-commit/mirrors-mypy
53
- rev: v1.16.1
53
+ rev: v1.17.1
54
54
  hooks:
55
55
  - id: mypy
56
56
  files: src
@@ -73,6 +73,6 @@ repos:
73
73
  - id: check-github-workflows
74
74
 
75
75
  - repo: https://github.com/henryiii/validate-pyproject-schema-store
76
- rev: 2025.06.23
76
+ rev: 2025.07.28
77
77
  hooks:
78
78
  - id: validate-pyproject
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plothist
3
- Version: 1.5.0
3
+ Version: 1.6.0
4
4
  Summary: Plot histograms in a scalable way and a beautiful style.
5
5
  Project-URL: Homepage, https://github.com/cyrraz/plothist
6
6
  Project-URL: Documentation, https://plothist.readthedocs.io/
@@ -1,6 +1,3 @@
1
1
  comment: false
2
2
 
3
3
  github_checks: false
4
-
5
- ignore:
6
- - "src/plothist/scripts"
@@ -1,5 +1,8 @@
1
1
  import sys
2
2
 
3
+ from sphinx_gallery.scrapers import matplotlib_scraper
4
+ from sphinx_gallery.sorting import ExplicitOrder
5
+
3
6
  import plothist
4
7
 
5
8
  #
@@ -175,9 +178,6 @@ def reset_mpl(gallery_conf, fname):
175
178
  set_style("default")
176
179
 
177
180
 
178
- from sphinx_gallery.scrapers import matplotlib_scraper
179
-
180
-
181
181
  class matplotlib_svg_scraper:
182
182
  def __repr__(self):
183
183
  return self.__class__.__name__
@@ -186,8 +186,6 @@ class matplotlib_svg_scraper:
186
186
  return matplotlib_scraper(*args, format="svg", bbox_inches="tight", **kwargs)
187
187
 
188
188
 
189
- from sphinx_gallery.sorting import ExplicitOrder
190
-
191
189
  sphinx_gallery_conf = {
192
190
  # path to your example scripts
193
191
  "examples_dirs": ["../src/plothist/examples"],
@@ -0,0 +1,21 @@
1
+ .. _documentation-statistics-label:
2
+
3
+ ===================
4
+ Notes on statistics
5
+ ===================
6
+
7
+ In the :func:`plot_error_hist() <plothist.plotters.plot_error_hist>` function, the represented uncertainties can be:
8
+
9
+ - ``symmetrical``: use the square root of the variance of the histogram
10
+ - ``asymmetrical``: frequentist approach based on a Poisson confidence interval (see `page 16 on this presentation <https://www.nikhef.nl/~ivov/Statistics/PoissonError/2017_05_15_PoissonError_LHCb_IvovanVulpen.pdf>`_)
11
+
12
+ - the upper limit on bins with no entries can be interpreted differently. By default, a one-sided 68% confidence bound is used. The behavior can be changed using the ``uncertainty_type = asymmetrical_double_sided_zeros``, which will use a two-sided 68% confidence bound.
13
+ - this method doesn't support weighted histograms. For more details, see the `note about uncertainties when the histogram entries are weighted <https://www.pp.rhul.ac.uk/~cowan/stat/notes/errors_with_weights.pdf>`_.
14
+
15
+ .. image:: ../img/uncertainty_types.png
16
+ :alt: Simple error hist
17
+ :width: 500
18
+
19
+ The uncertainty on the efficiency (ratio of two histograms h1 and h2 when the entries of h1 are a subset of the entries of h2) in :func:`get_efficiency() <plothist.comparison.get_efficiency>` is calculated following equation (19) of:
20
+
21
+ - `T. Ullrich, Z. Xu, Treatment of Errors in Efficiency Calculations, arxiv: 0701199v1. <https://arxiv.org/pdf/physics/0701199v1.pdf>`_
@@ -83,7 +83,10 @@ extend-select = [
83
83
  "PD", # pandas-vet
84
84
  ]
85
85
  ignore = [
86
- "E402", # module level import not at top of file
87
86
  "NPY002", # Replace legacy `np.random` call with `np.random.Generator`
87
+ ]
88
+ [tool.ruff.lint.per-file-ignores]
89
+ "src/plothist/examples/**/*.py" = [
90
+ "E402", # module level import not at top of file
88
91
  "PD901", # Avoid using the generic variable name `df` for DataFrames
89
92
  ]
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.5.0'
21
- __version_tuple__ = version_tuple = (1, 5, 0)
20
+ __version__ = version = '1.6.0'
21
+ __version_tuple__ = version_tuple = (1, 6, 0)
@@ -22,9 +22,16 @@ def _check_uncertainty_type(uncertainty_type: str) -> None:
22
22
  If the uncertainty type is not valid.
23
23
 
24
24
  """
25
- if uncertainty_type not in ["symmetrical", "asymmetrical"]:
25
+ _valid_uncertainty_types = [
26
+ "symmetrical",
27
+ "asymmetrical",
28
+ "asymmetrical_double_sided_zeros",
29
+ "asymmetrical_one_sided_zeros",
30
+ ]
31
+
32
+ if uncertainty_type not in _valid_uncertainty_types:
26
33
  raise ValueError(
27
- f"Uncertainty type {uncertainty_type} not valid. Must be 'symmetrical' or 'asymmetrical'."
34
+ f"Uncertainty type {uncertainty_type} not valid. Must be in {_valid_uncertainty_types}."
28
35
  )
29
36
 
30
37
 
@@ -42,11 +49,12 @@ def _is_unweighted(hist: bh.Histogram) -> bool:
42
49
  bool
43
50
  True if the histogram is unweighted, False otherwise.
44
51
  """
45
- return np.allclose(hist.variances(), hist.values())
52
+ return np.allclose(hist.variances(), hist.values(), equal_nan=True)
46
53
 
47
54
 
48
55
  def get_asymmetrical_uncertainties(
49
56
  hist: bh.Histogram,
57
+ uncertainty_type: str = "asymmetrical",
50
58
  ) -> tuple[np.ndarray, np.ndarray]:
51
59
  """
52
60
  Get Poisson asymmetrical uncertainties for a histogram via a frequentist approach based on a confidence-interval computation.
@@ -57,6 +65,8 @@ def get_asymmetrical_uncertainties(
57
65
  ----------
58
66
  hist : bh.Histogram
59
67
  The histogram.
68
+ uncertainty_type : str, optional
69
+ The type of uncertainty to compute for bins with 0 entry. Default is "asymmetrical" (= "asymmetrical_one_sided_zeros"). Use "asymmetrical_double_sided_zeros" to have the double-sided definition. More information in :ref:`documentation-statistics-label`.
60
70
 
61
71
  Returns
62
72
  -------
@@ -72,16 +82,44 @@ def get_asymmetrical_uncertainties(
72
82
 
73
83
  """
74
84
  _check_counting_histogram(hist)
85
+ _check_uncertainty_type(uncertainty_type)
75
86
 
76
87
  if not _is_unweighted(hist):
77
88
  raise ValueError(
78
89
  "Asymmetrical uncertainties can only be computed for an unweighted histogram."
79
90
  )
80
- conf_level = 0.682689492
81
- alpha = 1.0 - conf_level
91
+
92
+ alpha = 1.0 - 0.682689492
93
+ tail_probability = alpha / 2
94
+
82
95
  n = hist.values()
83
- uncertainties_low = n - stats.gamma.ppf(alpha / 2, n, scale=1)
84
- uncertainties_high = stats.gamma.ppf(1 - alpha / 2, n + 1, scale=1) - n
96
+
97
+ lower_bound = np.zeros_like(n, dtype=float)
98
+ upper_bound = np.zeros_like(n, dtype=float)
99
+
100
+ # Two-sided Garwood intervals for n > 0
101
+ lower_bound[n > 0] = stats.gamma.ppf(q=tail_probability, a=n[n > 0], scale=1)
102
+ upper_bound[n > 0] = stats.gamma.ppf(
103
+ q=1 - tail_probability, a=n[n > 0] + 1, scale=1
104
+ )
105
+
106
+ if uncertainty_type == "asymmetrical_double_sided_zeros":
107
+ # Two-sided Garwood intervals for n == 0
108
+ upper_bound[n == 0] = stats.gamma.ppf(q=1 - tail_probability, a=1, scale=1)
109
+ elif uncertainty_type in ["asymmetrical_one_sided_zeros", "asymmetrical"]:
110
+ # One-sided upper limit for n == 0
111
+ upper_bound[n == 0] = stats.gamma.ppf(q=1 - 2 * tail_probability, a=1, scale=1)
112
+ else:
113
+ raise ValueError(
114
+ f"Invalid uncertainty type '{uncertainty_type}' for asymmetrical uncertainties."
115
+ )
116
+
117
+ # Compute asymmetric uncertainties
118
+ uncertainties_low = n - lower_bound
119
+ uncertainties_high = upper_bound - n
120
+
121
+ uncertainties_low = np.nan_to_num(uncertainties_low, nan=0.0)
122
+ uncertainties_high = np.nan_to_num(uncertainties_high, nan=0.0)
85
123
 
86
124
  return uncertainties_low, uncertainties_high
87
125
 
@@ -174,8 +212,10 @@ def get_pull(
174
212
  _check_counting_histogram(h1)
175
213
  _check_counting_histogram(h2)
176
214
 
177
- if h1_uncertainty_type == "asymmetrical":
178
- uncertainties_low, uncertainties_high = get_asymmetrical_uncertainties(h1)
215
+ if "asymmetrical" in h1_uncertainty_type:
216
+ uncertainties_low, uncertainties_high = get_asymmetrical_uncertainties(
217
+ h1, h1_uncertainty_type
218
+ )
179
219
  h1_variances = np.where(
180
220
  h1.values() >= h2.values(),
181
221
  uncertainties_low**2,
@@ -232,8 +272,10 @@ def get_difference(
232
272
 
233
273
  difference_values = h1.values() - h2.values()
234
274
 
235
- if h1_uncertainty_type == "asymmetrical":
236
- uncertainties_low, uncertainties_high = get_asymmetrical_uncertainties(h1)
275
+ if "asymmetrical" in h1_uncertainty_type:
276
+ uncertainties_low, uncertainties_high = get_asymmetrical_uncertainties(
277
+ h1, h1_uncertainty_type
278
+ )
237
279
 
238
280
  difference_uncertainties_low = np.sqrt(uncertainties_low**2 + h2.variances())
239
281
  difference_uncertainties_high = np.sqrt(uncertainties_high**2 + h2.variances())
@@ -390,11 +432,13 @@ def get_ratio(
390
432
 
391
433
  ratio_values = np.where(h2.values() != 0, h1.values() / h2.values(), np.nan)
392
434
 
393
- if h1_uncertainty_type == "asymmetrical":
394
- uncertainties_low, uncertainties_high = get_asymmetrical_uncertainties(h1)
435
+ if "asymmetrical" in h1_uncertainty_type:
436
+ uncertainties_low, uncertainties_high = get_asymmetrical_uncertainties(
437
+ h1, h1_uncertainty_type
438
+ )
395
439
 
396
440
  if ratio_uncertainty_type == "uncorrelated":
397
- if h1_uncertainty_type == "asymmetrical":
441
+ if "asymmetrical" in h1_uncertainty_type:
398
442
  h1_high = h1.copy()
399
443
  h1_high[:] = np.c_[h1_high.values(), uncertainties_high**2]
400
444
  h1_low = h1.copy()
@@ -405,7 +449,7 @@ def get_ratio(
405
449
  ratio_uncertainties_low = np.sqrt(get_ratio_variances(h1, h2))
406
450
  ratio_uncertainties_high = ratio_uncertainties_low
407
451
  elif ratio_uncertainty_type == "split":
408
- if h1_uncertainty_type == "asymmetrical":
452
+ if "asymmetrical" in h1_uncertainty_type:
409
453
  ratio_uncertainties_low = uncertainties_low / h2.values()
410
454
  ratio_uncertainties_high = uncertainties_high / h2.values()
411
455
  else:
@@ -493,7 +537,7 @@ def get_comparison(
493
537
  h1, h2, h1_uncertainty_type
494
538
  )
495
539
  elif comparison == "asymmetry":
496
- if h1_uncertainty_type == "asymmetrical":
540
+ if "asymmetrical" in h1_uncertainty_type:
497
541
  raise ValueError(
498
542
  "Asymmetrical uncertainties are not supported for the asymmetry comparison."
499
543
  )
@@ -501,7 +545,7 @@ def get_comparison(
501
545
  lower_uncertainties = uncertainties
502
546
  upper_uncertainties = uncertainties
503
547
  elif comparison == "efficiency":
504
- if h1_uncertainty_type == "asymmetrical":
548
+ if "asymmetrical" in h1_uncertainty_type:
505
549
  raise ValueError(
506
550
  "Asymmetrical uncertainties are not supported in an efficiency computation."
507
551
  )
@@ -0,0 +1,120 @@
1
+ """
2
+ Representation of different uncertainty types.
3
+ ==============================================
4
+
5
+ This example demonstrates how to use the `plot_error_hist` function with different uncertainty types
6
+ """
7
+
8
+ import boost_histogram as bh
9
+ import matplotlib.pyplot as plt
10
+ import numpy as np
11
+
12
+ from plothist import add_text, plot_error_hist
13
+
14
+
15
+ def shifted_array(values, offset: int, size: int = 15) -> np.ndarray:
16
+ """
17
+ Create an array of a given size with NaNs and fill it with values at specified offsets.
18
+ The values are inserted at every offset starting from the given offset.
19
+ """
20
+
21
+ arr = np.full(size, np.nan)
22
+ indices = range(offset, size, len(values))
23
+
24
+ for i, idx in enumerate(indices):
25
+ if i >= len(values):
26
+ break
27
+ arr[idx] = values[i]
28
+
29
+ return arr
30
+
31
+
32
+ def make_grouped_edges(
33
+ n_groups, group_size=4, inner_spacing=0.1, inter_spacing=0.2
34
+ ) -> np.ndarray:
35
+ """
36
+ Create a set of edges for a histogram with grouped categories.
37
+ """
38
+
39
+ edges = [
40
+ group_start + i * inner_spacing
41
+ for group in range(n_groups)
42
+ for i in range(group_size)
43
+ for group_start in [
44
+ group * (group_size * inner_spacing + inter_spacing - inner_spacing)
45
+ ]
46
+ ]
47
+ return np.array(edges)
48
+
49
+
50
+ # Create a category axis with explicit locations
51
+ edges = make_grouped_edges(4)
52
+ axis = bh.axis.Variable(edges)
53
+
54
+ hists = []
55
+ entries = [0, 0.5, 3, 500]
56
+
57
+ for k in range(3):
58
+ hist = bh.Histogram(axis, storage=bh.storage.Weight())
59
+ values = shifted_array(entries, k)
60
+ hist[:] = np.c_[values, values]
61
+ hists.append(hist)
62
+
63
+
64
+ fig, (ax_top, ax_bot) = plt.subplots(
65
+ 2, 1, gridspec_kw={"height_ratios": [1, 3], "hspace": 0.05}
66
+ )
67
+
68
+ for ax in (ax_top, ax_bot):
69
+ plot_error_hist(
70
+ hists[0], ax=ax, label="symmetrical", uncertainty_type="symmetrical"
71
+ )
72
+ plot_error_hist(
73
+ hists[1], ax=ax, label="asymmetrical", uncertainty_type="asymmetrical"
74
+ )
75
+ plot_error_hist(
76
+ hists[2],
77
+ ax=ax,
78
+ label="asymmetrical_double_sided_zeros",
79
+ uncertainty_type="asymmetrical_double_sided_zeros",
80
+ )
81
+
82
+ add_text("plot_error_hist() with different uncertainty type", ax=ax_top, x="right")
83
+
84
+ # Set axis limits
85
+ ax_top.set_ylim(465, 530)
86
+ ax_bot.set_ylim(-0.5, 6.9)
87
+ ax_bot.set_xlim(xmin=-0.05)
88
+
89
+ # Format bottom ticks and labels
90
+ ax_bot.set_xticks(edges[1::4] + 0.05)
91
+ ax_bot.set_xlabel("Entry category")
92
+ ax_bot.set_xticklabels(entries)
93
+ ax_bot.set_ylabel("Entries")
94
+ ax_bot.yaxis.label.set_horizontalalignment("left")
95
+ ax_bot.spines.top.set_visible(False)
96
+ ax_bot.xaxis.set_minor_locator(plt.NullLocator()) # Hide x-axis minor ticks
97
+
98
+ # Format top ticks and labels
99
+ ax_top.xaxis.tick_top()
100
+ ax_top.spines.bottom.set_visible(False)
101
+ ax_top.set_xticklabels([])
102
+ ax_top.set_xticks([])
103
+
104
+ # Draw break marks
105
+ d = 0.5 # proportion of vertical to horizontal extent of the slanted line
106
+ kwargs = {
107
+ "marker": [(-1, -d), (1, d)],
108
+ "markersize": 12,
109
+ "linestyle": "none",
110
+ "color": "k",
111
+ "mec": "k",
112
+ "mew": 1,
113
+ "clip_on": False,
114
+ }
115
+ ax_top.plot([0, 1], [0, 0], transform=ax_top.transAxes, **kwargs)
116
+ ax_bot.plot([0, 1], [1, 1], transform=ax_bot.transAxes, **kwargs)
117
+
118
+ ax_top.legend(loc="upper left")
119
+
120
+ fig.savefig("uncertainty_types.svg", bbox_inches="tight")
@@ -112,7 +112,7 @@ def cubehelix_palette(
112
112
  pal = cmap(x)[:, :3].tolist()
113
113
  if reverse:
114
114
  pal = pal[::-1]
115
- return pal
115
+ return [tuple(c) for c in pal]
116
116
 
117
117
 
118
118
  def get_color_palette(
@@ -170,7 +170,7 @@ def get_color_palette(
170
170
  )
171
171
 
172
172
  plt_cmap = plt.get_cmap(cmap)
173
- return plt_cmap(np.linspace(0, 1, N))
173
+ return [tuple(k) for k in plt_cmap(np.linspace(0, 1, N))]
174
174
 
175
175
 
176
176
  def set_fitting_ylabel_fontsize(ax: plt.Axes) -> float:
@@ -406,7 +406,9 @@ def plot_error_hist(
406
406
  if uncertainty_type == "symmetrical":
407
407
  kwargs.setdefault("yerr", np.sqrt(hist.variances()))
408
408
  else:
409
- uncertainties_low, uncertainties_high = get_asymmetrical_uncertainties(hist)
409
+ uncertainties_low, uncertainties_high = get_asymmetrical_uncertainties(
410
+ hist, uncertainty_type
411
+ )
410
412
  kwargs.setdefault("yerr", [uncertainties_low, uncertainties_high])
411
413
 
412
414
  kwargs.setdefault("fmt", ".")
@@ -4,6 +4,7 @@ import pytest
4
4
  from plothist.comparison import (
5
5
  _check_binning_consistency,
6
6
  _check_uncertainty_type,
7
+ get_asymmetrical_uncertainties,
7
8
  get_comparison,
8
9
  )
9
10
 
@@ -11,11 +12,20 @@ from plothist.comparison import (
11
12
  def test_check_uncertainty_type_invalid():
12
13
  with pytest.raises(
13
14
  ValueError,
14
- match="Uncertainty type invalid_type not valid. Must be 'symmetrical' or 'asymmetrical'.",
15
+ match="Uncertainty type invalid_type not valid. Must be in",
15
16
  ):
16
17
  _check_uncertainty_type("invalid_type")
17
18
 
18
19
 
20
+ def test_get_asymmetrical_uncertainties_invalid():
21
+ h1 = bh.Histogram(bh.axis.Regular(10, 0, 1))
22
+ with pytest.raises(
23
+ ValueError,
24
+ match="Invalid uncertainty type 'symmetrical' for asymmetrical uncertainties.",
25
+ ):
26
+ get_asymmetrical_uncertainties(h1, uncertainty_type="symmetrical")
27
+
28
+
19
29
  def test_check_binning_consistency_dimensionality():
20
30
  h1 = bh.Histogram(bh.axis.Regular(10, 0, 1))
21
31
  h2 = bh.Histogram(bh.axis.Regular(10, 0, 1), bh.axis.Regular(5, 0, 5))
@@ -99,7 +99,7 @@ def test_update_variable_registry_ranges() -> None:
99
99
  """
100
100
  Test variable registry range update.
101
101
  """
102
- df = get_dummy_data()
102
+ dummy_data = get_dummy_data()
103
103
 
104
104
  registry_path = "./_test_variable_registry_ranges.yaml"
105
105
 
@@ -109,7 +109,7 @@ def test_update_variable_registry_ranges() -> None:
109
109
  )
110
110
 
111
111
  with pytest.raises(RuntimeError) as err:
112
- update_variable_registry_ranges(df, variable_keys, path=registry_path)
112
+ update_variable_registry_ranges(dummy_data, variable_keys, path=registry_path)
113
113
  assert (
114
114
  str(err.value)
115
115
  == f"Variable {variable_keys[0]} does not have a name, bins or range property in the registry {registry_path}."
@@ -123,7 +123,7 @@ def test_update_variable_registry_ranges() -> None:
123
123
  )
124
124
 
125
125
  with pytest.raises(RuntimeError) as err:
126
- update_variable_registry_ranges(df, variable_keys, path=registry_path)
126
+ update_variable_registry_ranges(dummy_data, variable_keys, path=registry_path)
127
127
  assert (
128
128
  str(err.value)
129
129
  == f"Variable {variable_keys[0]} does not have a name, bins or range property in the registry {registry_path}."
@@ -137,7 +137,7 @@ def test_update_variable_registry_ranges() -> None:
137
137
  )
138
138
 
139
139
  with pytest.raises(RuntimeError) as err:
140
- update_variable_registry_ranges(df, variable_keys, path=registry_path)
140
+ update_variable_registry_ranges(dummy_data, variable_keys, path=registry_path)
141
141
  assert (
142
142
  str(err.value)
143
143
  == f"Variable {variable_keys[0]} does not have a name, bins or range property in the registry {registry_path}."
@@ -151,7 +151,7 @@ def test_update_variable_registry_ranges() -> None:
151
151
  )
152
152
 
153
153
  with pytest.raises(RuntimeError) as err:
154
- update_variable_registry_ranges(df, variable_keys, path=registry_path)
154
+ update_variable_registry_ranges(dummy_data, variable_keys, path=registry_path)
155
155
  assert (
156
156
  str(err.value)
157
157
  == f"Variable {variable_keys[0]} does not have a name, bins or range property in the registry {registry_path}."
@@ -160,7 +160,7 @@ def test_update_variable_registry_ranges() -> None:
160
160
  # Standard registry creation
161
161
  create_variable_registry(variable_keys, path=registry_path, reset=True)
162
162
 
163
- update_variable_registry_ranges(df, variable_keys, path=registry_path)
163
+ update_variable_registry_ranges(dummy_data, variable_keys, path=registry_path)
164
164
 
165
165
  for key in variable_keys:
166
166
  registry = get_variable_from_registry(key, path=registry_path)
@@ -183,14 +183,14 @@ def test_update_variable_registry_ranges() -> None:
183
183
  assert registry["range"] == (-1, 1)
184
184
 
185
185
  # Range values shouldn't be updated as overwrite=False
186
- update_variable_registry_ranges(df, ["variable_0"], path=registry_path)
186
+ update_variable_registry_ranges(dummy_data, ["variable_0"], path=registry_path)
187
187
 
188
188
  registry = get_variable_from_registry("variable_0", path=registry_path)
189
189
  assert registry["range"] == (-1, 1)
190
190
 
191
191
  # Range values should be updated as overwrite=True
192
192
  update_variable_registry_ranges(
193
- df, ["variable_0"], path=registry_path, overwrite=True
193
+ dummy_data, ["variable_0"], path=registry_path, overwrite=True
194
194
  )
195
195
 
196
196
  registry = get_variable_from_registry("variable_0", path=registry_path)
@@ -316,16 +316,16 @@ def test_update_variable_registry_ranges_all_keys() -> None:
316
316
  """
317
317
  Test update of variable registry with variable_keys=None
318
318
  """
319
- df = get_dummy_data()
319
+ dummy_data = get_dummy_data()
320
320
 
321
321
  registry_path = "./_test_variable_registry_update_all_keys.yaml"
322
322
 
323
323
  create_variable_registry(variable_keys, path=registry_path)
324
- update_variable_registry_ranges(df, variable_keys=None, path=registry_path)
324
+ update_variable_registry_ranges(dummy_data, variable_keys=None, path=registry_path)
325
325
 
326
326
  for key in variable_keys:
327
327
  variable = get_variable_from_registry(key, path=registry_path)
328
- assert pytest.approx(variable["range"][0]) == df[key].min()
329
- assert pytest.approx(variable["range"][1]) == df[key].max()
328
+ assert pytest.approx(variable["range"][0]) == dummy_data[key].min()
329
+ assert pytest.approx(variable["range"][1]) == dummy_data[key].max()
330
330
 
331
331
  os.remove(registry_path)
@@ -1,14 +0,0 @@
1
- .. _documentation-statistics-label:
2
-
3
- ===================
4
- Notes on statistics
5
- ===================
6
-
7
- The following urls are helpful to understand the statistical treatment of the uncertainties on the content of each bin of a histogram:
8
-
9
- - `Note about uncertainties when the histogram entries are weighted. <https://www.pp.rhul.ac.uk/~cowan/stat/notes/errors_with_weights.pdf>`_
10
- - `Presentation about asymmetrical uncertainties. <https://www.nikhef.nl/~ivov/Statistics/PoissonError/2017_05_15_PoissonError_LHCb_IvovanVulpen.pdf>`_
11
-
12
- The uncertainty on the efficiency (ratio of two histograms h1 and h2 when the entries of h1 are a subset of the entries of h2) is calculated following equation (19) of:
13
-
14
- - `T. Ullrich, Z. Xu, Treatment of Errors in Efficiency Calculations, arxiv: 0701199v1. <https://arxiv.org/pdf/physics/0701199v1.pdf>`_
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes