diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index bf20f7f7..91839552 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -3,10 +3,12 @@ ## UPCOMING * Fix scaling a weighted storage [#559][] +* Fix partial summation over a Categorical axis [#564][] * Support running type checking from Python < 3.8 [#542][] [#542]: https://github.com/scikit-hep/boost-histogram/pull/542 [#559]: https://github.com/scikit-hep/boost-histogram/pull/559 +[#564]: https://github.com/scikit-hep/boost-histogram/pull/564 ## Version 1.0 diff --git a/src/boost_histogram/_internal/axis.py b/src/boost_histogram/_internal/axis.py index f109eb07..d88113af 100644 --- a/src/boost_histogram/_internal/axis.py +++ b/src/boost_histogram/_internal/axis.py @@ -165,15 +165,23 @@ def _process_loc( Compute start and stop into actual start and stop values in Boost.Histogram. None -> -1 or 0 for start, -> len or len+1 for stop. If start or stop are callable, then call them with the axes. + + For a non-ordered axes, flow is all or nothing, so this will ensure overflow + is turned off if underflow is not None. """ def _process_internal(item: Optional[AxCallOrInt], default: int) -> int: return default if item is None else item(self) if callable(item) else item - begin = _process_internal(start, -1 if self._ax.traits_underflow else 0) - end = _process_internal( - stop, len(self) + (1 if self._ax.traits_overflow else 0) - ) + underflow = -1 if self._ax.traits_underflow else 0 + overflow = 1 if self._ax.traits_overflow else 0 + + # Non-ordered axes only use flow if integrating from None to None + if not self._ax.traits_ordered and not (start is None and stop is None): + overflow = 0 + + begin = _process_internal(start, underflow) + end = _process_internal(stop, len(self) + overflow) return begin, end diff --git a/src/register_algorithm.cpp b/src/register_algorithm.cpp index ed339b86..fbf2e6d8 100644 --- a/src/register_algorithm.cpp +++ b/src/register_algorithm.cpp @@ -31,7 +31,7 @@ void register_algorithms(py::module& algorithm) { self.begin.index, self.end.index, merge, - self.crop); + self.crop ? "slice_mode.crop" : "slice_mode.shrink"); } else { return py:: str("reduce_command(shrink{0}({1}, lower={2}, upper={3}{4}))") diff --git a/tests/test_histogram_indexing.py b/tests/test_histogram_indexing.py index bb24f0b5..3429b9fb 100644 --- a/tests/test_histogram_indexing.py +++ b/tests/test_histogram_indexing.py @@ -377,3 +377,30 @@ def test_axes_tuple_Nd(): assert b1.ndim == 3 assert a1.ndim == 2 + + +# issue 556 +def test_single_flow_bin(): + # Flow is removed for category axes unless full sum is used + h = bh.Histogram(bh.axis.IntCategory([0, 1, 2])) + h.view(True)[:] = 1 + + assert h[::sum] == 4 + assert h[0::sum] == 3 + assert h[1::sum] == 2 + assert h[2::sum] == 1 + with pytest.raises(ValueError): + h[3::sum] + + assert h[1:2][sum] == 4 + + h = bh.Histogram(bh.axis.Integer(0, 3)) + h.view(True)[:] = 1 + + assert h[::sum] == 5 + assert h[0::sum] == 4 + assert h[1::sum] == 3 + assert h[2::sum] == 2 + assert h[3::sum] == 1 + + assert h[1:2][sum] == 5