Skip to content

Commit 5688702

Browse files
committed
fix: slicing supports under/overflow (#576)
1 parent 6765aac commit 5688702

File tree

3 files changed

+55
-11
lines changed

3 files changed

+55
-11
lines changed

docs/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# What's new in boost-histogram
22

3+
## UPCOMING
4+
5+
* Fix "picking" on a flow bin [#576][]
6+
7+
[#576]: https://github.com/scikit-hep/boost-histogram/pull/576
8+
39

410
## Version 0.13
511

src/boost_histogram/_internal/hist.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -684,16 +684,20 @@ def __getitem__(self, index): # noqa: C901
684684

685685
integrations = set()
686686
slices = []
687+
pick_each = dict()
687688

688689
# Compute needed slices and projections
689690
for i, ind in enumerate(indexes):
690691
if hasattr(ind, "__index__"):
691-
ind = slice(ind.__index__(), ind.__index__() + 1, sum)
692-
692+
pick_each[i] = ind.__index__() + ( # type: ignore
693+
1 if self.axes[i].traits.underflow else 0
694+
)
695+
continue
693696
elif not isinstance(ind, slice):
694697
raise IndexError(
695698
"Must be a slice, an integer, or follow the locator protocol."
696699
)
700+
697701
# If the dictionary brackets are forgotten, it's easy to put a slice
698702
# into a slice - adding a nicer error message in that case
699703
if any(isinstance(v, slice) for v in (ind.start, ind.stop, ind.step)):
@@ -732,16 +736,25 @@ def __getitem__(self, index): # noqa: C901
732736
logger.debug("Reduce with %s", slices)
733737
reduced = self._hist.reduce(*slices)
734738

735-
if not integrations:
736-
return self._new_hist(reduced)
737-
else:
738-
projections = [i for i in range(self.ndim) if i not in integrations]
739-
740-
return (
741-
self._new_hist(reduced.project(*projections))
742-
if projections
743-
else reduced.sum(flow=True)
739+
if pick_each:
740+
my_slice = tuple(
741+
pick_each.get(i, slice(None)) for i in range(reduced.rank())
744742
)
743+
logger.debug("Slices: %s", my_slice)
744+
axes = [
745+
reduced.axis(i) for i in range(reduced.rank()) if i not in pick_each
746+
]
747+
logger.debug("Axes: %s", axes)
748+
new_reduced = reduced.__class__(axes)
749+
new_reduced.view(flow=True)[...] = reduced.view(flow=True)[my_slice]
750+
reduced = new_reduced
751+
integrations = {i - sum(j <= i for j in pick_each) for i in integrations}
752+
753+
if integrations:
754+
projections = [i for i in range(reduced.rank()) if i not in integrations]
755+
reduced = reduced.project(*projections)
756+
757+
return self._new_hist(reduced) if reduced.rank() > 0 else reduced.sum(flow=True)
745758

746759
def __setitem__(self, index, value):
747760
"""

tests/test_histogram_indexing.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import numpy as np
33
import pytest
44
from numpy.testing import assert_array_equal
5+
from pytest import approx
56

67
import boost_histogram as bh
78

@@ -352,6 +353,30 @@ def test_pick_int_category():
352353
assert_array_equal(h[:, :, bh.loc(7)].view(), 0)
353354

354355

356+
@pytest.mark.parametrize(
357+
"ax",
358+
[bh.axis.Regular(3, 0, 1), bh.axis.Variable([0, 0.3, 0.6, 1])],
359+
ids=["regular", "variable"],
360+
)
361+
def test_pick_flowbin(ax):
362+
w = 1e-2 # e.g. a cross section for a process
363+
x = [-0.1, -0.1, 0.1, 0.1, 0.1]
364+
y = [-0.1, 0.1, -0.1, -0.1, 0.1]
365+
366+
h = bh.Histogram(
367+
ax,
368+
ax,
369+
storage=bh.storage.Weight(),
370+
)
371+
h.fill(x, y, weight=w)
372+
373+
uf_slice = h[bh.tag.underflow, ...]
374+
assert uf_slice.values(flow=True) == approx(np.array([1, 1, 0, 0, 0]) * w)
375+
376+
uf_slice = h[..., bh.tag.underflow]
377+
assert uf_slice.values(flow=True) == approx(np.array([1, 2, 0, 0, 0]) * w)
378+
379+
355380
def test_axes_tuple():
356381
h = bh.Histogram(bh.axis.Regular(10, 0, 1))
357382
assert isinstance(h.axes[:1], bh._internal.axestuple.AxesTuple)

0 commit comments

Comments
 (0)