# Tai Sakuma <tai.sakuma@gmail.com>
import math
from .Round import Round
##__________________________________________________________________||
[docs]class RoundLog(object):
"""Binning with equal width in log scale
Parameters
----------
width : float or int, default 1
The common logarithm (log10) of the width.
aboundary : float or int, optional
A boundary. If not given, ``width/2`` will be used.
min : float or int, optional
The lowest bin will be the bin that ``min`` falls in. It must be a
positive value. If given, ``__call__(val)`` returns ``underflow_bin``
if the ``val`` is less than the lower edge of the lowest bin.
underflow_bin : optional
The underflow bin. When ``min`` is given, the ``__call__(val)`` returns
``underflow_bin`` if the ``val`` is less than the lower edge of the
lowest bin.
max : float or int, optional
The highest bin will be the bin that ``max`` falls in except when
``max`` is one of boundaries. It must be a positive value. When ``max``
is one of boundaries, the highest bin is the bin whose upper edge is
``max``. If given, ``__call__(val)`` returns the overflow bin if the
``val`` is greater than or equal to the upper edge of the highest bin.
overflow_bin : optional
The overflow bin if ``overflow_bin`` is any value other than ``True``.
If ``overflow_bin`` is ``True``, the overflow bin will be the upper
edge of the highest bin. When ``max`` is given, the ``__call__(val)``
returns the overflow bin if the ``val`` is greater than or equal to the
upper edge of the highest bin.
valid : function, optional
Boolean function to test if value is valid
"""
[docs] def __init__(self, width=0.1, aboundary=1,
min=None, underflow_bin=None,
max=None, overflow_bin=None,
valid=None):
self._round = Round(width=width, aboundary=math.log10(aboundary))
self.width = width
self.aboundary = aboundary
self.min = min
self.max = max
self.valid = valid
if self.min is None:
self.min_bin_log10_lowedge = None
self.underflow_bin = None
else:
self.min_bin_log10_lowedge = self._round(math.log10(self.min))
self.underflow_bin = underflow_bin
if self.max is None:
self.max_bin_log10_upedge = None
self.overflow_bin = None
else:
self._round(math.log10(self.max)) # = self._round.boundaries[-2]
self.max_bin_log10_upedge = self._round.boundaries[-1]
if overflow_bin is True:
self.overflow_bin = 10**self.max_bin_log10_upedge
else:
self.overflow_bin = overflow_bin
def __repr__(self):
return '{}(width={!r}, aboundary={!r}, min={!r}, underflow_bin={!r}, max={!r}, overflow_bin={!r}, valid={!r})'.format(
self.__class__.__name__,
self.width,
self.aboundary,
self.min,
self.underflow_bin,
self.max,
self.overflow_bin,
self.valid
)
def __call__(self, val):
if self.valid:
if not self.valid(val):
return None
if val <= 0.0:
if self.min is not None:
return self.underflow_bin
elif val == 0.0:
return 0
else:
return None
if self.min is not None:
if math.log10(val) < self.min_bin_log10_lowedge:
return self.underflow_bin
if math.isinf(val):
if self.max is not None:
return self.overflow_bin
else:
return None
if self.max is not None:
if self.max_bin_log10_upedge <= math.log10(val):
return self.overflow_bin
val = math.log10(val)
val = self._round(val)
if val is None:
return None
return 10**val
def next(self, bin):
if bin is None:
return None
if bin == self.underflow_bin:
return self.__call__(self.min)
if bin < 0:
return None
if bin == 0:
return 0
if bin == self.overflow_bin:
return self.overflow_bin
log10_bin = self._round(math.log10(bin))
if log10_bin is None:
return None
log10_next = self._round.next(log10_bin)
if self.max is not None:
if log10_next == self.max_bin_log10_upedge:
return self.overflow_bin
return 10**log10_next
##__________________________________________________________________||