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
@@ -0,0 +1,94 @@
1
+ """
2
+ Color palettes in stacked histograms
3
+ ====================================
4
+
5
+ Examples of color palettes in stacked histograms.
6
+ """
7
+
8
+ from plothist_utils import get_dummy_data
9
+
10
+ df = get_dummy_data()
11
+
12
+ import matplotlib.pyplot as plt
13
+
14
+ from plothist import add_text, get_color_palette, make_hist, plot_error_hist, plot_model
15
+
16
+ # Define the histograms
17
+ key = "variable_1"
18
+ xrange = (-8, 10)
19
+ category = "category"
20
+
21
+ # Define masks
22
+ signal_mask = df[category] == 7
23
+ data_mask = df[category] == 8
24
+
25
+ background_categories = [0, 1, 2, 3, 4, 5]
26
+ background_categories_labels = [f"c{i}" for i in background_categories]
27
+ background_masks = [df[category] == p for p in background_categories]
28
+
29
+ # Make histograms
30
+ data_hist = make_hist(df[key][data_mask], bins=50, range=xrange, weights=1)
31
+ background_hists = [
32
+ make_hist(df[key][mask], bins=50, range=xrange, weights=1)
33
+ for mask in background_masks
34
+ ]
35
+ signal_hist = make_hist(df[key][signal_mask], bins=50, range=xrange, weights=1)
36
+
37
+ # Optional: scale to data
38
+ background_scaling_factor = data_hist.sum().value / sum(background_hists).sum().value
39
+ background_hists = [background_scaling_factor * h for h in background_hists]
40
+
41
+ signal_scaling_factor = data_hist.sum().value / signal_hist.sum().value
42
+ signal_hist *= signal_scaling_factor
43
+
44
+
45
+ # Plotting section
46
+ nrows, ncols = 2, 2
47
+
48
+ fig, axes = plt.subplots(
49
+ nrows=nrows,
50
+ ncols=ncols,
51
+ figsize=(12, 10),
52
+ )
53
+ fig.subplots_adjust(hspace=0.25)
54
+
55
+ cmap_list = ["viridis", "ggplot", "coolwarm", "YlGnBu_r"]
56
+ ax_coords = [(x, y) for x in range(nrows) for y in range(ncols)]
57
+
58
+ for k, cmap_name in enumerate(cmap_list):
59
+ background_categories_colors = get_color_palette(
60
+ cmap_name, len(background_categories)
61
+ )
62
+
63
+ ax = axes[ax_coords[k]]
64
+
65
+ plot_model(
66
+ stacked_components=background_hists,
67
+ stacked_labels=background_categories_labels,
68
+ stacked_colors=background_categories_colors,
69
+ xlabel=key,
70
+ ylabel="Entries",
71
+ model_uncertainty=False,
72
+ fig=fig,
73
+ ax=ax,
74
+ )
75
+
76
+ plot_error_hist(
77
+ data_hist,
78
+ color="black",
79
+ label="Data",
80
+ ax=ax,
81
+ )
82
+
83
+ ax.set_xlim(xrange)
84
+ ax.legend()
85
+
86
+ cmap_name = cmap_name.replace("_", r"\_")
87
+ add_text(
88
+ rf"$\mathrm{{\mathbf{{cmap = {cmap_name}}}}}$", x="right", fontsize=12, ax=ax
89
+ )
90
+
91
+ fig.savefig(
92
+ "color_palette_hists.svg",
93
+ bbox_inches="tight",
94
+ )
@@ -0,0 +1,100 @@
1
+ """
2
+ Color palettes
3
+ ==============
4
+
5
+ Examples of color palettes.
6
+ """
7
+
8
+ import matplotlib.colors as mcolors
9
+ import matplotlib.pyplot as plt
10
+ import numpy as np
11
+ from matplotlib import patches
12
+
13
+ from plothist import get_color_palette
14
+
15
+
16
+ def create_palette_plot(colors, fig_name, add_text=False, add_black_border=False):
17
+ ncolors = len(colors)
18
+
19
+ # Create a figure and axis
20
+ fig, ax = plt.subplots(figsize=(ncolors, 1))
21
+
22
+ # Plot the colored squares with small spacing
23
+ square_size = 1
24
+ spacing = 0.1
25
+ x = 0
26
+
27
+ for color in colors:
28
+ rect = patches.Rectangle((x, 0), square_size, square_size, color=color)
29
+ ax.add_patch(rect)
30
+ x += square_size + spacing
31
+
32
+ if add_text:
33
+ # Add text displaying the color value
34
+ ax.text(
35
+ x - (square_size + spacing) / 1.81,
36
+ -0.18,
37
+ mcolors.rgb2hex(color).upper(),
38
+ ha="center",
39
+ fontsize=10,
40
+ )
41
+
42
+ # Set the x-axis limits and show the ticks
43
+ ax.set_xlim(-0.5, x - spacing)
44
+ ax.set_xticks(np.arange(0, x, square_size + spacing))
45
+ ax.set_xticklabels(np.arange(1, ncolors + 1), fontsize=8)
46
+ ax.set_xticklabels([]) # Remove the x-tick labels
47
+
48
+ # Set the y-axis ticks and labels
49
+ ax.set_yticks([])
50
+ ax.set_yticklabels([])
51
+
52
+ # Remove the borders around the plot
53
+ ax.spines["top"].set_visible(False)
54
+ ax.spines["bottom"].set_visible(False)
55
+ ax.spines["left"].set_visible(False)
56
+ ax.spines["right"].set_visible(False)
57
+
58
+ # Remove the x-label and y-label
59
+ ax.set_xlabel("")
60
+ ax.set_ylabel("")
61
+
62
+ if add_black_border:
63
+ # Add a black border rectangle
64
+ border_rect = patches.Rectangle(
65
+ (0, 0),
66
+ x - spacing,
67
+ square_size,
68
+ edgecolor="black",
69
+ facecolor="none",
70
+ linewidth=1,
71
+ )
72
+ ax.add_patch(border_rect)
73
+
74
+ # Adjust the padding and remove extra whitespace
75
+ plt.margins(0)
76
+ plt.gca().set_axis_off()
77
+ plt.subplots_adjust(left=0.05, right=0.95)
78
+
79
+ plt.savefig(fig_name, bbox_inches="tight")
80
+
81
+ return fig
82
+
83
+
84
+ figs = []
85
+
86
+ ncolors = 7
87
+
88
+ ncolors_ggplot = 7 if ncolors > 7 else ncolors
89
+ colors = get_color_palette("ggplot", ncolors_ggplot)
90
+ figs.append(
91
+ create_palette_plot(colors, fig_name="usage_style_cycle.svg", add_text=True)
92
+ )
93
+
94
+ colors = get_color_palette("cubehelix", ncolors)
95
+ figs.append(create_palette_plot(colors, fig_name="usage_cubehelix.svg"))
96
+
97
+ cmap_list = ["viridis", "coolwarm", "YlGnBu_r"]
98
+ for cmap_name in cmap_list:
99
+ colors = get_color_palette(cmap_name, ncolors)
100
+ figs.append(create_palette_plot(colors, fig_name=f"usage_{cmap_name}_palette.svg"))
@@ -0,0 +1,63 @@
1
+ """
2
+ Default style: matplotlib vs plothist
3
+ =====================================
4
+
5
+ Illustration of the difference between matplotlib and plothist default styles.
6
+ """
7
+
8
+ import matplotlib.pyplot as plt
9
+ import numpy as np
10
+ from plothist_utils import get_dummy_data
11
+
12
+ df = get_dummy_data()
13
+
14
+ figs = []
15
+
16
+ for style in ["matplotlib", "plothist"]:
17
+ if style == "matplotlib":
18
+ plt.style.use("default")
19
+ plt.rcParams["font.family"] = "DejaVu Sans"
20
+ else:
21
+ # No need to set the style if we use plothist, just importing it is enough
22
+ # Here we set the style because the matplotlib style was set before
23
+ from plothist import set_style
24
+
25
+ set_style("default")
26
+
27
+ # Create a figure with subplots
28
+ fig, (ax1, ax2) = plt.subplots(
29
+ 2, 1, figsize=(6, 5.4), sharex=True, gridspec_kw={"height_ratios": [4, 1]}
30
+ )
31
+
32
+ # Plot histograms in the first subplot (ax1)
33
+ hist_0, bins, _ = ax1.hist(
34
+ df["variable_0"], bins=20, histtype="step", linewidth=1.2, label="h1"
35
+ )
36
+ h1 = ax1.hist(
37
+ df["variable_1"], bins=bins, histtype="step", linewidth=1.2, label="h2"
38
+ )
39
+ ax1.set_ylabel("Entries")
40
+ ax1.legend()
41
+
42
+ # Calculate the ratio of histogram values and plot in the second subplot (ax2)
43
+ with np.errstate(divide="ignore", invalid="ignore"):
44
+ ratio = hist_0 / h1[0] # Divide bin values of variable_0 by variable_1
45
+ bin_centers = 0.5 * (bins[:-1] + bins[1:]) # Calculate bin centers
46
+
47
+ # Create fake error bars for the ratio
48
+ ax2.plot(bin_centers, ratio, marker="|", linestyle="", markersize=15, color="black")
49
+ ax2.plot(bin_centers, ratio, marker="o", linestyle="", markersize=4, color="black")
50
+
51
+ ax2.axhline(y=1, color="black", linestyle="--", linewidth=0.8)
52
+ ax2.set_xlabel("Variable")
53
+ ax2.set_ylabel("Ratio")
54
+
55
+ ax1.set_xlim(-10, 10)
56
+ ax2.set_xlim(-10, 10)
57
+ ax2.set_ylim(0, 2)
58
+
59
+ fig.subplots_adjust(hspace=0.15)
60
+
61
+ fig.savefig(f"{style}_example.svg", bbox_inches="tight")
62
+
63
+ figs.append(fig)
plothist/histogramming.py CHANGED
@@ -1,6 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ import warnings
4
+ from collections.abc import Sequence
5
+ from typing import Callable
6
+
1
7
  import boost_histogram as bh
