Source code for Karma.PostProcessing.Lumberjack._core
from __future__ import print_function
import numpy as np
from collections import OrderedDict
from copy import deepcopy
__all__ = ['Quantity', 'apply_defines', 'apply_filters', 'define_quantities']
[docs]class Quantity(object):
__slots__ = ('_dict',)
def __init__(self, name, expression, binning, named_binnings=None):
self._dict = dict(
name=name,
expression=expression,
binning=binning,
named_binnings=named_binnings,
)
def __repr__(self):
return "{}({})".format(
self.__class__.name,
", ".join(["{}={}".format(_k, repr(_v)) for _k, _v in self._dict.iteritems()])
)
@property
def range(self):
return (self.binning[0], self.binning[-1])
@property
def binning(self):
"""The default binning for this quantity. Always defined, even if no special "named binnings" are not."""
return self._dict['binning']
@property
def named_binnings(self):
"""Named binnings defined for this quantity."""
return self._dict['named_binnings']
@property
def expression(self):
return self._dict['expression']
@property
def name(self):
return self._dict['name']
[docs] def clone(self, **kwargs):
"""Create a clone of quantity, replacing properties as specified by the keyword arguments."""
_self_dict_copy = deepcopy(self._dict)
_self_dict_copy.update(kwargs)
return Quantity(**_self_dict_copy)
@property
def named_binning_keys(self):
"""Splitting keys for which named binnings have been defined for this quantity."""
_nb = self._dict.get('named_binnings', None)
if _nb is None:
return None
return _nb.keys()
[docs] def iter_bins(self, indices=slice(None)):
"""Generator function. Yields tuple (lo, hi) of bin edges for each bin. A subset may be obtained by providing a list of indices or a `slice` object."""
if isinstance(indices, slice):
# can use `slice` object for indexing binnings directly
_iter_pairs = zip(self._dict['binning'][:-1][indices], self._dict['binning'][1:][indices])
else:
# use list comrehensions to iteratr over a subset bins with explicitly given `indices`
_iter_pairs = zip(
[self._dict['binning'][:-1][_i] for _i in indices],
[self._dict['binning'][1:][_i] for _i in indices])
for _lo, _hi in _iter_pairs:
yield (_lo, _hi)
[docs] def get_named_binning(self, key, value):
"""Retrieve a binning (if defined) for the case where the splitting key `key` has value `value`. If none defined, return `None`."""
_nb = self._dict.get('named_binnings', None)
if _nb is None:
return None
_nb = _nb.get(key, None)
if _nb is None:
return None
_nb = _nb.get(value, None)
return _nb
def apply_defines(data_frame, defines):
"""Applies all 'Defines' specified in a dictionary to an data frame."""
_df = data_frame
for _k, _v in defines.iteritems():
print("[apply_defines] Defining quantity '{}': {}".format(_k, _v))
try:
_df = _df.Define(_k, _v)
except Exception as _e:
print("[apply_defines] WARNING: Error defining quantity '{}': {}".format(_k, _e))
return _df
def apply_filters(data_frame, filters):
"""Applies all 'Filters' specified in a list to an data frame."""
_df = data_frame
for _filter_expr in filters:
_df = _df.Filter(_filter_expr)
return _df
def define_quantities(data_frame, quantities):
"""Define aliases for quantity expressions as specified in dictionary `quantities`."""
_define_dict = OrderedDict() # map of quantities by unique name
for _q_key, _q in quantities.iteritems():
# skip quantities with identical names and expressions (can get by name without Define)
if _q.name == _q.expression:
continue
# check if a definition already exists for this name
if _q.name in _define_dict:
_orig_def = _define_dict[_q.name]
raise ValueError("Redefinition of quantity '{}' from '{}' to '{}'".format(_q.name, _orig_def, _q.expression))
_define_dict[_q.name] = _q.expression
_df = apply_defines(data_frame, _define_dict)
return _df