Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: temporary patch on broken sum with empty axis #718

Merged
merged 1 commit into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions src/boost_histogram/_core/accumulators.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Tuple, TypeVar
from typing import Any, Tuple, TypeVar, overload

from numpy.typing import ArrayLike

Expand All @@ -14,7 +14,12 @@ class _BaseAccumulator:
def _ipython_key_completions_(self) -> Tuple[str, ...]: ...

class WeightedSum(_BaseAccumulator):
def __init__(self, value: float | None, variance: float | None) -> None: ...
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, value: float) -> None: ...
@overload
def __init__(self, value: float, variance: float) -> None: ...
@property
def value(self) -> float: ...
@property
Expand All @@ -29,7 +34,10 @@ class WeightedSum(_BaseAccumulator):
def __setitem__(self, key: str, value: float) -> None: ...

class Sum(_BaseAccumulator):
def __init__(self, value: float | None) -> None: ...
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, value: float) -> None: ...
@property
def value(self) -> float: ...
def __iadd__(self: T, arg0: float) -> T: ...
Expand All @@ -40,6 +48,9 @@ class Sum(_BaseAccumulator):
def _large(self) -> float: ...

class WeightedMean(_BaseAccumulator):
@overload
def __init__(self) -> None: ...
@overload
def __init__(
self,
sum_of_weights: float,
Expand Down Expand Up @@ -73,6 +84,9 @@ class WeightedMean(_BaseAccumulator):
def __setitem__(self, key: str, value: float) -> None: ...

class Mean(_BaseAccumulator):
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, count: float, value: float, variance: float) -> None: ...
@property
def count(self) -> float: ...
Expand Down
3 changes: 3 additions & 0 deletions src/boost_histogram/_internal/hist.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,9 @@ def sum(self, flow: bool = False) -> Union[float, Accumulator]:
"""
Compute the sum over the histogram bins (optionally including the flow bins).
"""
if any(x == 0 for x in (self.axes.extent if flow else self.axes.size)):
return self._storage_type.accumulator()

return self._hist.sum(flow) # type: ignore[no-any-return]

@property
Expand Down
27 changes: 20 additions & 7 deletions src/boost_histogram/_internal/storage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from typing import ClassVar, Type, Union

import boost_histogram

from .._core import accumulators
from .._core import storage as store
from .utils import set_module

Expand All @@ -15,37 +18,47 @@ def __init_subclass__(cls, *, family: object) -> None:
def __repr__(self) -> str:
return f"{self.__class__.__name__}()"

accumulator: ClassVar[
Union[
Type[int],
Type[float],
Type[accumulators.WeightedMean],
Type[accumulators.WeightedSum],
Type[accumulators.Mean],
]
]


@set_module("boost_histogram.storage")
class Int64(store.int64, Storage, family=boost_histogram):
pass
accumulator = int


@set_module("boost_histogram.storage")
class Double(store.double, Storage, family=boost_histogram):
pass
accumulator = float


@set_module("boost_histogram.storage")
class AtomicInt64(store.atomic_int64, Storage, family=boost_histogram):
pass
accumulator = int


@set_module("boost_histogram.storage")
class Unlimited(store.unlimited, Storage, family=boost_histogram):
pass
accumulator = float


@set_module("boost_histogram.storage")
class Weight(store.weight, Storage, family=boost_histogram):
pass
accumulator = accumulators.WeightedSum


@set_module("boost_histogram.storage")
class Mean(store.mean, Storage, family=boost_histogram):
pass
accumulator = accumulators.Mean


@set_module("boost_histogram.storage")
class WeightedMean(store.weighted_mean, Storage, family=boost_histogram):
pass
accumulator = accumulators.WeightedMean
22 changes: 22 additions & 0 deletions tests/test_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,25 @@ def test_UHI_variance_counts():
h.fill(0.5, sample=[1], weight=[0.5])
h.fill(0.5, sample=[1], weight=[0.5])
assert not math.isnan(h.variances()[0])


@pytest.mark.parametrize(
"storage",
[
bh.storage.Int64,
bh.storage.Double,
bh.storage.AtomicInt64,
bh.storage.Unlimited,
bh.storage.Weight,
bh.storage.Mean,
bh.storage.WeightedMean,
],
)
def test_empty_axis_histogram(storage):
h2d = bh.Histogram(
bh.axis.Regular(10, 0, 10),
bh.axis.StrCategory([], growth=True),
storage=storage(),
)
assert h2d.sum() == h2d._storage_type.accumulator()
assert h2d.sum(flow=True) == h2d._storage_type.accumulator()