2
8
  import numpy as np
3
- import warnings
4
9
 
5
10
 
6
11
  # Define a custom warning for range issues
@@ -12,18 +17,24 @@ class RangeWarning(Warning):
12
17
  warnings.filterwarnings("always", category=RangeWarning)
13
18
 
14
19
 
15
- def create_axis(bins, range=None, data=np.array([]), overflow=False, underflow=False):
20
+ def create_axis(
21
+ bins: int | list[float] | np.ndarray,
22
+ range: tuple[float | str, float | str] | None = None,
23
+ data: list[float] | np.ndarray | None = None,
24
+ overflow: bool = False,
25
+ underflow: bool = False,
26
+ ) -> bh.axis.Regular | bh.axis.Variable:
16
27
  """
17
28
  Create an axis object for histogram binning based on the input data and parameters.
18
29
 
19
30
  Parameters
20
31
  ----------
21
- bins : int or array-like
32
+ bins : int or list[float]
22
33
  The number of bins or bin edges for the axis.
23
- range : None or tuple, optional
24
- The range of the axis. If None, it will be determined based on the data.
25
- data : array-like, optional
26
- The input data for determining the axis range. Default is an empty array.
34
+ range : None or tuple[float | str, float | str], optional
35
+ The range of the axis. If None, it will be determined based on the data. Default is None.
36
+ data : list[float] or np.ndarray, optional
37
+ The input data for determining the axis range. Default is None.
27
38
  overflow : bool, optional
28
39
  Whether to include an overflow bin. If False, the upper edge of the last bin is inclusive. Default is False.
29
40
  underflow : bool, optional
@@ -46,20 +57,19 @@ def create_axis(bins, range=None, data=np.array([]), overflow=False, underflow=F
46
57
  ValueError
47
58
  If the range parameter contains "min" or "max" but the data is empty.
48
59
  """
