Skip to content

Histogram Module

Import the histogram utilities from spyral_utils.plot

Modules for creating and plotting histograms in matplotlib

The default matplotlib histogramming tools can be hard to use for large datasets as they require a lot of rebinning or all of the data to be loaded. Here we give some of our own which allow for incremental filling of histograms

Classes:

Name Description
Hist1D

A 1-D histogram dataclass. Should not be instantiated directly

Hist2D

A 2-D histogram dataclass. Should not be instantiated directly

Histogrammer

A parent object used to create, manage histograms

Hist1D dataclass

Dataclass wrapping a numpy array used to store histogram data and retrieve histogram statistics

Attributes:

Name Type Description
name str

histogram name

counts ndarray

array of histogram counts

bins ndarray

array of histogram bin edges

bin_width float

the width of histogram bins

Methods:

Name Description
get_bin

get the bin number for an x-coordinate value

stats_for_range

get some statistics for a subrange of the histogram

get_subrange

get a histogram subrange (bin edges, counts)

Source code in src/spyral_utils/plot/histogram.py
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@dataclass
class Hist1D:
    """Dataclass wrapping a numpy array used to store histogram data and retrieve histogram statistics

    Attributes
    ----------
    name: str
        histogram name
    counts: ndarray
        array of histogram counts
    bins: ndarray
        array of histogram bin edges
    bin_width: float
        the width of histogram bins

    Methods
    -------
    get_bin(x: float) -> int | None
        get the bin number for an x-coordinate value
    stats_for_range(xrange: tuple[float, float]) -> tuple[float, float, float] | None
        get some statistics for a subrange of the histogram
    get_subrange(xrange: tuple[float, float]) -> tuple[ndarray, ndarray]
        get a histogram subrange (bin edges, counts)
    """

    name: str
    counts: NDArray[np.float64]
    bins: NDArray[np.float64]
    bin_width: float

    def get_bin(self, x: float) -> int | None:
        """Get the bin number which contains the x-coordinate

        Parameters
        ----------
        x: float
            X-coordinate for which we want to find the bin number

        Returns
        -------
        int | None
            The bin number or None if the x value does not fall within the histogram
        """
        if x < self.bins.min() or x > self.bins.max():
            return None

        return int(floor((x - self.bins[0]) / self.bin_width))

    def stats_for_range(
        self, xrange: tuple[float, float]
    ) -> tuple[float, float, float] | None:
        """Get some statistics for a histogram subrange

        Calculates the mean, integral, and standard deviation of the sub-range

        Parameters
        ----------
        xrange: tuple[float, float]
            the subrange of the histogram (min, max) in x-coordinates

        Returns
        -------
        tuple[float, float, float] | None
            Returns a tuple of (integral, mean, std. dev.) for the subrange, or None if the subrange is not within the histogram bounds

        """
        clamped_range = clamp_range(xrange, (self.bins.min(), self.bins.max()))
        bin_min = self.get_bin(clamped_range[0])
        bin_max = self.get_bin(clamped_range[1])
        if bin_min is None or bin_max is None:
            return None
        integral = np.sum(self.counts[bin_min:bin_max])
        mean = np.average(
            self.bins[bin_min:bin_max], weights=self.counts[bin_min:bin_max]
        )
        variance = np.average(
            (self.bins[bin_min:bin_max] - mean) ** 2.0,
            weights=self.counts[bin_min:bin_max],
        )
        return (integral, mean, np.sqrt(variance))  # type: ignore

    def get_subrange(
        self, xrange: tuple[float, float]
    ) -> tuple[np.ndarray, np.ndarray]:
        """Get a subrange of the histogram

        Parameters
        ----------
        xrange: tuple[float, float]
            the subrange of the histogram (min, max) in x-coordinates

        Returns
        -------
        tuple[ndarray, ndarray]
            the subrange (bin edges, counts)
        """
        mask = np.logical_and(self.bins >= xrange[0], self.bins < xrange[1])
        return (self.bins[mask], self.counts[mask[:-1]])

