plothist 1.3.2__py3-none-any.whl → 1.5.0__py3-none-any.whl

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 (69) hide show
  1. plothist/__init__.py +66 -74
  2. plothist/_version.py +21 -0
  3. plothist/_version.pyi +2 -0
  4. plothist/comparison.py +115 -106
  5. plothist/examples/1d_hist/1d_comparison_asymmetry.py +37 -0
  6. plothist/examples/1d_hist/1d_comparison_difference.py +40 -0
  7. plothist/examples/1d_hist/1d_comparison_efficiency.py +37 -0
  8. plothist/examples/1d_hist/1d_comparison_only_efficiency.py +33 -0
  9. plothist/examples/1d_hist/1d_comparison_pull.py +37 -0
  10. plothist/examples/1d_hist/1d_comparison_ratio.py +37 -0
  11. plothist/examples/1d_hist/1d_comparison_relative_difference.py +37 -0
  12. plothist/examples/1d_hist/1d_comparison_split_ratio.py +37 -0
  13. plothist/examples/1d_hist/1d_elt1.py +38 -0
  14. plothist/examples/1d_hist/1d_elt1_stacked.py +45 -0
  15. plothist/examples/1d_hist/1d_elt2.py +33 -0
  16. plothist/examples/1d_hist/1d_hist_simple.py +28 -0
  17. plothist/examples/1d_hist/1d_int_category.py +41 -0
  18. plothist/examples/1d_hist/1d_profile.py +33 -0
  19. plothist/examples/1d_hist/1d_side_by_side.py +58 -0
  20. plothist/examples/1d_hist/1d_str_category.py +41 -0
  21. plothist/examples/1d_hist/README.rst +4 -0
  22. plothist/examples/2d_hist/2d_hist_correlations.py +65 -0
  23. plothist/examples/2d_hist/2d_hist_simple.py +28 -0
  24. plothist/examples/2d_hist/2d_hist_simple_discrete_colormap.py +42 -0
  25. plothist/examples/2d_hist/2d_hist_uneven.py +28 -0
  26. plothist/examples/2d_hist/2d_hist_with_projections.py +36 -0
  27. plothist/examples/2d_hist/README.rst +4 -0
  28. plothist/examples/README.rst +7 -0
  29. plothist/examples/advanced/1d_comparison_advanced.py +87 -0
  30. plothist/examples/advanced/1d_side_by_side_with_numbers.py +81 -0
  31. plothist/examples/advanced/README.rst +4 -0
  32. plothist/examples/advanced/asymmetry_comparison_advanced.py +133 -0
  33. plothist/examples/advanced/model_examples_flatten2D.py +86 -0
  34. plothist/examples/func_1d/README.rst +4 -0
  35. plothist/examples/func_1d/fct_1d.py +27 -0
  36. plothist/examples/func_1d/fct_1d_stacked.py +42 -0
  37. plothist/examples/model_ex/README.rst +4 -0
  38. plothist/examples/model_ex/model_all_comparisons.py +103 -0
  39. plothist/examples/model_ex/model_all_comparisons_no_model_unc.py +115 -0
  40. plothist/examples/model_ex/model_examples_pull.py +56 -0
  41. plothist/examples/model_ex/model_examples_pull_no_model_unc.py +59 -0
  42. plothist/examples/model_ex/model_examples_stacked.py +74 -0
  43. plothist/examples/model_ex/model_examples_stacked_unstacked.py +60 -0
  44. plothist/examples/model_ex/model_examples_unstacked.py +57 -0
  45. plothist/examples/model_ex/model_with_stacked_and_unstacked_function_components.py +50 -0
  46. plothist/examples/model_ex/model_with_stacked_and_unstacked_histograms_components.py +69 -0
  47. plothist/examples/model_ex/ratio_data_vs_model_with_stacked_and_unstacked_function_components.py +61 -0
  48. plothist/examples/utility/README.rst +4 -0
  49. plothist/examples/utility/add_text_example.py +39 -0
  50. plothist/examples/utility/color_palette_hists.py +94 -0
  51. plothist/examples/utility/color_palette_squares.py +100 -0
  52. plothist/examples/utility/matplotlib_vs_plothist_style.py +63 -0
  53. plothist/histogramming.py +77 -48
  54. plothist/plothist_style.py +61 -62
  55. plothist/plotters.py +280 -233
  56. plothist/test_helpers.py +43 -0
  57. plothist/variable_registry.py +62 -43
  58. {plothist-1.3.2.dist-info → plothist-1.5.0.dist-info}/METADATA +20 -12
  59. plothist-1.5.0.dist-info/RECORD +63 -0
  60. {plothist-1.3.2.dist-info → plothist-1.5.0.dist-info}/licenses/LICENSE +1 -1
  61. plothist/dummy_data.csv +0 -100001
  62. plothist/get_dummy_data.py +0 -17
  63. plothist/scripts/__init__.py +0 -2
  64. plothist/scripts/install_latin_modern_fonts.py +0 -145
  65. plothist/scripts/make_examples.py +0 -210
  66. plothist-1.3.2.dist-info/RECORD +0 -18
  67. plothist-1.3.2.dist-info/entry_points.txt +0 -3
  68. {plothist-1.3.2.dist-info → plothist-1.5.0.dist-info}/WHEEL +0 -0
  69. {plothist-1.3.2.dist-info → plothist-1.5.0.dist-info}/licenses/AUTHORS.md +0 -0
plothist/plotters.py CHANGED
@@ -1,52 +1,57 @@
1
- # -*- coding: utf-8 -*-
2
1
  """
3
2
  Collection of functions to plot histograms
4
3
  """
5
4
 
6
- import numpy as np
5
+ from __future__ import annotations
6
+
7
+ import re
8
+ from typing import Callable
9
+
7
10
  import boost_histogram as bh
8
11
  import matplotlib.pyplot as plt
12
+ import numpy as np
9
13
  from matplotlib.transforms import Bbox
10
- import re
14
+
11
15
  from plothist.comparison import (
12
- get_comparison,
13
- get_asymmetrical_uncertainties,
14
16
  _check_binning_consistency,
15
17
  _check_uncertainty_type,
18
+ get_asymmetrical_uncertainties,
19
+ get_comparison,
16
20
  )