60
+ if data is None:
61
+ data = np.array([])
49
62
 
50
- try:
51
- N = len(bins)
52
- except TypeError:
53
- N = 1
63
+ is_variable_bins = isinstance(bins, (list, np.ndarray))
54
64
 
55
- if N > 1:
65
+ if is_variable_bins:
56
66
  if range is not None:
57
67
  warnings.warn(
58
68
  f"Custom binning -> ignore supplied range ({range}).", stacklevel=2
59
69
  )
60
70
  return bh.axis.Variable(bins, underflow=underflow, overflow=overflow)
61
71
 
62
- if bins <= 0:
72
+ if isinstance(bins, int) and bins <= 0:
63
73
  raise ValueError(f"Number of bins must be positive, but got {bins}.")
64
74
 
65
75
  # Inspired from np.histograms
@@ -69,8 +79,8 @@ def create_axis(bins, range=None, data=np.array([]), overflow=False, underflow=F
69
79
  "Cannot use 'min'/'max' range values with empty data. "
70
80
  "Please supply a range or provide data."
71
81
  )
72
- x_min = min(data) if range[0] == "min" else range[0]
73
- x_max = max(data) if range[1] == "max" else range[1]
82
+ x_min = min(data) if range[0] == "min" else float(range[0])
83
+ x_max = max(data) if range[1] == "max" else float(range[1])
74
84
  if x_min > x_max:
75
85
  raise ValueError(
76
86
  f"Range of [{x_min}, {x_max}] is not valid. Max must be larger than min."
@@ -79,10 +89,10 @@ def create_axis(bins, range=None, data=np.array([]), overflow=False, underflow=F
79
89
  raise ValueError(f"Range of [{x_min}, {x_max}] is not finite.")
80
90
  elif len(data) == 0:
81
91
  # handle empty arrays. Can't determine range, so use 0-1.
82
- x_min, x_max = 0, 1
92
+ x_min, x_max = 0.0, 1.0
83
93
  else:
84
- x_min = min(data)
85
- x_max = max(data)
94
+ x_min = float(min(data))
95
+ x_max = float(max(data))
86
96
  if not (np.isfinite(x_min) and np.isfinite(x_max)):
87
97
  raise ValueError(f"Autodetected range of [{x_min}, {x_max}] is not finite.")
88
98
 
@@ -94,30 +104,35 @@ def create_axis(bins, range=None, data=np.array([]), overflow=False, underflow=F
94
104
  return bh.axis.Regular(bins, x_min, x_max, underflow=underflow, overflow=overflow)
95
105
 
96
106
 
97
- def make_hist(data=np.array([]), bins=50, range=None, weights=1):
107
+ def make_hist(
108
+ data: list[float] | np.ndarray | None = None,
109
+ bins: int | list[float] | np.ndarray = 50,
110
+ range: tuple[float | str, float | str] | None = None,
111
+ weights: float | list[float] | np.ndarray = 1,
112
+ ) -> bh.Histogram:
98
113
  """
99
114
  Create a histogram object and fill it with the provided data.
100
115
 
101
116
  Parameters
102
117
  ----------
103
- data : array-like, optional
104
- 1D array-like data used to fill the histogram (default is an empty array).
105
- If an empty array, an empty histogram is returned.
106
- bins : int or tuple, optional
118
+ data : list[float] or np.ndarray, optional
119
+ 1D array-like data used to fill the histogram (default is None).
120
+ If None is provided, an empty histogram is returned.
121
+ bins : int or list[float], optional
107
122
  Binning specification for the histogram (default is 50).
108
123
  If an integer, it represents the number of bins.
109
- If a tuple, it should be the explicit list of all bin edges.
110
- range : tuple, optional
124
+ If a list, it should be the explicit list of all bin edges.
125
+ range : tuple[float | str, float | str], optional
111
126
  The range of values to consider for the histogram bins (default is None).
112
127
  If None, the range is determined from the data.
113
- weights : float or array-like, optional
128
+ weights : float or list[float] or np.ndarray, optional
114
129
  Weight(s) to apply to the data points (default is 1).
115
130
  If a float, a single weight is applied to all data points.
116
131
  If an array-like, weights are applied element-wise.
117
132
 
118
133
  Returns
119
134
  -------
120
- histogram : boost_histogram.Histogram
135
+ histogram : bh.Histogram
121
136
  The filled histogram object.
122
137
 
123
138
  Warns
@@ -125,6 +140,8 @@ def make_hist(data=np.array([]), bins=50, range=None, weights=1):
125
140
  RangeWarning
126
141
  If more than 1% of the data is outside of the binning range.
127
142
  """
143
+ if data is None:
144
+ data = np.array([])
128
145
 
129
146
  axis = create_axis(bins, range, data)
130
147
 
@@ -145,7 +162,7 @@ def make_hist(data=np.array([]), bins=50, range=None, weights=1):
145
162
  # Issue a warning if more than 1% of the data is outside of the binning range
146
163
  if range_coverage < 0.99:
147
164
  warnings.warn(
148
- f"Only {100*range_coverage:.2f}% of data contained in the binning range [{axis.edges[0]}, {axis.edges[-1]}].",
165
+ f"Only {100 * range_coverage:.2f}% of data contained in the binning range [{axis.edges[0]}, {axis.edges[-1]}].",
149
166
  category=RangeWarning,
150
167
  stacklevel=2,
151
168
  )
@@ -153,31 +170,38 @@ def make_hist(data=np.array([]), bins=50, range=None, weights=1):
153
170
  return h
154
171
 
155
172
 
156
- def make_2d_hist(data=np.array([[], []]), bins=(10, 10), range=(None, None), weights=1):
173
+ def make_2d_hist(
174
+ data: list[np.ndarray] | np.ndarray | None = None,
175
+ bins: Sequence[int | Sequence[float]] | None = None,
176
+ range: tuple[
177
+ tuple[float | str, float | str] | None, tuple[float | str, float | str] | None
178
+ ] = (None, None),
179
+ weights: float | list[float] | np.ndarray = 1,
180
+ ) -> bh.Histogram:
157
181
  """
158
182
  Create a 2D histogram object and fill it with the provided data.
159
183
 
160
184
  Parameters
161
185
  ----------
162
- data : array-like, optional
163
- 2D array-like data used to fill the histogram (default is an empty array).
164
- If an empty array, an empty histogram is returned.
165
- bins : tuple, optional
166
- Binning specification for each dimension of the histogram (default is (10, 10)).
186
+ data : list[np.ndarray] or np.ndarray, optional
187
+ 2D array-like data used to fill the histogram (default is None).
188
+ If None is provided, an empty histogram is returned.
189
+ bins : Sequence[int | Sequence[float]], optional
190
+ Binning specification for each dimension of the histogram (if None, it will be set to [50, 50]).
167
191
  Each element of the tuple represents the number of bins for the corresponding dimension.
168
192
  Also support explicit bin edges specification (for non-constant bin size).
169
- range : tuple, optional
193
+ range : tuple[tuple[float | str, float | str] | None, tuple[float | str, float | str] | None], optional
170
194
  The range of values to consider for each dimension of the histogram (default is (None, None)).
171
195
  If None, the range is determined from the data for that dimension.
172
196
  The tuple should have the same length as the data.
173
- weights : float or array-like, optional
197
+ weights : float or list[float] or np.ndarray, optional
174
198
  Weight(s) to apply to the data points (default is 1).
175
199
  If a float, a single weight is applied to all data points.
176
200
  If an array-like, weights are applied element-wise.
177
201
 
178
202
  Returns
179
203
  -------
180
- histogram : boost_histogram.Histogram
204
+ histogram : bh.Histogram
181
205
  The filled 2D histogram object.
182
206
 
183
207
  Raises
@@ -190,10 +214,14 @@ def make_2d_hist(data=np.array([[], []]), bins=(10, 10), range=(None, None), wei
190
214
  RangeWarning
191
215
  If more than 1% of the data is outside of the binning range.
192
216
  """
217
+ if data is None:
218
+ data = np.array([[], []])
193
219
  if len(data) != 2:
194
220
  raise ValueError("data should have two components, x and y")
195
221
  if len(data[0]) != len(data[1]):
196
222
  raise ValueError("x and y must have the same length.")
223
+ if bins is None:
224
+ bins = [50, 50]
197
225
 
198
226
  x_axis = create_axis(bins[0], range[0], data[0])
199
227
  y_axis = create_axis(bins[1], range[1], data[1])
@@ -219,7 +247,7 @@ def make_2d_hist(data=np.array([[], []]), bins=(10, 10), range=(None, None), wei
219
247
  # Issue a warning if more than 1% of the data is outside of the binning range
220
248
  if range_coverage < 0.99:
221
249
  warnings.warn(
222
- f"Only {100*range_coverage:.2f}% of data contained in the binning range ([{x_axis.edges[0]}, {x_axis.edges[-1]}], [{y_axis.edges[0]}, {y_axis.edges[-1]}]).",
250
+ f"Only {100 * range_coverage:.2f}% of data contained in the binning range ([{x_axis.edges[0]}, {x_axis.edges[-1]}], [{y_axis.edges[0]}, {y_axis.edges[-1]}]).",
223
251
  category=RangeWarning,
224
252
  stacklevel=2,
225
253
  )
@@ -227,13 +255,13 @@ def make_2d_hist(data=np.array([[], []]), bins=(10, 10), range=(None, None), wei
227
255
  return h
228
256
 
229
257
 
230
- def _check_counting_histogram(hist):
258
+ def _check_counting_histogram(hist: bh.Histogram) -> None:
231
259
  """
232
260
  Check that the histogram is a counting histogram.
233
261
 
234
262
  Parameters
235
263
  ----------
236
- hist : boost_histogram.Histogram
264
+ hist : bh.Histogram
237
265
  The histogram to check.
238
266
 
239
267
  Raise
@@ -246,10 +274,11 @@ def _check_counting_histogram(hist):
246
274
  raise ValueError(
247
275
  f"The histogram must be a counting histogram, but the input histogram has kind {hist.kind}."
248
276
  )
249
- return
250
277
 
251
278
 
252
- def _make_hist_from_function(func, ref_hist):
279
+ def _make_hist_from_function(
280
+ func: Callable[[np.ndarray], np.ndarray], ref_hist: bh.Histogram
281
+ ) -> bh.Histogram:
253
282
  """
254
283
  Create a histogram from a function and a reference histogram.
255
284
  The returned histogram has the same binning as the reference histogram and
@@ -257,14 +286,14 @@ def _make_hist_from_function(func, ref_hist):
257
286
 
258
287
  Parameters
259
288
  ----------
260
- func : function
289
+ func : Callable[[np.ndarray], np.ndarray]
261
290
  1D function. The function should support vectorization (i.e. accept a numpy array as input).
262
- ref_hist : boost_histogram.Histogram
291
+ ref_hist : bh.Histogram
263
292
  The reference 1D histogram to use for the binning.
264
293
 
265
294
  Returns
266
295
  -------
267
- hist : boost_histogram.Histogram
296
+ hist : bh.Histogram
268
297
  The histogram filled with the function.
269
298
 
270
299
  Raises
@@ -282,18 +311,18 @@ def _make_hist_from_function(func, ref_hist):
282
311
  return hist
283
312
 
284
313
 
285
- def flatten_2d_hist(hist):
314
+ def flatten_2d_hist(hist: bh.Histogram) -> bh.Histogram:
286
315
  """
287
316
  Flatten a 2D histogram into a 1D histogram.
288
317
 
289
318
  Parameters
290
319
  ----------
291
- hist : Histogram object
320
+ hist : bh.Histogram
292
321
  The 2D histogram to be flattened.
293
322
 
294
323
  Returns
295
324
  -------
296
- Histogram object
325
+ bh.Histogram
297
326
  The flattened 1D histogram.
298
327
 
299
328
  Raises