get_bin(x)

Get the bin number which contains the x-coordinate

Parameters:

Name Type Description Default
x float

X-coordinate for which we want to find the bin number

required

Returns:

Type Description
int | None

The bin number or None if the x value does not fall within the histogram

Source code in src/spyral_utils/plot/histogram.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
def get_bin(self, x: float) -> int | None:
    """Get the bin number which contains the x-coordinate

    Parameters
    ----------
    x: float
        X-coordinate for which we want to find the bin number

    Returns
    -------
    int | None
        The bin number or None if the x value does not fall within the histogram
    """
    if x < self.bins.min() or x > self.bins.max():
        return None

    return int(floor((x - self.bins[0]) / self.bin_width))

get_subrange(xrange)

Get a subrange of the histogram

Parameters:

Name Type Description Default
xrange tuple[float, float]

the subrange of the histogram (min, max) in x-coordinates

required

Returns:

Type Description
tuple[ndarray, ndarray]

the subrange (bin edges, counts)

Source code in src/spyral_utils/plot/histogram.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def get_subrange(
    self, xrange: tuple[float, float]
) -> tuple[np.ndarray, np.ndarray]:
    """Get a subrange of the histogram

    Parameters
    ----------
    xrange: tuple[float, float]
        the subrange of the histogram (min, max) in x-coordinates

    Returns
    -------
    tuple[ndarray, ndarray]
        the subrange (bin edges, counts)
    """
    mask = np.logical_and(self.bins >= xrange[0], self.bins < xrange[1])
    return (self.bins[mask], self.counts[mask[:-1]])

stats_for_range(xrange)

Get some statistics for a histogram subrange

Calculates the mean, integral, and standard deviation of the sub-range

Parameters:

Name Type Description Default
xrange tuple[float, float]

the subrange of the histogram (min, max) in x-coordinates

required

Returns:

Type Description
tuple[float, float, float] | None

Returns a tuple of (integral, mean, std. dev.) for the subrange, or None if the subrange is not within the histogram bounds

Source code in src/spyral_utils/plot/histogram.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def stats_for_range(
    self, xrange: tuple[float, float]
) -> tuple[float, float, float] | None:
    """Get some statistics for a histogram subrange

    Calculates the mean, integral, and standard deviation of the sub-range

    Parameters
    ----------
    xrange: tuple[float, float]
        the subrange of the histogram (min, max) in x-coordinates

    Returns
    -------
    tuple[float, float, float] | None
        Returns a tuple of (integral, mean, std. dev.) for the subrange, or None if the subrange is not within the histogram bounds

    """
    clamped_range = clamp_range(xrange, (self.bins.min(), self.bins.max()))
    bin_min = self.get_bin(clamped_range[0])
    bin_max = self.get_bin(clamped_range[1])
    if bin_min is None or bin_max is None:
        return None
    integral = np.sum(self.counts[bin_min:bin_max])
    mean = np.average(
        self.bins[bin_min:bin_max], weights=self.counts[bin_min:bin_max]
    )
    variance = np.average(
        (self.bins[bin_min:bin_max] - mean) ** 2.0,
        weights=self.counts[bin_min:bin_max],
    )
    return (integral, mean, np.sqrt(variance))  # type: ignore

Hist2D dataclass

Dataclass wrapping a numpy array used to store two-dimensional histogram data and retrieve histogram statistics

Attributes:

Name Type Description
name str

histogram name

counts ndarray

array of histogram counts

x_bins ndarray

array of histogram x bin edges

y_bins ndarray

array of histogram y bin edges

x_bin_width float

the width of histogram x bins

y_bin_width float

the width of histogram y bins

Methods:

Name Description
get_bin

get the x and y bin numbers for an (x,y)-coordinate value

stats_for_range

get some statistics for a subrange of the histogram

get_subrange

get a histogram subrange (x bin edges, y bin edges, counts)