17
- from plothist.histogramming import _make_hist_from_function, _check_counting_histogram
21
+ from plothist.histogramming import _check_counting_histogram, _make_hist_from_function
18
22
  from plothist.plothist_style import set_fitting_ylabel_fontsize
19
23
 
20
24
 
21
25
  def create_comparison_figure(
22
- figsize=(6, 5),
23
- nrows=2,
24
- gridspec_kw={"height_ratios": [4, 1]},
25
- hspace=0.15,
26
- ):
26
+ figsize: tuple[float, float] = (6, 5),
27
+ nrows: int = 2,
28
+ gridspec_kw: dict | None = None,
29
+ hspace: float = 0.15,
30
+ ) -> tuple[plt.Figure, np.ndarray]:
27
31
  """
28
32
  Create a figure with subplots for comparison.
29
33
 
30
34
  Parameters
31
35
  ----------
32
- figsize : tuple, optional
36
+ figsize : tuple[float, float], optional
33
37
  Figure size in inches. Default is (6, 5).
34
38
  nrows : int, optional
35
39
  Number of rows in the subplot grid. Default is 2.
36
- gridspec_kw : dict, optional
37
- Additional keyword arguments for the GridSpec. Default is {"height_ratios": [4, 1]}.
40
+ gridspec_kw : dict | None, optional
41
+ Additional keyword arguments for the GridSpec. Default is None.
42
+ If None is provided, this is set to {"height_ratios": [4, 1]}.
38
43
  hspace : float, optional
39
44
  Height spacing between subplots. Default is 0.15.
40
45
 
41
-
42
46
  Returns
43
47
  -------
44
48
  fig : matplotlib.figure.Figure
45
49
  The created figure.
46
- axes : ndarray
50
+ axes : np.ndarray
47
51
  Array of Axes objects representing the subplots.
48
-
49
52
  """
53
+ if gridspec_kw is None:
54
+ gridspec_kw = {"height_ratios": [4, 1]}
50
55
  if figsize is None:
51
56
  figsize = plt.rcParams["figure.figsize"]
52
57
 
@@ -60,13 +65,13 @@ def create_comparison_figure(
60
65
  return fig, axes
61
66
 
62
67
 
63
- def plot_hist(hist, ax, **kwargs):
68
+ def plot_hist(hist: bh.Histogram | list[bh.Histogram], ax: plt.Axes, **kwargs) -> None:
64
69
  """
65
70
  Plot a histogram or a list of histograms from boost_histogram.
66
71
 
67
72
  Parameters
68
73
  ----------
69
- hist : boost_histogram.Histogram or list of boost_histogram.Histogram
74
+ hist : bh.Histogram | list[bh.Histogram]
70
75
  The histogram(s) to plot.
71
76
  ax : matplotlib.axes.Axes
72
77
  The Axes instance for plotting.
@@ -95,34 +100,38 @@ def plot_hist(hist, ax, **kwargs):
95
100
 
96
101
 
97
102
  def plot_2d_hist(
98
- hist,
99
- fig=None,
100
- ax=None,
101
- ax_colorbar=None,
102
- pcolormesh_kwargs={},
103
- colorbar_kwargs={},
104
- square_ax=True,
105
- ):
103
+ hist: bh.Histogram,
104
+ fig: plt.Figure | None = None,
105
+ ax: plt.Axes | None = None,
106
+ ax_colorbar: plt.Axes | None = None,
107
+ pcolormesh_kwargs: dict | None = None,
108
+ colorbar_kwargs: dict | None = None,
109
+ square_ax: bool = True,
110
+ ) -> tuple[plt.Figure, plt.Axes, plt.Axes]:
106
111
  """
107
112
  Plot a 2D histogram using a pcolormesh plot and add a colorbar.
108
113
 
109
114
  Parameters
110
115
  ----------
111
- hist : boost_histogram.Histogram
116
+ hist : bh.Histogram
112
117
  The 2D histogram to plot.
113
- fig : matplotlib.figure.Figure, optional
114
- The Figure instance for plotting. If fig, ax and ax_colorbar are None, a new figure will be created. Default is None.
115
- ax : matplotlib.axes.Axes, optional
116
- The Axes instance for plotting. If fig, ax and ax_colorbar are None, a new figure will be created. Default is None.
117
- ax_colorbar : matplotlib.axes.Axes
118
- The Axes instance for the colorbar. If fig, ax and ax_colorbar are None, a new figure will be created. Default is None.
119
- pcolormesh_kwargs : dict, optional
120
- Additional keyword arguments forwarded to ax.pcolormesh() (default is {}).
121
- colorbar_kwargs : dict, optional
122
- Additional keyword arguments forwarded to ax.get_figure().colorbar() (default is {}).
118
+ fig : matplotlib.figure.Figure | None, optional
119
+ The Figure instance for plotting. If fig, ax and ax_colorbar are all None, a new figure will be created. Default is None.
120
+ ax : matplotlib.axes.Axes | None, optional
121
+ The Axes instance for plotting. If fig, ax and ax_colorbar are all None, a new figure will be created. Default is None.
122
+ ax_colorbar : matplotlib.axes.Axes | None, optional
123
+ The Axes instance for the colorbar. If fig, ax and ax_colorbar are all None, a new figure will be created. Default is None.
124
+ pcolormesh_kwargs : dict | None, optional
125
+ Additional keyword arguments forwarded to ax.pcolormesh(). Default is None.
126
+ colorbar_kwargs : dict | None, optional
127
+ Additional keyword arguments forwarded to ax.get_figure().colorbar(). Default is None.
123
128
  square_ax : bool, optional
124
- Whether to make the main ax square (default is True).
129
+ Whether to make the main ax square. Default is True.
125
130
  """
131
+ if colorbar_kwargs is None:
132
+ colorbar_kwargs = {}
133
+ if pcolormesh_kwargs is None:
134
+ pcolormesh_kwargs = {}
126
135
  # Create copies of the kwargs arguments passed as lists/dicts to avoid modifying them
127
136
  pcolormesh_kwargs = pcolormesh_kwargs.copy()
128
137
  colorbar_kwargs = colorbar_kwargs.copy()
@@ -146,7 +155,7 @@ def plot_2d_hist(
146
155
  return fig, ax, ax_colorbar
147
156
 
148
157
 
149
- def _invert_collection_order(ax, n=0):
158
+ def _invert_collection_order(ax: plt.Axes, n: int = 0) -> None:
150
159
  """
151
160
  Invert the order of the collection objects in an Axes instance.
152
161
 
@@ -156,7 +165,6 @@ def _invert_collection_order(ax, n=0):
156
165
  The Axes instance for plotting.
157
166
  n : int, optional
158
167
  The number of collections to keep in the original order. Default is 0.
159
-
160
168
  """
161
169
  # Retrieve the list of collection objects
162
170
  collections = list(ax.collections)
@@ -173,17 +181,23 @@ def _invert_collection_order(ax, n=0):
173
181
  ax.add_collection(collection)
174
182
 
175
183
 
176
- def plot_function(func, range, ax, stacked=False, npoints=1000, **kwargs):
184
+ def plot_function(
185
+ func: Callable[[np.ndarray], np.ndarray] | list[Callable[[np.ndarray], np.ndarray]],
186
+ range: tuple[float, float],
187
+ ax: plt.Axes,
188
+ stacked: bool = False,
189
+ npoints: int = 1000,
190
+ **kwargs,
191
+ ) -> None:
177
192
  """
178
193
  Plot a 1D function on a given range.
179
194
 
180
195
  Parameters
181
196
  ----------
182
- func : function or list of functions
183
- The 1D function or list of functions to plot.
184
- The function(s) should support vectorization (i.e. accept a numpy array as input).
185
- range : tuple
186
- The range of the function(s). The function(s) will be plotted on the interval [range[0], range[1]].
197
+ func : Callable[[np.ndarray], np.ndarray] | list[Callable[[np.ndarray], np.ndarray]]
198
+ The 1D function or list of functions to plot. Should support vectorization.
199
+ range : tuple[float, float]
200
+ The range of the function(s). Will be plotted on the interval [range[0], range[1]].
187
201
  ax : matplotlib.axes.Axes
188
202
  The Axes instance for plotting.
189
203
  stacked : bool, optional
@@ -205,7 +219,7 @@ def plot_function(func, range, ax, stacked=False, npoints=1000, **kwargs):
205
219
  **kwargs,
206
220
  )
