from copy import deepcopy
import os
import pickle
import pickle as _builtin_pickle
import dill as pickle
from hashlib import sha1
import numpy as np
from scipy.interpolate import interp1d
__all__ = ['inherit_docstring_from', 'combine_dicts', 'data_file_name', 'find_file_in_folders',
'read_pickle', 'save_pickle', 'hashablize', 'deterministic_hash', 'InterpolateAndExtrapolate1D',
'arrays_to_grid']
[docs]def inherit_docstring_from(cls):
"""Decorator for inheriting doc strings, stolen from
https://groups.google.com/forum/#!msg/comp.lang.python/HkB1uhDcvdk/lWzWtPy09yYJ
"""
def docstring_inheriting_decorator(fn):
fn.__doc__ = getattr(cls, fn.__name__).__doc__
return fn
return docstring_inheriting_decorator
[docs]def combine_dicts(*args, exclude=(), deep_copy=False):
"""Returns a new dict with entries from all dicts passed, with later dicts overriding earlier ones.
:param exclude: Remove these keys from the result.
:param deepcopy: Perform a deepcopy of the dicts before combining them.
"""
if not len(args):
return dict()
result = {}
for d in args:
if deep_copy:
d = deepcopy(d)
result.update(d)
result = {k: v for k, v in result.items() if k not in exclude}
return result
[docs]def data_file_name(filename, data_dirs=None):
"""Returns filename if a file exists. Also checks data_dirs for the file."""
if os.path.exists(filename):
return filename
if data_dirs is not None:
return find_file_in_folders(filename, data_dirs)
return FileNotFoundError(filename)
[docs]def find_file_in_folders(filename, folders):
"""Searches for filename in folders, then return full path or raise FileNotFoundError
Does not recurse into subdirectories
"""
if isinstance(folders, str):
folders = [folders]
for folder in folders:
full_path = os.path.join(folder, filename)
if os.path.exists(full_path):
return full_path
raise FileNotFoundError(filename)
[docs]def read_pickle(filename):
with open(filename, mode='rb') as infile:
result = pickle.load(infile)
return result
[docs]def save_pickle(stuff, filename):
"""Saves stuff in a pickle at filename"""
dirname = os.path.dirname(filename)
if dirname != '' and not os.path.exists(dirname):
os.makedirs(dirname)
with open(filename, mode='wb') as outfile:
pickle.dump(stuff, outfile)
[docs]def hashablize(obj):
"""Convert a container hierarchy into one that can be hashed.
See http://stackoverflow.com/questions/985294
"""
try:
hash(obj)
except TypeError:
if isinstance(obj, dict):
return tuple((k, hashablize(v)) for (k, v) in sorted(obj.items()))
elif isinstance(obj, np.ndarray):
return tuple(obj.tolist())
elif hasattr(obj, '__iter__'):
return tuple(hashablize(o) for o in obj)
else:
raise TypeError("Can't hashablize object of type %r" % type(obj))
else:
return obj
[docs]def deterministic_hash(thing):
"""Return a deterministic hash of a container hierarchy using hashablize, pickle and sha1"""
return sha1(_builtin_pickle.dumps(hashablize(thing))).hexdigest()
def _events_to_analysis_dimensions(events, analysis_space):
"""Return a list of arrays of the values of events in each of the analysis dimensions specified in analysis_space"""
return [events[x] for x, bins in analysis_space]
[docs]class InterpolateAndExtrapolate1D(object):
"""Extends scipy.interpolate.interp1d to do constant extrapolation outside of the data range
"""
def __init__(self, points, values):
# Support for scalar arguments
try:
points[0]
except (TypeError, IndexError):
points = np.array([points])
try:
values[0]
except (TypeError, IndexError):
values = np.array([values])
points = np.asarray(points)
assert len(points) == len(values)
if len(points) == 1:
self.interpolator = lambda x: np.ones(len(x)) * values[0]
else:
self.interpolator = interp1d(points, values)
self.min = points.min()
self.max = points.max()
def __call__(self, points):
# Support for scalar arguments
give_scalar = False
try:
points[0]
except (TypeError, IndexError):
points = np.array([points])
points = np.clip(points, self.min, self.max)
result = self.interpolator(points)
if give_scalar:
return result[0]
return result
[docs]def arrays_to_grid(arrs):
"""Convert a list of n 1-dim arrays to an n+1-dim. array, where last dimension denotes coordinate values at point.
"""
return np.stack(np.meshgrid(*arrs, indexing='ij'), axis=-1)