""" Provide various utilties not found in Python 2.7 or numpy 1.5 """ from __future__ import division from __future__ import print_function from __future__ import with_statement import functools import operator import numpy as np import copy import datetime import os import struct import time import logging import sys from collections import Iterable def cumsum(list,seed=0): """Return List with cumulative sum starting at seed (exclusive).""" return [x for x in cumsum_iter(list,seed)] def cumsum0(list,seed=0): """Return List with cumulative sum starting at seed (inclusive).""" return [x for x in cumsum0_iter(list,seed)] def cumsum_iter(list,seed=0): """Cumulative sum starting at seed (default 0).""" t = seed for l in list: t += l yield t def cumsum0_iter(list,seed=0): """Iterator for cumulative sum starting at seed (default 0).""" t = seed yield t for l in list: t += l yield t def cumsum0_range_iter(list,seed=0): """Iterator for cumulative sum duets for slicing starting at seed (default 0).""" t1 = seed for l in list: t0 = t1 t1 += l yield t0,t1 def cumsum0_enum_range_iter(list,seed=0): """Iterator for cumulative sum and counter triples for slicing starting at seed (default 0).""" t1 = seed for i,l in enumerate(list): t0 = t1 t1 += l yield i,t0,t1 def prod(seq): """Product of a sequence.""" if not np.iterable(seq): seq = (seq,) return functools.reduce(operator.mul, seq, 1) def contract(a, sequence, axis=0, dimension = None): """ Contract array from list. typical use would be: categories, members = np.unique(X.index, return_inverse = True) result = contract(X.values, members) replacing: result = np.zeros((..., len(categories), ...), dtype = np.float64) values = X.values for i,j in enumerate(members): result[..., j,...] += values[..., i,...] """ shape = np.array(a.shape) ii = [slice(i) for i in shape] jj = copy.deepcopy(ii) if axis == -1: axis += a.ndim axis_dimension = np.amax(sequence) + 1 if dimension == None: dimension = axis_dimension else: assert dimension >= axis_dimension, "Target dimesnion too small." shape[axis] = dimension out = np.zeros(shape) for i,j in enumerate(sequence): jj[axis] = j ii[axis] = i out[jj] += a[ii] return out def project(a, values, axis=0, return_values = False, return_inverse = False): """ Project array onto values. """ k, kinv = np.unique(values, return_inverse = True) p = (contract(a, kinv),) if return_values: p.append(k) if return_inverse: p.append(kinv) if len(p) == 1: p = p[0] return p def isinslice(index, Slice): """ Determine whether index is part of a slice. """ start, stop, stride = Slice.indices(index + 1) if (index - start) % stride != 0: return False if stride < 0: return (start >= index > stop) else: return (start <= index < stop) def bool2sign(true): return 1 if true else -1 def sign(integer): return cmp(integer,0) def bool2slice(true): return slice(None,None,1) if true else slice(None,None,-1) class UTCFormatter(logging.Formatter): converter = time.gmtime def format(self, record): record.nameb = '[{:s}]'.format(record.name) return super(UTCFormatter, self).format(record) class Slice(object): """ Slice iterator object. """ def __init__(self, *args, **kwargs): """ Construct from slice indices or slice object. Provide optional object size. """ if len(args) == 1 and isinstance(args[0],slice): self.slice = args[0] else: self.slice = slice(*args) self.size = kwargs.pop('size', None) assert len(kwargs) == 0 def __iter__(self): """ Slice object iterator. """ if self.slice.step == None: self.slice.step = 1 if self.size is None: self.size = max(self.slice.start, self.slice.stop) + 1 xslice = self.slice.indices(self.size) for i in xrange(*xslice): yield i def iter(self, size = None): """ Return iterator with defined object size. """ size = self.size for i in self.__iter__(): yield i self.size = size def cachedmethod(method, *args, **kwargs): """ Decorator to compute a quantity on first call only. The data is stored in '_'+method.__name__ Use: class X(parent_object): ... @cachedmethod def method(parameters): ... Use function clear_cachedmethod to delete data field. if you have an instance x of your class X: x = X(init_parameters) you would call clear_cachedmethod(x.method) """ #@functools.wraps(method) def cached_method(self, *args, **kwargs): """ Method only to be called first time or when parameters change. """ recalc = kwargs.pop('recalc', False) if recalc: clear(self) reload = not self.__dict__.has_key('_'+method.__name__) if method.func_code.co_argcount > 1: args_name = '_'+method.__name__+'_args' kwargs_name = '_'+method.__name__+'_kwargs' if not reload: if self.__dict__.get(args_name, None) != args: self.__dict__[args_name] = args reload = True if self.__dict__.get(kwargs_name, None) != kwargs: self.__dict__[kwargs_name] = kwargs reload = True else: if not args_name in self.__dict__: self.__dict__[args_name] = args if not kwargs_name in self.__dict__: self.__dict__[kwargs_name] = kwargs if reload: self.__dict__['_'+method.__name__] = \ method(self, *args, **kwargs) return self.__dict__['_'+method.__name__] def clear(self): """ Clear storage. Requires class instance passed explicitly. >>> x.method.clear(x) """ self.__dict__.pop('_'+method.__name__, None) self.__dict__.pop('_'+method.__name__+'_args', None) self.__dict__.pop('_'+method.__name__+'_kwargs', None) cached_method.__dict__.update(method.__dict__) cached_method.__dict__['clear'] = clear cached_method.__dict__['method'] = method.__name__ if method.__doc__ is not None: cached_method.__doc__ = method.__doc__ + '\n' + cached_method.__doc__ cached_method.__name__ = method.__name__ cached_method.__module__ = getattr(method, '__module__') return cached_method def clear_cachedmethod(method): """ Clear the stored data for a method created with the @firstcall decorator. >>> clear_cachedmethod(x.method) """ method.clear(method.im_self) class CachedAttribute(object): """ Computes attribute value and caches it in the instance. Source: http://stackoverflow.com/questions/3237678/how-to-create-decorator-for-lazy-initialization-of-a-property Reference as given in source: http://code.activestate.com/recipes/276643-caching-and-aliasing-with-descriptors/ Use 'del inst.myMethod' to clear cache. """ def __init__(self, method, name = None): self.method = method self.name = name or method.__name__ def __get__(self, obj, objtype): if obj is None: return self elif self.name in obj.__dict__: return obj.__dict__[self.name] else: value = self.method(obj) obj.__dict__[self.name] = value return value def __set__(self, obj, value): raise AttributeError("Cannot assign to " + self.name + '.') def __delete__(self, obj): del obj.__dict__[self.name] class CachedClassAttribute(object): """ Computes class attribute value and caches it in the class. Source: http://stackoverflow.com/questions/3237678/how-to-create-decorator-for-lazy-initialization-of-a-property Reference as given in source: http://code.activestate.com/recipes/276643-caching-and-aliasing-with-descriptors/ Use 'del MyClass.myMethod' to clear cache. """ # has not been tested ... def __init__(self, method, name = None): self.method = method self.name = name or method.__name__ def __get__(self, obj, objtype): if self.name in objtype.__dict__: return objtype.__dict__[self.name] else: value = self.method(objtype) objtype.__dict__[self.name] = value return value def __set__(self, obj, value): raise AttributeError("Cannot assign to " + self.name + '.') def __delete__(self, objtype): # does this really pass type? del objtype.__dict__[self.name] def Property(func): """ Use: class Person(object): @Property def name(): doc = "The person name" def fget(self): return _name def fset(self, name): self._name = name def fdel(self): del self.last_name return locals() """ return property(**func()) from types import MethodType def make_cached_attribute(self, func, name = None, doc = None, args = None, kw = None): """ Add cached attribute to class dynamaically. EXAMPLE: (call in __init__) def var(self, idx): return np.array([x.output[idx] for x in self.data]) make_cached_attribute(self.__class__, functools.partial(var,idx=21), 'xh','central XH') """ if args is None: args = list() if kw is None: kw = dict() setattr(self, name + '_kw', kw) setattr(self, name + '_args', args) def f(self): kw = self.__getattribute__(name + '_kw') args = self.__getattribute__(name + '_args') return func(self, *args, **kw) f.__doc__ = doc f.im_class = self.__class__ f.im_func = f f.im_self = None f = CachedAttribute(f, name) # def __get__(self, instance, owner): # return MethodType(self, instance, owner) # f.__get__ = __get__ setattr(self.__class__, name, f) class OutFile(object): """ Contex Manager: Open file if filename is given or use file. """ def __init__(self, outfile, silent = False, overwrite = False): """ TODO - open os.stdout if file does not exist? """ # self.setup_logger(silent = silent) self.open = isinstance(outfile, file) if not self.open: filename = os.path.expanduser(os.path.expandvars(outfile)) assert overwrite or not os.path.exists(filename) f = open(filename,'w') else: f = outfile self.f = f def __enter__(self): return self.f def __exit__(self, exc_type, exc_val, exc_tb): if self.open: f.close() # self.close_logger() def xz_file_size(filename): """ Return file size of xz xompressed files. The file format is documented at "http://tukaani.org/xz/xz-file-format.txt" """ def decode(buffer, index = 0): """ Decode variable length integers from buffer. """ i = 0 size = 0 b = 0x80 while b & 0x80 > 0: b, = struct.unpack('B',buffer[index+i]) size += (b & 0x7f) << (7 * i) i += 1 return size, index+i with open(filename) as f: f.seek(-8,os.SEEK_END) bkwd_size = np.ndarray((), dtype = " {mass: 1}, {mass: 2} {('A','B'): X} --> {'A': X}, {'B': X} mayeb should have separate keywors for list/tuple and dictionary resolution (depeth) """ no_resolve = (str, set) class _multi_loop_container(object): def __init__(self, item, level = 0): self.item = item self.level = level def multi_loop(self, method, *args, **kwargs): """ Loops over all iterable parameters except strings and sets. """ kwargs_new = dict(kwargs) args_new = list(args) descend = kwargs_new.setdefault('loop_descend', 1) del kwargs_new['loop_descend'] if descend <= 0: return [method(*args_new, **kwargs_new)] result = [] for iv,v in enumerate(args): if isinstance(v, self.no_resolve): continue level = 1 if isinstance(v, self._multi_loop_container): if v.level == descend: continue level = v.level + 1 v = v.item if isinstance(v, dict): if len(v) <= 1: continue for k,i in v.iteritems(): args_new[iv] = {k:i} result += self.multi_loop(method, *args_new, loop_descend = descend, **kwargs_new) return result if isinstance(v, Iterable): for i in v: args_new[iv] = self._multi_loop_container(i, level) result += self.multi_loop(method, *args_new, loop_descend = descend, **kwargs_new) return result for kw,v in kwargs.iteritems(): if isinstance(v, self.no_resolve): continue level = 1 if isinstance(v, self._multi_loop_container): if v.level == descend: continue level = v.level + 1 v = v.item if isinstance(v, dict): if len(v) <= 1: continue for k,i in v.iteritems(): kwargs_new[kw] = {k:i} result += self.multi_loop(method, *args_new, loop_descend = descend, **kwargs_new) return result if isinstance(v, Iterable): for i in v: kwargs_new[kw] = self._multi_loop_container(i,level) result += self.multi_loop(method, *args_new, loop_descend = descend, **kwargs_new) return result # get rid of containers for iv,v in enumerate(args_new): if isinstance(v, self._multi_loop_container): args_new[iv] = v.item for kw,v in kwargs_new.iteritems(): if isinstance(v, self._multi_loop_container): kwargs_new[kw] = v.item return [method(*args_new, **kwargs_new)] def loopmethod(descend = 1): """ Decorator to compute a looped method. Use: @loopmethod(descend) method_to_loop If descend is False, stope at first level, otherwise descend down nested lists, sets, and tuples. Call: self.method_to_loop(*args,**kwargs) Returns list of results. TODO: could add "str" parameter to resolve strings """ def loop_method(method): """ Decorator to compute a looped method. Use: @loopedmethod method_to_loop Call: self.method_to_loop(*args,**kwargs) """ no_resolve = (str, set) class _container(object): def __init__(self, item, level): self.item = item self.level = level # @wraps(method) def looped_method(self, *args, **kwargs): """ Loop over all Iterables in *args and **kwargs except strings and sets. """ if descend <= 0: return [method(self, *args, **kwargs)] kwargs_new = dict(kwargs) args_new = list(args) result = [] for iv,v in enumerate(args): if isinstance(v, self.no_resolve): continue level = 1 if isinstance(v, _container): if v.level == descend: continue level = v.level + 1 v = v.item if isinstance(v, dict): if len(v) <= 1: continue for k,i in v.iteritems(): args_new[iv] = {k:i} result += looped_method(self, *args_new, **kwargs_new) return result if isinstance(v, Iterable): for i in v: args_new[iv] = _container(i, level) result += looped_method(self, *args_new, **kwargs_new) return result for kw,v in kwargs.iteritems(): if isinstance(v, self.no_resolve): continue level = 1 if isinstance(v, _container): if v.level == descend: continue level = v.level + 1 v = v.item if isinstance(v, dict): if len(v) <= 1: continue for k,i in v.iteritems(): kwargs_new[kw] = {k:i} result += looped_method(self, *args_new, **kwargs_new) return result if isinstance(v, Iterable): for i in v: kwargs_new[kw] = _container(i, level) result += looped_method(self, *args_new, **kwargs_new) return result # get rid of containers for iv,v in enumerate(args_new): if isinstance(v, _container): args_new[iv] = v.item for kw,v in kwargs_new.iteritems(): if isinstance(v, _container): kwargs_new[kw] = v.item return [method(self, *args_new, **kwargs_new)] looped_method.__dict__.update(method.__dict__) looped_method.__dict__['method'] = method.__name__ if method.__doc__ is not None: looped_method.__doc__ = method.__doc__ + '\n' + looped_method.__doc__ looped_method.__name__ = method.__name__ looped_method.__module__ = getattr(method, '__module__') return looped_method return loop_method # class test(object): # def __init__(self, *args, **kwargs): # X = self.x(*args, **kwargs) # print(X) # @loopmethod(3) # def x(self,*args, **kwargs): # print(args,kwargs) class test(MultiLoop): def __init__(self, *args, **kwargs): X = self.multi_loop(self.x, *args, loop_descend = 2, **kwargs) print(X) def x(self,*args, **kwargs): base = kwargs.setdefault('base',None) print(base.values()) print(args,kwargs) def float2str(f, precision = 13): """ Use g format but add '.' to be compatible with KEPLER """ s = ("{:."+str(precision)+"g}").format(f) if ((s.find('.') == -1) and (s.find('e') == -1)): s += '.' if ((s.find('.') == -1) and (s.find('e') != -1)): s = s.replace('e','.e') return s def bit_count(x): assert x>=0, 'negative numbers have infinite number of leading bits' count = 0 bit = 1 while bit <= x: count += int((x & bit) > 0) bit <<= 1 return count def queue_processor(input, output, params): """ Worker thread to process data from queue. Assume input is multiprocessing.JoinableQueue or Queue.Queue. call signature queue_processor(input, output, params) input is a Queue provides get() and task_done() output is a Queue that provides put() params should be a dictionary that contains data basic initialization data could be a large data set to operate on processor a class that is initialzed with data __init__(data) it is called to provide results that are put in out queue __call__(task) """ # do we really need a dictionary? processor = params.get('processor')( params.get('data', None)) # just to make sure we remove unnecessay references # as we may have many copies in parallel processes del params for task in iter(input.get, 'STOP'): output.put(processor(task)) input.task_done() input.task_done() def loader(c, name = None): """ Return load function for class. Usage: (for example) in file: loadlc = loader(LCData) command line lcdata = loadlc('filename') """ if name is None: name = c.__name__ def load(*args, **kwargs): """ Check whether file excists and load lc file. Return None if dump file does not exist. """ try: return c(*args, **kwargs) except IOError: filename = args[0] # should this be done with a logger? print(' [{:s}] ERROR: File "{:s}" not found.'.format( name, filename)) return None return load def stuple(*args): """ convert string to tuple with one element, list to tuple, None to empty tuple, leave tuple unchanged. """ out = () for a in args: if isinstance(a, str): s = (' ' + a + ' ').splitlines() s[0] = s[0][1:] s[-1] = s[-1][:-1] out += tuple(s) elif a is not None: assert isinstance(a, (list, tuple, np.ndarray)) for b in a: out += stuple(b) return out import physconst def ergs2mbol(ergs): return +4.77 - log10(ergs / physconst.XLSUN) * 2.5 # the following now is a singleton metaclass class SMeta(type): """ Usage: __metaclass__ = SMeta """ def __call__(*args): cls = args[0] key = args[1:] try: cache = cls._cache except: cache = dict() cls._cache = cache try: obj = cache[key] except: obj = type.__call__(*args) cache[key] = obj return obj