207
221
  else:
208
- if kwargs.get("labels", None) is None:
222
+ if kwargs.get("labels") is None:
209
223
  kwargs["labels"] = []
210
224
 
211
225
  if not isinstance(func, list):
@@ -221,43 +235,44 @@ def plot_function(func, range, ax, stacked=False, npoints=1000, **kwargs):
221
235
 
222
236
 
223
237
  def plot_2d_hist_with_projections(
224
- hist,
225
- xlabel=None,
226
- ylabel=None,
227
- ylabel_x_projection=None,
228
- xlabel_y_projection=None,
229
- colorbar_label=None,
230
- offset_x_labels=False,
231
- pcolormesh_kwargs={},
232
- colorbar_kwargs={},
233
- plot_hist_kwargs={},
234
- figsize=(6, 6),
235
- ):
236
- """Plot a 2D histogram with projections on the x and y axes.
238
+ hist: bh.Histogram,
239
+ xlabel: str | None = None,
240
+ ylabel: str | None = None,
241
+ ylabel_x_projection: str | None = None,
242
+ xlabel_y_projection: str | None = None,
243
+ colorbar_label: str | None = None,
244
+ offset_x_labels: bool = False,
245
+ pcolormesh_kwargs: dict | None = None,
246
+ colorbar_kwargs: dict | None = None,
247
+ plot_hist_kwargs: dict | None = None,
248
+ figsize: tuple[float, float] = (6, 6),
249
+ ) -> tuple[plt.Figure, plt.Axes, plt.Axes, plt.Axes, plt.Axes]:
250
+ """
251
+ Plot a 2D histogram with projections on the x and y axes.
237
252
 
238
253
  Parameters
239
254
  ----------
240
- hist : 2D boost_histogram.Histogram
255
+ hist : bh.Histogram
241
256
  The 2D histogram to plot.
242
- xlabel : str, optional
257
+ xlabel : str | None, optional
243
258
  Label for the x axis. Default is None.
244
- ylabel : str, optional
259
+ ylabel : str | None, optional
245
260
  Label for the y axis. Default is None.
246
- ylabel_x_projection : str, optional
261
+ ylabel_x_projection : str | None, optional
247
262
  Label for the y axis of the x projection. Default is None.
248
- xlabel_y_projection : str, optional
263
+ xlabel_y_projection : str | None, optional
249
264
  Label for the x axis of the y projection. Default is None.
250
- colorbar_label : str, optional
265
+ colorbar_label : str | None, optional
251
266
  Label for the colorbar. Default is None.
252
267
  offset_x_labels : bool, optional
253
268
  Whether to offset the x labels to avoid overlapping with the exponent label (i.e. "10^X") of the axis. Default is False.
254
- pcolormesh_kwargs : dict, optional
255
- Keyword arguments for the pcolormesh call. Default is {}.
256
- colorbar_kwargs : dict, optional
257
- Keyword arguments for the colorbar call. Default is {}.
258
- plot_hist_kwargs : dict, optional
259
- Keyword arguments for the plot_hist call (x and y projections). Default is {}.
260
- figsize : tuple, optional
269
+ pcolormesh_kwargs : dict | None, optional
270
+ Keyword arguments for the pcolormesh call. Default is None.
271
+ colorbar_kwargs : dict | None, optional
272
+ Keyword arguments for the colorbar call. Default is None.
273
+ plot_hist_kwargs : dict | None, optional
274
+ Keyword arguments for the plot_hist call (x and y projections). Default is None.
275
+ figsize : tuple[float, float], optional
261
276
  Figure size in inches. Default is (6, 6). To get square bins if the figure is not square shaped, be sure to set the bins and the ranges of the histogram according to the ratio of the figure width and height.