Source code in src/spyral_utils/plot/histogram.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
@dataclass
class Hist2D:
    """Dataclass wrapping a numpy array used to store two-dimensional histogram data and retrieve histogram statistics

    Attributes
    ----------
    name: str
        histogram name
    counts: ndarray
        array of histogram counts
    x_bins: ndarray
        array of histogram x bin edges
    y_bins: ndarray
        array of histogram y bin edges
    x_bin_width: float
        the width of histogram x bins
    y_bin_width: float
        the width of histogram y bins

    Methods
    -------
    get_bin(coords: tuple[float, float]) -> tuple[int, int] | None
        get the x and y bin numbers for an (x,y)-coordinate value
    stats_for_range(xrange: tuple[float, float], yrange: tuple[float, float]) -> tuple[float, float, float, float, float] | None
        get some statistics for a subrange of the histogram
    get_subrange(xrange: tuple[float, float], xrange: tuple[float, float]) -> tuple[ndarray, ndarray, ndarray]
        get a histogram subrange (x bin edges, y bin edges, counts)
    """

    name: str
    counts: NDArray[np.float64]
    x_bins: NDArray[np.float64]
    y_bins: NDArray[np.float64]
    x_bin_width: float
    y_bin_width: float

    def get_bin(self, coords: tuple[float, float]) -> tuple[int, int] | None:
        """Get the x and y bin numbers for an (x,y)-coordinate value

        Parameters
        ----------
        coords: tuple[float, float]
            The (x,y) corrdinate for which we want to find the bin numbers

        Returns
        -------
        tuple[int, int] | None
            Returns the (x bin, y bin) numbers or None if out of range
        """
        if (coords[0] < self.x_bins.min() or coords[0] > self.x_bins.max()) or (
            coords[1] < self.y_bins.min() or coords[1] > self.y_bins.max()
        ):
            return None

        y_bin = int(floor((coords[1] - self.y_bins[0]) / self.y_bin_width))
        x_bin = int(floor((coords[0] - self.x_bins[0]) / self.x_bin_width))
        return (x_bin, y_bin)

    # returns (integral, mean x, std_dev x, mean y, std_dev y)
    def stats_for_range(
        self, xrange: tuple[float, float], yrange: tuple[float, float]
    ) -> tuple[float, float, float, float, float] | None:
        """Get some statistics for a histogram subrange

        Calculates the mean in x and y, integral, and standard deviation in x and y of the sub-range

        Parameters
        ----------
        xrange: tuple[float, float]
            the subrange of the histogram (min, max) in x-coordinates
        yrange: tuple[float, float]
            the subrange of the histogram (min, max) in y-coordinates

        Returns
        -------
        tuple[float, float, float, float, float] | None
            Returns a tuple of (integral, x mean, y mean, x std. dev., y std. dev.) for the subrange, or None if the subrange is not within the histogram bounds

        """
        clamped_x_range = clamp_range(xrange, (self.x_bins.min(), self.x_bins.max()))
        clamped_y_range = clamp_range(yrange, (self.y_bins.min(), self.y_bins.max()))
        bin_min = self.get_bin((clamped_x_range[0], clamped_y_range[0]))
        bin_max = self.get_bin((clamped_x_range[1], clamped_y_range[1]))
        if bin_min == None or bin_max == None:
            return None

        x_bin_range = np.arange(start=bin_min[0], stop=bin_max[0], step=1)
        y_bin_range = np.arange(start=bin_min[1], stop=bin_max[1], step=1)
        bin_mesh = np.ix_(y_bin_range, x_bin_range)

        integral = np.sum(self.counts[bin_mesh])
        mean_x = np.average(
            self.x_bins[bin_min[0] : bin_max[0]],
            weights=np.sum(self.counts.T[bin_min[0] : bin_max[0]], 1),
        )
        mean_y = np.average(
            self.y_bins[bin_min[1] : bin_max[1]],
            weights=np.sum(self.counts[bin_min[1] : bin_max[1]], 1),
        )
        var_x = np.average(
            (self.x_bins[bin_min[0] : bin_max[0]] - mean_x) ** 2.0,
            weights=np.sum(self.counts.T[bin_min[0] : bin_max[0]], 1),
        )
        var_y = np.average(
            (self.y_bins[bin_min[1] : bin_max[1]] - mean_y) ** 2.0,
            weights=np.sum(self.counts[bin_min[1] : bin_max[1]], 1),
        )
        return (integral, mean_x, mean_y, np.sqrt(var_x), np.sqrt(var_y))  # type: ignore

    def get_subrange(
        self, xrange: tuple[float, float], yrange: tuple[float, float]
    ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
        """Get a subrange of the histogram

        Parameters
        ----------
        xrange: tuple[float, float]
            the subrange of the histogram (min, max) in x-coordinates
        yrange: tuple[float, float]
            the subrange of the histogram (min, max) in y-coordinates

        Returns
        -------
        tuple[ndarray, ndarray, ndarray]
            the subrange (x bin edges, y bin edges, counts)
        """
        x_mask = np.logical_and(self.x_bins >= xrange[0], self.x_bins < xrange[1])
        y_mask = np.logical_and(self.y_bins >= yrange[0], self.y_bins < yrange[1])
        bin_mesh = np.ix_(y_mask, x_mask)
        return (self.x_bins[x_mask], self.y_bins[y_mask], self.counts[bin_mesh])

get_bin(coords)

Get the x and y bin numbers for an (x,y)-coordinate value

Parameters:

Name Type Description Default
coords tuple[float, float]

The (x,y) corrdinate for which we want to find the bin numbers

required

Returns:

Type Description
tuple[int, int] | None

Returns the (x bin, y bin) numbers or None if out of range

Source code in src/spyral_utils/plot/histogram.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
def get_bin(self, coords: tuple[float, float]) -> tuple[int, int] | None:
    """Get the x and y bin numbers for an (x,y)-coordinate value

    Parameters
    ----------
    coords: tuple[float, float]
        The (x,y) corrdinate for which we want to find the bin numbers

    Returns
    -------
    tuple[int, int] | None
        Returns the (x bin, y bin) numbers or None if out of range
    """
    if (coords[0] < self.x_bins.min() or coords[0] > self.x_bins.max()) or (
        coords[1] < self.y_bins.min() or coords[1] > self.y_bins.max()
    ):
        return None

    y_bin = int(floor((coords[1] - self.y_bins[0]) / self.y_bin_width))
    x_bin = int(floor((coords[0] - self.x_bins[0]) / self.x_bin_width))
    return (x_bin, y_bin)

get_subrange(xrange, yrange)

Get a subrange of the histogram

Parameters:

Name Type Description Default
xrange tuple[float, float]

the subrange of the histogram (min, max) in x-coordinates

required
yrange tuple[float, float]

the subrange of the histogram (min, max) in y-coordinates

required

Returns:

Type Description
tuple[ndarray, ndarray, ndarray]

the subrange (x bin edges, y bin edges, counts)

Source code in src/spyral_utils/plot/histogram.py
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
def get_subrange(
    self, xrange: tuple[float, float], yrange: tuple[float, float]
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
    """Get a subrange of the histogram

    Parameters
    ----------
    xrange: tuple[float, float]
        the subrange of the histogram (min, max) in x-coordinates
    yrange: tuple[float, float]
        the subrange of the histogram (min, max) in y-coordinates

    Returns
    -------
    tuple[ndarray, ndarray, ndarray]
        the subrange (x bin edges, y bin edges, counts)
    """
    x_mask = np.logical_and(self.x_bins >= xrange[0], self.x_bins < xrange[1])
    y_mask = np.logical_and(self.y_bins >= yrange[0], self.y_bins < yrange[1])
    bin_mesh = np.ix_(y_mask, x_mask)
    return (self.x_bins[x_mask], self.y_bins[y_mask], self.counts[bin_mesh])

stats_for_range(xrange, yrange)

Get some statistics for a histogram subrange

Calculates the mean in x and y, integral, and standard deviation in x and y of the sub-range

Parameters:

Name Type Description Default
xrange tuple[float, float]

the subrange of the histogram (min, max) in x-coordinates

required
yrange tuple[float, float]

the subrange of the histogram (min, max) in y-coordinates

required

Returns:

Type Description
tuple[float, float, float, float, float] | None

Returns a tuple of (integral, x mean, y mean, x std. dev., y std. dev.) for the subrange, or None if the subrange is not within the histogram bounds

Source code in src/spyral_utils/plot/histogram.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
def stats_for_range(
    self, xrange: tuple[float, float], yrange: tuple[float, float]
) -> tuple[float, float, float, float, float] | None:
    """Get some statistics for a histogram subrange

    Calculates the mean in x and y, integral, and standard deviation in x and y of the sub-range

    Parameters
    ----------
    xrange: tuple[float, float]
        the subrange of the histogram (min, max) in x-coordinates
    yrange: tuple[float, float]
        the subrange of the histogram (min, max) in y-coordinates

    Returns
    -------
    tuple[float, float, float, float, float] | None
        Returns a tuple of (integral, x mean, y mean, x std. dev., y std. dev.) for the subrange, or None if the subrange is not within the histogram bounds

    """
    clamped_x_range = clamp_range(xrange, (self.x_bins.min(), self.x_bins.max()))
    clamped_y_range = clamp_range(yrange, (self.y_bins.min(), self.y_bins.max()))
    bin_min = self.get_bin((clamped_x_range[0], clamped_y_range[0]))
    bin_max = self.get_bin((clamped_x_range[1], clamped_y_range[1]))
    if bin_min == None or bin_max == None:
        return None

    x_bin_range = np.arange(start=bin_min[0], stop=bin_max[0], step=1)
    y_bin_range = np.arange(start=bin_min[1], stop=bin_max[1], step=1)
    bin_mesh = np.ix_(y_bin_range, x_bin_range)

    integral = np.sum(self.counts[bin_mesh])
    mean_x = np.average(
        self.x_bins[bin_min[0] : bin_max[0]],
        weights=np.sum(self.counts.T[bin_min[0] : bin_max[0]], 1),
    )
    mean_y = np.average(
        self.y_bins[bin_min[1] : bin_max[1]],
        weights=np.sum(self.counts[bin_min[1] : bin_max[1]], 1),
    )
    var_x = np.average(
        (self.x_bins[bin_min[0] : bin_max[0]] - mean_x) ** 2.0,
        weights=np.sum(self.counts.T[bin_min[0] : bin_max[0]], 1),
    )
    var_y = np.average(
        (self.y_bins[bin_min[1] : bin_max[1]] - mean_y) ** 2.0,
        weights=np.sum(self.counts[bin_min[1] : bin_max[1]], 1),
    )
    return (integral, mean_x, mean_y, np.sqrt(var_x), np.sqrt(var_y))  # type: ignore

Histogrammer

Histogrammer is a wrapper around a dictionary of str->Hist1D|Hist2D that interfaces with matplotlib

A new histogram can be added to the dictionary using the add_hist1d/add_hist2d methods. The name passed to these methods is used as the key for the dictionary. To add data to the histograms use the fill_hist1d/fill_hist2d methods. The fill methods accept arrays of data, and this is by intent. It would not be efficient to fill the histograms point by point. Rather, prefer passing entire data sets (like dataframe columns). Finally, to retrieve a histogram (for plotting, etc), use the get_hist1d/get_hist2d methods. Prefer the getters over direct access to the underlying dictionary as the getters perfom some error checking.

Attributes:

Name Type Description
histograms dict[str, Hist1D | Hist2D]

the histograms held by the Histogrammer, mapped by name

Methods:

Name Description
add_hist1d

add a Hist1D

add_hist2d

add a Hist2D

fill_hist1d

fill an existing Hist1D with some data

fill_hist2d

fill an existing Hist2D with some data

get_hist1d

get a Hist1D by name

get_hist2d

get a Hist2D by name

Source code in src/spyral_utils/plot/histogram.py
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
class Histogrammer:
    """Histogrammer is a wrapper around a dictionary of str->Hist1D|Hist2D that interfaces with matplotlib

    A new histogram can be added to the dictionary using the add_hist1d/add_hist2d methods. The name passed to
    these methods is used as the key for the dictionary. To add data to the histograms use the fill_hist1d/fill_hist2d methods.
    The fill methods accept arrays of data, and this is by intent. It would not be efficient to fill the histograms point by point. Rather, prefer
    passing entire data sets (like dataframe columns). Finally, to retrieve a histogram (for plotting, etc), use the get_hist1d/get_hist2d methods.
    Prefer the getters over direct access to the underlying dictionary as the getters perfom some error checking.

    Attributes
    ----------
    histograms: dict[str, Hist1D | Hist2D]
        the histograms held by the Histogrammer, mapped by name

    Methods
    -------
    add_hist1d(name: str, bins: int, range: tuple[float, float])
        add a Hist1D
    add_hist2d(name: str, bins: tuple[int, int], ranges: tuple[tuple[float, float], tuple[float, float]])
        add a Hist2D
    fill_hist1d(self, name: str, data: ndarray) -> bool
        fill an existing Hist1D with some data
    fill_hist2d(self, name: str, x_data: ndarray, y_data: ndarray) -> bool
        fill an existing Hist2D with some data
    get_hist1d(name: str) -> Hist1D | None
        get a Hist1D by name
    get_hist2d(name: str) -> Hist2D | None
        get a Hist2D by name
    """

    def __init__(self):
        self.histograms: dict[str, Hist1D | Hist2D] = {}

    def add_hist1d(self, name: str, bins: int, range: tuple[float, float]):
        """Add a Hist1D to the Histogrammer

        Parameters
        ----------
        name: str
            The name of the histogram, it should be unqiue
        bins: int
            The number of bins
        range: tuple[float, float]
            The x-range of the histogram in x-axis coordinates
        """
        if name in self.histograms:
            print(f"Overwriting histogram named {name} in Histogrammer.add_histogram!")

        hist = Hist1D(
            name, np.empty(0), np.empty(0), np.abs(range[0] - range[1]) / float(bins)
        )
        hist.counts, hist.bins = np.histogram(a=[], bins=bins, range=range)
        self.histograms[name] = hist

    def add_hist2d(
        self,
        name: str,
        bins: tuple[int, int],
        ranges: tuple[tuple[float, float], tuple[float, float]],
    ):
        """Add a Hist2D to the Histogrammer

        Parameters
        ----------
        name: str
            The name of the histogram, it should be unqiue
        bins: tuple[int, int]
            The number of (x bins, y bins)
        ranges: tuple[tuple[float, float], tuple[float, float]]
            The range of the histogram ((min x, max x), (min y, max y))
        """
        if name in self.histograms:
            print(f"Overwriting histogram named {name} in Histogrammer.add_histogram!")

        hist = Hist2D(
            name,
            np.empty(0),
            np.empty(0),
            np.empty(0),
            np.abs(ranges[0][0] - ranges[0][1]) / float(bins[0]),
            np.abs(ranges[1][0] - ranges[1][1]) / float(bins[1]),
        )
        hist.counts, hist.x_bins, hist.y_bins = np.histogram2d(
            x=[], y=[], bins=bins, range=ranges
        )
        hist.counts = hist.counts.T
        self.histograms[name] = hist

    def fill_hist1d(self, name: str, data: np.ndarray) -> bool:
        """Fill a Hist1D with some data

        Parameters
        ----------
        name: str
            The name of the Hist1D
        data: ndarray
            The data to fill the histogram with. Should be a 1-D array

        Returns
        -------
        bool
            Indicates if data was successfully added to the histogram

        """
        if name not in self.histograms:
            return False

        hist = self.histograms[name]
        if type(hist) is not Hist1D:
            return False

        hist.counts = hist.counts + np.histogram(a=data, bins=hist.bins)[0]
        return True

    def fill_hist2d(self, name: str, x_data: np.ndarray, y_data: np.ndarray) -> bool:
        """Fill a Hist1D with some data

        The parameters x_data and y_data should have the same length.

        Parameters
        ----------
        name: str
            The name of the Hist1D
        x_data: ndarray
            The x coordinates of the data. Should be a 1-D array.
        y_data: ndarray
            The y coordinates of the data. Should be a 1-D array.

        Returns
        -------
        bool
            Indicates if data was successfully added to the histogram

        """
        if name not in self.histograms:
            return False

        hist = self.histograms[name]
        if type(hist) is not Hist2D:
            return False
        counts, _, _ = np.histogram2d(
            x_data.flatten(), y_data.flatten(), bins=(hist.x_bins, hist.y_bins)  # type: ignore
        )
        hist.counts += counts.T
        return True

    def get_hist1d(self, name: str) -> Hist1D | None:
        """Retrieve a Hist1D by name

        Parameters
        ----------
        name: str
            The name of the histogram

        Returns
        -------
        Hist1D | None
            Returns Hist1D if a Hist1D exists with the given name. Otherwise returns None.

        """
        if name not in self.histograms:
            return None

        hist = self.histograms[name]
        if type(hist) is not Hist1D:
            return None
        else:
            return hist

    def get_hist2d(self, name: str) -> Hist2D | None:
        """Retrieve a Hist2D by name

        Parameters
        ----------
        name: str
            The name of the histogram

        Returns
        -------
        Hist2D | None
            Returns Hist2D if a Hist2D exists with the given name. Otherwise returns None.

        """
        if name not in self.histograms:
            return None

        hist = self.histograms[name]
        if type(hist) is not Hist2D:
            return None
        else:
            return hist

add_hist1d(name, bins, range)

Add a Hist1D to the Histogrammer

Parameters:

Name Type Description Default
name str

The name of the histogram, it should be unqiue

required
bins int

The number of bins

required
range tuple[float, float]

The x-range of the histogram in x-axis coordinates

required
Source code in src/spyral_utils/plot/histogram.py
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
def add_hist1d(self, name: str, bins: int, range: tuple[float, float]):
    """Add a Hist1D to the Histogrammer

    Parameters
    ----------
    name: str
        The name of the histogram, it should be unqiue
    bins: int
        The number of bins
    range: tuple[float, float]
        The x-range of the histogram in x-axis coordinates
    """
    if name in self.histograms:
        print(f"Overwriting histogram named {name} in Histogrammer.add_histogram!")

    hist = Hist1D(
        name, np.empty(0), np.empty(0), np.abs(range[0] - range[1]) / float(bins)
    )
    hist.counts, hist.bins = np.histogram(a=[], bins=bins, range=range)
    self.histograms[name] = hist

add_hist2d(name, bins, ranges)

Add a Hist2D to the Histogrammer

Parameters:

Name Type Description Default
name str

The name of the histogram, it should be unqiue

required
bins tuple[int, int]

The number of (x bins, y bins)

required
ranges tuple[tuple[float, float], tuple[float, float]]

The range of the histogram ((min x, max x), (min y, max y))

required
Source code in src/spyral_utils/plot/histogram.py
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
def add_hist2d(
    self,
    name: str,
    bins: tuple[int, int],
    ranges: tuple[tuple[float, float], tuple[float, float]],
):
    """Add a Hist2D to the Histogrammer

    Parameters
    ----------
    name: str
        The name of the histogram, it should be unqiue
    bins: tuple[int, int]
        The number of (x bins, y bins)
    ranges: tuple[tuple[float, float], tuple[float, float]]
        The range of the histogram ((min x, max x), (min y, max y))
    """
    if name in self.histograms:
        print(f"Overwriting histogram named {name} in Histogrammer.add_histogram!")

    hist = Hist2D(
        name,
        np.empty(0),
        np.empty(0),
        np.empty(0),
        np.abs(ranges[0][0] - ranges[0][1]) / float(bins[0]),
        np.abs(ranges[1][0] - ranges[1][1]) / float(bins[1]),
    )
    hist.counts, hist.x_bins, hist.y_bins = np.histogram2d(
        x=[], y=[], bins=bins, range=ranges
    )
    hist.counts = hist.counts.T
    self.histograms[name] = hist

fill_hist1d(name, data)

Fill a Hist1D with some data

Parameters:

Name Type Description Default
name str

The name of the Hist1D

required
data ndarray

The data to fill the histogram with. Should be a 1-D array

required

Returns:

Type Description
bool

Indicates if data was successfully added to the histogram

Source code in src/spyral_utils/plot/histogram.py
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
def fill_hist1d(self, name: str, data: np.ndarray) -> bool:
    """Fill a Hist1D with some data

    Parameters
    ----------
    name: str
        The name of the Hist1D
    data: ndarray
        The data to fill the histogram with. Should be a 1-D array

    Returns
    -------
    bool
        Indicates if data was successfully added to the histogram

    """
    if name not in self.histograms:
        return False

    hist = self.histograms[name]
    if type(hist) is not Hist1D:
        return False

    hist.counts = hist.counts + np.histogram(a=data, bins=hist.bins)[0]
    return True

fill_hist2d(name, x_data, y_data)

Fill a Hist1D with some data

The parameters x_data and y_data should have the same length.

Parameters:

Name Type Description Default
name str

The name of the Hist1D

required
x_data ndarray

The x coordinates of the data. Should be a 1-D array.

required
y_data ndarray

The y coordinates of the data. Should be a 1-D array.

required

Returns:

Type Description
bool

Indicates if data was successfully added to the histogram

Source code in src/spyral_utils/plot/histogram.py
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
def fill_hist2d(self, name: str, x_data: np.ndarray, y_data: np.ndarray) -> bool:
    """Fill a Hist1D with some data

    The parameters x_data and y_data should have the same length.

    Parameters
    ----------
    name: str
        The name of the Hist1D
    x_data: ndarray
        The x coordinates of the data. Should be a 1-D array.
    y_data: ndarray
        The y coordinates of the data. Should be a 1-D array.

    Returns
    -------
    bool
        Indicates if data was successfully added to the histogram

    """
    if name not in self.histograms:
        return False

    hist = self.histograms[name]
    if type(hist) is not Hist2D:
        return False
    counts, _, _ = np.histogram2d(
        x_data.flatten(), y_data.flatten(), bins=(hist.x_bins, hist.y_bins)  # type: ignore
    )
    hist.counts += counts.T
    return True

get_hist1d(name)

Retrieve a Hist1D by name

Parameters:

Name Type Description Default
name str

The name of the histogram

required

Returns:

Type Description
Hist1D | None

Returns Hist1D if a Hist1D exists with the given name. Otherwise returns None.

Source code in src/spyral_utils/plot/histogram.py
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
def get_hist1d(self, name: str) -> Hist1D | None:
    """Retrieve a Hist1D by name

    Parameters
    ----------
    name: str
        The name of the histogram

    Returns
    -------
    Hist1D | None
        Returns Hist1D if a Hist1D exists with the given name. Otherwise returns None.

    """
    if name not in self.histograms:
        return None

    hist = self.histograms[name]
    if type(hist) is not Hist1D:
        return None
    else:
        return hist

get_hist2d(name)

Retrieve a Hist2D by name

Parameters:

Name Type Description Default
name str

The name of the histogram

required

Returns:

Type Description
Hist2D | None

Returns Hist2D if a Hist2D exists with the given name. Otherwise returns None.

Source code in src/spyral_utils/plot/histogram.py
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
def get_hist2d(self, name: str) -> Hist2D | None:
    """Retrieve a Hist2D by name

    Parameters
    ----------
    name: str
        The name of the histogram

    Returns
    -------
    Hist2D | None
        Returns Hist2D if a Hist2D exists with the given name. Otherwise returns None.

    """
    if name not in self.histograms:
        return None

    hist = self.histograms[name]
    if type(hist) is not Hist2D:
        return None
    else:
        return hist