# Tai Sakuma <tai.sakuma@gmail.com>
from .ReturnTrue import ReturnTrue
##__________________________________________________________________||
[docs]class Binning(object):
[docs] def __init__(self, boundaries=None, lows=None, ups=None,
retvalue='lowedge', bins=None, underflow_bin=None, overflow_bin=None,
valid=ReturnTrue()):
if boundaries is None:
if lows is None or ups is None:
raise ValueError("Only either boundaries or pairs of lows and ups need to be given!")
if not tuple(lows[1:]) == tuple(ups[:-1]):
raise ValueError("Boundaries cannot be determined from lows=" + str(lows) + " and ups=" + str(ups))
self.boundaries = tuple(lows) + (ups[-1], )
self.lows = tuple(lows)
self.ups = tuple(ups)
else:
if lows is not None or ups is not None:
raise ValueError("Only either boundaries or pairs of lows and ups need to be given!")
if len(boundaries) < 2:
raise ValueError("Needs at least one bin! boundaries=" + str(boundaries))
self.boundaries = tuple(boundaries)
self.lows = tuple(boundaries[:-1])
self.ups = tuple(boundaries[1:])
supportedRetvalues = ('number', 'lowedge')
if retvalue not in supportedRetvalues:
raise ValueError("The retvalue '%s' is not supported! " % (retvalue, ) + "Supported values are '" + "', '".join(supportedRetvalues) + "'")
self.lowedge = (retvalue == 'lowedge')
if self.lowedge:
if bins is not None: raise ValueError("bins cannot be given when retvalue is '" + retvalue + "'!")
if underflow_bin is not None: raise ValueError("underflow_bin cannot be given when retvalue is '" + retvalue + "'!")
if overflow_bin is not None: raise ValueError("overflow_bin cannot be given when retvalue is '" + retvalue + "'!")
if self.lowedge:
self.bins = self.lows
self.underflow_bin = float("-inf")
self.overflow_bin = self.ups[-1]
else:
self.bins = bins if bins is not None else tuple(range(1, len(self.lows) + 1))
self.underflow_bin = underflow_bin if underflow_bin is not None else min(self.bins) - 1
self.overflow_bin = overflow_bin if overflow_bin is not None else max(self.bins) + 1
self._valid = valid
def __repr__(self):
return '{}(boundaries={!r}, underflow_bin={!r}, overflow_bin={!r}, valid={!r})'.format(
self.__class__.__name__,
self.boundaries, self.underflow_bin, self.overflow_bin, self._valid
)
def __call__(self, val):
if not self._valid(val): return None
if val < self.lows[0]: return self.underflow_bin
if self.ups[-1] <= val: return self.overflow_bin
return [b for b, l, u in zip(self.bins, self.lows, self.ups) if l <= val < u][0]
def next(self, bin):
if self.lowedge:
# call self._call__() to ensure that the 'bin' is indeed one of the
# bins.
bin = self.__call__(bin)
if bin == self.underflow_bin: return self.bins[0]
if bin == self.overflow_bin: return self.overflow_bin
if bin == self.bins[-1]: return self.overflow_bin
return self.bins[self.bins.index(bin) + 1]
##__________________________________________________________________||