262
277
 
263
278
  Returns
@@ -273,6 +288,12 @@ def plot_2d_hist_with_projections(
273
288
  ax_colorbar : matplotlib.axes.Axes
274
289
  The axes for the colorbar.
275
290
  """
291
+ if plot_hist_kwargs is None:
292
+ plot_hist_kwargs = {}
293
+ if colorbar_kwargs is None:
294
+ colorbar_kwargs = {}
295
+ if pcolormesh_kwargs is None:
296
+ pcolormesh_kwargs = {}
276
297
  _check_counting_histogram(hist)
277
298
 
278
299
  # Create copies of the kwargs arguments passed as lists/dicts to avoid modifying them
@@ -332,10 +353,7 @@ def plot_2d_hist_with_projections(
332
353
  ax_2d.set_ylim(ylim)
333
354
  ax_y_projection.set_ylim(ylim)
334
355
 
335
- if offset_x_labels:
336
- labelpad = 20
337
- else:
338
- labelpad = None
356
+ labelpad = 20 if offset_x_labels else None
339
357
 
340
358
  ax_2d.set_xlabel(xlabel, labelpad=labelpad)
341
359
  ax_2d.set_ylabel(ylabel)
@@ -351,13 +369,19 @@ def plot_2d_hist_with_projections(
351
369
  return fig, ax_2d, ax_x_projection, ax_y_projection, ax_colorbar
352
370
 
353
371
 
354
- def plot_error_hist(hist, ax, uncertainty_type="symmetrical", density=False, **kwargs):
372
+ def plot_error_hist(
373
+ hist: bh.Histogram,
374
+ ax: plt.Axes,
375
+ uncertainty_type: str = "symmetrical",
376
+ density: bool = False,
377
+ **kwargs,
378
+ ) -> None:
355
379
  """
356
380
  Create an errorbar plot from a boost histogram.
357
381
 
358
382
  Parameters
359
383
  ----------
360
- hist : boost_histogram.Histogram
384
+ hist : bh.Histogram
361
385
  The histogram to plot.
362
386
  ax : matplotlib.axes.Axes
363
387
  The Axes instance for plotting.
@@ -394,13 +418,13 @@ def plot_error_hist(hist, ax, uncertainty_type="symmetrical", density=False, **k
394
418
  )
395
419
 
396
420
 
397
- def plot_hist_uncertainties(hist, ax, **kwargs):
421
+ def plot_hist_uncertainties(hist: bh.Histogram, ax: plt.Axes, **kwargs) -> None:
398
422
  """
399
423
  Plot the symmetrical uncertainty, which is the Poisson standard deviation derived from the variance stored in the histogram, as a hatched area.
400
424
 
401
425
  Parameters
402
426
  ----------
403
- hist : boost_histogram.Histogram
427
+ hist : bh.Histogram
404
428
  The histogram from which we want to plot the uncertainties.
405
429
  ax : matplotlib.axes.Axes
406
430
  The Axes instance for plotting.
@@ -426,40 +450,40 @@ def plot_hist_uncertainties(hist, ax, **kwargs):
426
450
 
427
451
 
428
452
  def plot_two_hist_comparison(
429
- h1,
430
- h2,
431
- xlabel=None,
432
- ylabel=None,
433
- h1_label="h1",
434
- h2_label="h2",
435
- fig=None,
436
- ax_main=None,
437
- ax_comparison=None,
453
+ h1: bh.Histogram,
454
+ h2: bh.Histogram,
455
+ xlabel: str | None = None,
456
+ ylabel: str | None = None,
457
+ h1_label: str = "h1",
458
+ h2_label: str = "h2",
459
+ fig: plt.Figure | None = None,
460
+ ax_main: plt.Axes | None = None,
461
+ ax_comparison: plt.Axes | None = None,
438
462
  **comparison_kwargs,
439
- ):
463
+ ) -> tuple[plt.Figure, plt.Axes, plt.Axes]:
440
464
  """
441
465
  Compare two histograms.
442
466
 
443
467
  Parameters
444
468
  ----------
445
- h1 : boost_histogram.Histogram
469
+ h1 : bh.Histogram
446
470
  The first histogram to compare.
447
- h2 : boost_histogram.Histogram
471
+ h2 : bh.Histogram
448
472
  The second histogram to compare.
449
- xlabel : str, optional
473
+ xlabel : str | None, optional
450
474
  The label for the x-axis. Default is None.
451
- ylabel : str, optional
475
+ ylabel : str | None, optional
452
476
  The label for the y-axis. Default is None.
453
477
  h1_label : str, optional
454
478
  The label for the first histogram. Default is "h1".
455
479
  h2_label : str, optional
456
480
  The label for the second histogram. Default is "h2".
457
- fig : matplotlib.figure.Figure or None, optional
458
- The figure to use for the plot. If fig, ax_main and ax_comparison are None, a new figure will be created. Default is None.
459
- ax_main : matplotlib.axes.Axes or None, optional
460
- The main axes for the histogram comparison. If fig, ax_main and ax_comparison are None, a new axes will be created. Default is None.
461
- ax_comparison : matplotlib.axes.Axes or None, optional
462
- The axes for the comparison plot. If fig, ax_main and ax_comparison are None, a new axes will be created. Default is None.
481
+ fig : matplotlib.figure.Figure | None, optional
482
+ The figure to use for the plot. If fig, ax_main and ax_comparison are all None, a new figure will be created. Default is None.
483
+ ax_main : matplotlib.axes.Axes | None, optional
484
+ The main axes for the histogram comparison. If fig, ax_main and ax_comparison are all None, a new figure will be created. Default is None.
485
+ ax_comparison : matplotlib.axes.Axes | None, optional
486
+ The axes for the comparison plot. If fig, ax_main and ax_comparison are all None, a new figure will be created. Default is None.
463
487
  **comparison_kwargs : optional
464
488
  Arguments to be passed to plot_comparison(), including the choice of the comparison function and the treatment of the uncertainties (see documentation of plot_comparison() for details).
465
489
 
@@ -514,16 +538,16 @@ def plot_two_hist_comparison(
514
538
 
515
539
 
516
540
  def plot_comparison(
517
- h1,
518
- h2,
519
- ax,
520
- xlabel="",
521
- h1_label="h1",
522
- h2_label="h2",
523
- comparison="ratio",
524
- comparison_ylabel=None,
525
- comparison_ylim=None,
526
- h1_uncertainty_type="symmetrical",
541
+ h1: bh.Histogram,
542
+ h2: bh.Histogram,
543
+ ax: plt.Axes,
544
+ xlabel: str | None = None,
545
+ h1_label: str = "h1",
546
+ h2_label: str = "h2",
547
+ comparison: str = "ratio",
548
+ comparison_ylabel: str | None = None,
549
+ comparison_ylim: tuple[float, float] | None = None,
550
+ h1_uncertainty_type: str = "symmetrical",
527
551
  **plot_hist_kwargs,
528
552
  ):
529
553
  """
@@ -531,14 +555,14 @@ def plot_comparison(
531
555
 
532
556
  Parameters
533
557
  ----------
534
- h1 : boost_histogram.Histogram
558
+ h1 : bh.Histogram
535
559
  The first histogram for comparison.
536
- h2 : boost_histogram.Histogram
560
+ h2 : bh.Histogram
537
561
  The second histogram for comparison.
538
562
  ax : matplotlib.axes.Axes
539
563
  The axes to plot the comparison.
540
- xlabel : str, optional
541
- The label for the x-axis. Default is "".
564
+ xlabel : str | None, optional
565
+ The label for the x-axis. Default is None.
542
566
  h1_label : str, optional
543
567
  The label for the first histogram. Default is "h1".
544
568
  h2_label : str, optional
@@ -546,10 +570,10 @@ def plot_comparison(
546
570
  comparison : str, optional
547
571
  The type of comparison to plot ("ratio", "split_ratio", "pull", "difference", "relative_difference", "efficiency", or "asymmetry"). Default is "ratio".
548
572
  When the `split_ratio` option is used, both the h1 and h2 uncertainties are scaled down by the h2 bin contents, and the h2 adjusted uncertainties are shown separately as a hatched area.
549
- comparison_ylabel : str, optional
550
- The label for the y-axis. Default is the explicit formula used to compute the comparison plot.
551
- comparison_ylim : tuple or None, optional
552
- The y-axis limits for the comparison plot. Default is None. If None, standard y-axis limits are setup.
573
+ comparison_ylabel : str | None, optional
574
+ The label for the y-axis. If None, the label is the explicit formula used to compute the comparison plot. Default is None.
575
+ comparison_ylim : tuple[float, float] | None, optional
576
+ The y-axis limits for the comparison plot. If None, standard y-axis limits are setup. Default is None.
553
577
  h1_uncertainty_type : str, optional
554
578
  What kind of bin uncertainty to use for h1: "symmetrical" for the Poisson standard deviation derived from the variance stored in the histogram object, "asymmetrical" for asymmetrical uncertainties based on a Poisson confidence interval. Default is "symmetrical".
555
579
  Asymmetrical uncertainties are not supported for the asymmetry and efficiency comparisons.
@@ -564,7 +588,6 @@ def plot_comparison(
564
588
  See Also
565
589
  --------
566
590
  plot_two_hist_comparison : Compare two histograms and plot the comparison.
567
-
568
591
  """
569
592
 
570
593
  h1_label = _get_math_text(h1_label)
@@ -613,13 +636,12 @@ def plot_comparison(
613
636
  ax.set_ylabel(r"$\frac{" + h1_label + "}{" + h2_label + "}$")
614
637
 
615
638
  if comparison == "split_ratio":
616
- np.seterr(divide="ignore", invalid="ignore")
617
- h2_scaled_uncertainties = np.where(
618
- h2.values() != 0,
619
- np.sqrt(h2.variances()) / h2.values(),
620
- np.nan,
621
- )
622
- np.seterr(divide="warn", invalid="warn")
639
+ with np.errstate(divide="ignore", invalid="ignore"):
640
+ h2_scaled_uncertainties = np.where(
641
+ h2.values() != 0,
642
+ np.sqrt(h2.variances()) / h2.values(),
643
+ np.nan,
644
+ )
623
645
  ax.bar(
624
646
  x=h2.axes[0].centers,
625
647
  bottom=np.nan_to_num(
@@ -670,7 +692,9 @@ def plot_comparison(
670
692
  return ax
671
693
 
672
694
 
673
- def savefig(fig, path, new_figsize=None):
695
+ def savefig(
696
+ fig: plt.Figure, path: str, new_figsize: tuple[float, float] | None = None
697
+ ) -> None:
674
698
  """
675
699
  Save a Matplotlib figure with consistent figsize, axes size and subplot spacing (experimental feature).
676
700
 
@@ -680,8 +704,8 @@ def savefig(fig, path, new_figsize=None):
680
704
  The Matplotlib figure to be saved.
681
705
  path : str
682
706
  The output file path where the figure will be saved.
683
- new_figsize : tuple, optional
684
- The new figsize as a (width, height) tuple. If None, the original figsize is preserved.
707
+ new_figsize : tuple[float, float] | None, optional
708
+ The new figsize as a (width, height) tuple. If None, the original figsize is preserved. Default is None.
685
709
 
686
710
  Returns
687
711
  -------
@@ -721,7 +745,7 @@ def savefig(fig, path, new_figsize=None):
721
745
  fig.savefig(path)
722
746
 
723
747
 
724
- def _get_math_text(text):
748
+ def _get_math_text(text: str) -> str:
725
749
  """
726
750
  Search for text between $ and return it.
727
751
 
@@ -738,11 +762,10 @@ def _get_math_text(text):
738
762
  match = re.search(r"\$(.*?)\$", text)
739
763
  if match:
740
764
  return match.group(1)
741
- else:
742
- return text
765
+ return text
743
766
 
744
767
 
745
- def _get_model_type(components):
768
+ def _get_model_type(components: list) -> str:
746
769
  """
747
770
  Check that all components of a model are either all histograms or all functions
748
771
  and return the type of the model components.
@@ -764,71 +787,71 @@ def _get_model_type(components):
764
787
  """
765
788
  if all(isinstance(x, bh.Histogram) for x in components):
766
789
  return "histograms"
767
- elif all(callable(x) for x in components):
790
+ if all(callable(x) for x in components):
768
791
  return "functions"
769
- else:
770
- raise ValueError("All model components must be either histograms or functions.")
792
+ raise ValueError("All model components must be either histograms or functions.")
771
793
 
772
794
 
773
795
  def plot_model(
774
- stacked_components=[],
775
- stacked_labels=None,
776
- stacked_colors=None,
777
- unstacked_components=[],
778
- unstacked_labels=None,
779
- unstacked_colors=None,
780
- xlabel=None,
781
- ylabel=None,
782
- stacked_kwargs={},
783
- unstacked_kwargs_list=[],
784
- model_sum_kwargs={"show": True, "label": "Model", "color": "navy"},
785
- function_range=None,
786
- model_uncertainty=True,
787
- model_uncertainty_label="Model stat. unc.",
788
- fig=None,
789
- ax=None,
790
- ):
796
+ stacked_components: list[bh.Histogram] | None = None,
797
+ stacked_labels: list[str] | None = None,
798
+ stacked_colors: list[str] | list[tuple[float, float, float]] | None = None,
799
+ unstacked_components: list[bh.Histogram] | None = None,
800
+ unstacked_labels: list[str] | list[None] | None = None,
801
+ unstacked_colors: (
802
+ list[str] | list[tuple[float, float, float]] | list[None] | None
803
+ ) = None,
804
+ xlabel: str | None = None,
805
+ ylabel: str | None = None,
806
+ stacked_kwargs: dict | None = None,
807
+ unstacked_kwargs_list: list[dict] | None = None,
808
+ model_sum_kwargs: dict | None = None,
809
+ function_range: tuple[float, float] | None = None,
810
+ model_uncertainty: bool = True,
811
+ model_uncertainty_label: str = "Model stat. unc.",
812
+ fig: plt.Figure | None = None,
813
+ ax: plt.Axes | None = None,
814
+ ) -> tuple[plt.Figure, plt.Axes]:
791
815
  """
792
816
  Plot model made of a collection of histograms.
793
817
 
794
818
  Parameters
795
819
  ----------
796
- stacked_components : list of boost_histogram.Histogram, optional
797
- The list of histograms to be stacked composing the model. Default is [].
798
- stacked_labels : list of str, optional
820
+ stacked_components : list[bh.Histogram] | None, optional
821
+ The list of histograms to be stacked composing the model. Default is None.
822
+ stacked_labels : list[str] | None, optional
799
823
  The labels of the model stacked components. Default is None.
800
- stacked_colors : list of str, optional
824
+ stacked_colors : list[str] | None, optional
801
825
  The colors of the model stacked components. Default is None.
802
- unstacked_components : list of boost_histogram.Histogram, optional
803
- The list of histograms not to be stacked composing the model. Default is [].
804
- unstacked_labels : list of str, optional
826
+ unstacked_components : list[bh.Histogram] | None, optional
827
+ The list of histograms not to be stacked composing the model. Default is None.
828
+ unstacked_labels : list[str] | list[None] | None, optional
805
829
  The labels of the model unstacked components. Default is None.
806
- unstacked_colors : list of str, optional
830
+ unstacked_colors : list[str] | list[tuple[float, float, float]] | list[None] | None, optional
807
831
  The colors of the model unstacked components. Default is None.
808
- xlabel : str, optional
832
+ xlabel : str | None, optional
809
833
  The label for the x-axis. Default is None.
810
- ylabel : str, optional
834
+ ylabel : str | None, optional
811
835
  The label for the y-axis. Default is None.
812
- stacked_kwargs : dict, optional
813
- The keyword arguments used when plotting the stacked components in plot_hist() or plot_function(), one of which is called only once. Default is {}.
814
- unstacked_kwargs_list : list of dict, optional
815
- The list of keyword arguments used when plotting the unstacked components in plot_hist() or plot_function(), one of which is called once for each unstacked component. Default is [].
816
- model_sum_kwargs : dict, optional
836
+ stacked_kwargs : dict | None, optional
837
+ The keyword arguments used when plotting the stacked components in plot_hist() or plot_function(), one of which is called only once. Default is None.
838
+ unstacked_kwargs_list : list[dict] | None, optional
839
+ The list of keyword arguments used when plotting the unstacked components in plot_hist() or plot_function(), one of which is called once for each unstacked component. Default is None.
840
+ model_sum_kwargs : dict | None, optional
817
841
  The keyword arguments for the plot_hist() function for the sum of the model components.
818
842
  Has no effect if all the model components are stacked or if the model is one unstacked element.
819
843
  The special keyword "show" can be used with a boolean to specify whether to show or not the sum of the model components.
820
- Default is {"show": True, "label": "Model", "color": "navy"}.
821
- function_range : tuple, optional (mandatory if the model is made of functions)
822
- The range for the x-axis if the model is made of functions.
844
+ Default is None. If None is provided, this is set to {"show": True, "label": "Model", "color": "navy"}.
845
+ function_range : tuple[float, float] | None, optional (mandatory if the model is made of functions)
846
+ The range for the x-axis if the model is made of functions. Default is None.
823
847
  model_uncertainty : bool, optional
824
848
  If False, set the model uncertainties to zeros. Default is True.
825
849
  model_uncertainty_label : str, optional
826
850
  The label for the model uncertainties. Default is "Model stat. unc.".
827
- fig : matplotlib.figure.Figure or None, optional
828
- The Figure object to use for the plot. Create a new one if none is provided.
829
- ax : matplotlib.axes.Axes or None, optional
830
- The Axes object to use for the plot. Create a new one if none is provided.
831
-
851
+ fig : matplotlib.figure.Figure | None, optional
852
+ The Figure object to use for the plot. If fig and ax are all None, a new figure will be created. Default is None.
853
+ ax : matplotlib.axes.Axes | None, optional
854
+ The Axes object to use for the plot. If fig and ax are all None, a new figure will be created. Default is None.
832
855
 
833
856
  Returns
834
857
  -------
@@ -836,9 +859,19 @@ def plot_model(
836
859
  The Figure object containing the plot.
837
860
  ax : matplotlib.axes.Axes
838
861
  The Axes object containing the plot.
839
-
840
862
  """
841
863
 
864
+ if model_sum_kwargs is None:
865
+ model_sum_kwargs = {"show": True, "label": "Model", "color": "navy"}
866
+ if unstacked_kwargs_list is None:
867
+ unstacked_kwargs_list = []
868
+ if stacked_kwargs is None:
869
+ stacked_kwargs = {}
870
+ if unstacked_components is None:
871
+ unstacked_components = []
872
+ if stacked_components is None:
873
+ stacked_components = []
874
+
842
875
  # Create copies of the kwargs arguments passed as lists/dicts to avoid modifying them
843
876
  stacked_kwargs = stacked_kwargs.copy()
844
877
  unstacked_kwargs_list = unstacked_kwargs_list.copy()
@@ -979,78 +1012,79 @@ def plot_model(
979
1012
 
980
1013
 
981
1014
  def plot_data_model_comparison(
982
- data_hist,
983
- stacked_components=[],
984
- stacked_labels=None,
985
- stacked_colors=None,
986
- unstacked_components=[],
987
- unstacked_labels=None,
988
- unstacked_colors=None,
989
- xlabel=None,
990
- ylabel=None,
991
- data_label="Data",
992
- stacked_kwargs={},
993
- unstacked_kwargs_list=[],
994
- model_sum_kwargs={"show": True, "label": "Sum", "color": "navy"},
995
- model_uncertainty=True,
996
- model_uncertainty_label="Model stat. unc.",
997
- data_uncertainty_type="asymmetrical",
998
- fig=None,
999
- ax_main=None,
1000
- ax_comparison=None,
1001
- plot_only=None,
1015
+ data_hist: bh.Histogram,
1016
+ stacked_components: list[bh.Histogram] | None = None,
1017
+ stacked_labels: list[str] | None = None,
1018
+ stacked_colors: list[str] | list[tuple[float, float, float]] | None = None,
1019
+ unstacked_components: list[bh.Histogram] | None = None,
1020
+ unstacked_labels: list[str] | None = None,
1021
+ unstacked_colors: list[str] | list[tuple[float, float, float]] | None = None,
1022
+ xlabel: str | None = None,
1023
+ ylabel: str | None = None,
1024
+ data_label: str = "Data",
1025
+ stacked_kwargs: dict | None = None,
1026
+ unstacked_kwargs_list: list[dict] | None = None,
1027
+ model_sum_kwargs: dict | None = None,
1028
+ model_uncertainty: bool = True,
1029
+ model_uncertainty_label: str = "Model stat. unc.",
1030
+ data_uncertainty_type: str = "asymmetrical",
1031
+ fig: plt.Figure | None = None,
1032
+ ax_main: plt.Axes | None = None,
1033
+ ax_comparison: plt.Axes | None = None,
1034
+ plot_only: str | None = None,
1002
1035
  **comparison_kwargs,
1003
- ):
1036
+ ) -> tuple[plt.Figure, plt.Axes, plt.Axes]:
1004
1037
  """
1005
1038
  Compare data to model. The data uncertainties are computed using the Poisson confidence interval.
1006
1039
 
1007
1040
  Parameters
1008
1041
  ----------
1009
- data_hist : boost_histogram.Histogram
1042
+ data_hist : bh.Histogram
1010
1043
  The histogram for the data.
1011
- stacked_components : list of boost_histogram.Histogram, optional
1012
- The list of histograms to be stacked composing the model. Default is [].
1013
- stacked_labels : list of str, optional
1044
+ stacked_components : list[bh.Histogram] | None, optional
1045
+ The list of histograms to be stacked composing the model. Default is None.
1046
+ stacked_labels : list[str] | None, optional
1014
1047
  The labels of the model stacked components. Default is None.
1015
- stacked_colors : list of str, optional
1048
+ stacked_colors : list[str] | None, optional
1016
1049
  The colors of the model stacked components. Default is None.
1017
- unstacked_components : list of boost_histogram.Histogram, optional
1018
- The list of histograms not to be stacked composing the model. Default is [].
1019
- unstacked_labels : list of str, optional
1050
+ unstacked_components : list[bh.Histogram] | None, optional
1051
+ The list of histograms not to be stacked composing the model. Default is None.
1052
+ unstacked_labels : list[str] | None, optional
1020
1053
  The labels of the model unstacked components. Default is None.
1021
- unstacked_colors : list of str, optional
1054
+ unstacked_colors : list[str] | None, optional
1022
1055
  The colors of the model unstacked components. Default is None.
1023
- xlabel : str, optional
1056
+ xlabel : str | None, optional
1024
1057
  The label for the x-axis. Default is None.
1025
- ylabel : str, optional
1058
+ ylabel : str | None, optional
1026
1059
  The label for the y-axis. Default is None.
1027
1060
  data_label : str, optional
1028
1061
  The label for the data. Default is "Data".
1029
- stacked_kwargs : dict, optional
1030
- The keyword arguments used when plotting the stacked components in plot_hist() or plot_function(), one of which is called only once. Default is {}.
1031
- unstacked_kwargs_list : list of dict, optional
1032
- The list of keyword arguments used when plotting the unstacked components in plot_hist() or plot_function(), one of which is called once for each unstacked component. Default is [].
1033
- model_sum_kwargs : dict, optional
1062
+ stacked_kwargs : dict | None, optional
1063
+ The keyword arguments used when plotting the stacked components in plot_hist() or plot_function(), one of which is called only once. Default is None.
1064
+ unstacked_kwargs_list : list[dict] | None, optional
1065
+ The list of keyword arguments used when plotting the unstacked components in plot_hist() or plot_function(), one of which is called once for each unstacked component. Default is None.
1066
+ model_sum_kwargs : dict | None, optional
1034
1067
  The keyword arguments for the plot_hist() function for the sum of the model components.
1035
1068
  Has no effect if all the model components are stacked or if the model is one unstacked element.
1036
1069
  The special keyword "show" can be used with a boolean to specify whether to show or not the sum of the model components.
1037
- Default is {"show": True, "label": "Sum", "color": "navy"}.
1070
+ Default is None. If None is provided, this is set to {"show": True, "label": "Sum", "color": "navy"}.
1038
1071
  model_uncertainty : bool, optional
1039
1072
  If False, set the model uncertainties to zeros. Default is True.
1040
1073
  model_uncertainty_label : str, optional
1041
1074
  The label for the model uncertainties. Default is "Model stat. unc.".
1042
1075
  data_uncertainty_type : str, optional
1043
1076
  What kind of bin uncertainty to use for data_hist: "symmetrical" for the Poisson standard deviation derived from the variance stored in the histogram object, "asymmetrical" for asymmetrical uncertainties based on a Poisson confidence interval. Default is "asymmetrical".
1044
- fig : matplotlib.figure.Figure or None, optional
1045
- The figure to use for the plot. If fig, ax_main and ax_comparison are None, a new figure will be created. Default is None.
1046
- ax_main : matplotlib.axes.Axes or None, optional
1047
- The main axes for the histogram comparison. If fig, ax_main and ax_comparison are None, a new axes will be created. Default is None.
1048
- ax_comparison : matplotlib.axes.Axes or None, optional
1049
- The axes for the comparison plot. If fig, ax_main and ax_comparison are None, a new axes will be created. Default is None.
1050
- plot_only : str, optional
1077
+ fig : matplotlib.figure.Figure | None, optional
1078
+ The figure to use for the plot. If fig, ax_main and ax_comparison are all None, a new figure will be created. Default is None.
1079
+ ax_main : matplotlib.axes.Axes | None, optional
1080
+ The main axes for the histogram comparison. If fig, ax_main and ax_comparison are all None, a new figure will be created. Default is None.
1081
+ ax_comparison : matplotlib.axes.Axes | None, optional
1082
+ The axes for the comparison plot. If fig, ax_main and ax_comparison are all None, a new figure will be created. Default is None.
1083
+ plot_only : str | None, optional
1051
1084
  If "ax_main" or "ax_comparison", only the main or comparison axis is plotted on the figure. Both axes are plotted if None is specified, which is the default. This can only be used when fig, ax_main and ax_comparison are not provided by the user.
1052
1085
  **comparison_kwargs : optional
1053
- Arguments to be passed to plot_comparison(), including the choice of the comparison function and the treatment of the uncertainties (see documentation of plot_comparison() for details). If they are not provided explicitly, the following arguments are passed by default: h1_label="Data", h2_label="Pred.", comparison="split_ratio".
1086
+ Arguments to be passed to plot_comparison(), including the choice of the comparison function and the treatment of the uncertainties (see documentation of plot_comparison() for details).
1087
+ If they are not provided explicitly, the following arguments are passed by default: h1_label="Data", h2_label="Pred.", comparison="split_ratio".
1054
1088
 
1055
1089
  Returns
1056
1090
  -------
@@ -1066,6 +1100,22 @@ def plot_data_model_comparison(
1066
1100
  plot_comparison : Plot the comparison between two histograms.
1067
1101
 
1068
1102
  """
1103
+ if model_sum_kwargs is None:
1104
+ model_sum_kwargs = {"show": True, "label": "Sum", "color": "navy"}
1105
+ if unstacked_kwargs_list is None:
1106
+ unstacked_kwargs_list = []
1107
+ if stacked_kwargs is None:
1108
+ stacked_kwargs = {}
1109
+ if unstacked_components is None:
1110
+ unstacked_components = []
1111
+ if stacked_components is None:
1112
+ stacked_components = []
1113
+
1114
+ # Create copies of the kwargs arguments passed as lists/dicts to avoid modifying them
1115
+ stacked_kwargs = stacked_kwargs.copy()
1116
+ unstacked_kwargs_list = unstacked_kwargs_list.copy()
1117
+ model_sum_kwargs = model_sum_kwargs.copy()
1118
+
1069
1119
  comparison_kwargs.setdefault("h1_label", data_label)
1070
1120
  comparison_kwargs.setdefault("h2_label", "Pred.")
1071
1121
  comparison_kwargs.setdefault("comparison", "split_ratio")
@@ -1078,8 +1128,8 @@ def plot_data_model_comparison(
1078
1128
  model_type = _get_model_type(model_components)
1079
1129
 
1080
1130
  if model_type == "histograms":
1081
- _check_binning_consistency(model_components + [data_hist])
1082
- for component in model_components + [data_hist]:
1131
+ _check_binning_consistency([*model_components, data_hist])
1132
+ for component in [*model_components, data_hist]:
1083
1133
  _check_counting_histogram(component)
1084
1134
 
1085
1135
  if fig is None and ax_main is None and ax_comparison is None:
@@ -1097,11 +1147,8 @@ def plot_data_model_comparison(
1097
1147
  raise ValueError(
1098
1148
  "Need to provide fig, ax_main and ax_comparison (or none of them)."
1099
1149
  )
1100
- else:
1101
- if plot_only is not None:
1102
- raise ValueError(
1103
- "Cannot provide fig, ax_main or ax_comparison with plot_only."
1104
- )
1150
+ elif plot_only is not None:
1151
+ raise ValueError("Cannot provide fig, ax_main or ax_comparison with plot_only.")
1105
1152
 
1106
1153
  plot_model(
1107
1154
  stacked_components=stacked_components,
@@ -1114,7 +1161,7 @@ def plot_data_model_comparison(
1114
1161
  stacked_kwargs=stacked_kwargs,
1115
1162
  unstacked_kwargs_list=unstacked_kwargs_list,
1116
1163
  model_sum_kwargs=model_sum_kwargs,
1117
- function_range=[data_hist.axes[0].edges[0], data_hist.axes[0].edges[-1]],
1164
+ function_range=(data_hist.axes[0].edges[0], data_hist.axes[0].edges[-1]),
1118
1165
  model_uncertainty=model_uncertainty,
1119
1166
  model_uncertainty_label=model_uncertainty_label,
1120
1167
  fig=fig,