# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
import os
import sys
import warnings
from collections import OrderedDict
import numpy as np
from larray.core.axis import Axis
from larray.core.array import LArray, larray_nan_equal, get_axes, ndtest, zeros, zeros_like, sequence
from larray.util.misc import float_error_handler_factory, is_interactive_interpreter, renamed_to, inverseop
from larray.inout.session import check_pattern, handler_classes, ext_default_engine
# XXX: inherit from OrderedDict or LArray?
[docs]class Session(object):
"""
Groups several array objects together.
Parameters
----------
args : str or dict of str, array or iterable of tuples (str, array)
Name of file to load or dictionary containing couples (name, array).
kwargs : dict of str, array
List of arrays to add written as 'name'=array, ...
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2))
create a Session by passing a list of pairs (name, array)
>>> s = Session([('arr1', arr1), ('arr2', arr2), ('arr3', arr3)])
create a Session using keyword arguments (but you lose order on Python < 3.6)
>>> s = Session(arr1=arr1, arr2=arr2, arr3=arr3)
create a Session by passing a dictionary (but you lose order on Python < 3.6)
>>> s = Session({'arr1': arr1, 'arr2': arr2, 'arr3': arr3})
load Session from file
>>> s = Session('my_session.h5') # doctest: +SKIP
"""
[docs] def __init__(self, *args, **kwargs):
object.__setattr__(self, '_objects', OrderedDict())
if len(args) == 1:
a0 = args[0]
if isinstance(a0, str):
# assume a0 is a filename
self.load(a0)
else:
items = a0.items() if isinstance(a0, dict) else a0
# assume we have an iterable of tuples
for k, v in items:
self[k] = v
else:
self.add(*args, **kwargs)
# XXX: behave like a dict and return keys instead?
def __iter__(self):
return iter(self.values())
[docs] def add(self, *args, **kwargs):
"""
Adds objects to the current session.
Parameters
----------
args : array
List of objects to add. Objects must have an attribute 'name'.
kwargs : dict of str, array
List of objects to add written as 'name'=array, ...
Examples
--------
>>> s = Session()
>>> axis1, axis2 = Axis('x=x0..x2'), Axis('y=y0..y2')
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2))
>>> s.add(axis1, axis2, arr1=arr1, arr2=arr2, arr3=arr3)
>>> # print item's names in sorted order
>>> s.names
['arr1', 'arr2', 'arr3', 'x', 'y']
"""
for arg in args:
self[arg.name] = arg
for k, v in kwargs.items():
self[k] = v
def _ipython_key_completions_(self):
return list(self.keys())
def __getitem__(self, key):
if isinstance(key, int):
keys = list(self.keys())
return self._objects[keys[key]]
elif isinstance(key, LArray):
assert np.issubdtype(key.dtype, np.bool_)
assert key.ndim == 1
# only keep True values
truenames = key[key].axes[0].labels
return Session([(name, self[name]) for name in truenames])
elif isinstance(key, (tuple, list)):
assert all(isinstance(k, str) for k in key)
return Session([(k, self[k]) for k in key])
else:
return self._objects[key]
[docs] def get(self, key, default=None):
"""
Returns the array object corresponding to the key.
If the key doesn't correspond to any array object, a default one can be returned.
Parameters
----------
key : str
Name of the array.
default : array, optional
Returned array if the key doesn't correspond to any array of the current session.
Returns
-------
LArray
Array corresponding to the given key or a default one if not found.
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2))
>>> s = Session([('arr1', arr1), ('arr2', arr2), ('arr3', arr3)])
>>> arr = s.get('arr1')
>>> arr
a\\b b0 b1
a0 0 1
a1 2 3
>>> arr = s.get('arr4', zeros('a=a0,a1;b=b0,b1', dtype=int))
>>> arr
a\\b b0 b1
a0 0 0
a1 0 0
"""
try:
return self[key]
except KeyError:
return default
def __setitem__(self, key, value):
self._objects[key] = value
def __delitem__(self, key):
del self._objects[key]
def __getattr__(self, key):
if key in self._objects:
return self._objects[key]
else:
raise AttributeError("'{}' object has no attribute '{}'".format(self.__class__.__name__, key))
def __setattr__(self, key, value):
self._objects[key] = value
def __delattr__(self, key):
del self._objects[key]
def __dir__(self):
return list(set(dir(self.__class__)) | set(self.keys()))
# needed to make *un*pickling work (because otherwise, __getattr__ is called before ._objects exists, which leads to
# an infinite recursion)
def __getstate__(self):
return self.__dict__
def __setstate__(self, d):
# equivalent to self.__dict__ = d (we need this form because __setattr__ is overridden)
object.__setattr__(self, '__dict__', d)
[docs] def load(self, fname, names=None, engine='auto', display=False, **kwargs):
"""
Loads array objects from a file, or several .csv files.
WARNING: never load a file using the pickle engine (.pkl or .pickle) from an untrusted source, as it can lead
to arbitrary code execution.
Parameters
----------
fname : str
This can be either the path to a single file, a path to a directory containing .csv files or a pattern
representing several .csv files.
names : list of str, optional
List of arrays to load. If `fname` is None, list of paths to CSV files.
Defaults to all valid objects present in the file/directory.
engine : {'auto', 'pandas_csv', 'pandas_hdf', 'pandas_excel', 'xlwings_excel', 'pickle'}, optional
Load using `engine`. Defaults to 'auto' (use default engine for the format guessed from the file extension).
display : bool, optional
Whether or not to display which file is being worked on. Defaults to False.
Examples
--------
In one module
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2)) # doctest: +SKIP
>>> s = Session([('arr1', arr1), ('arr2', arr2), ('arr3', arr3)]) # doctest: +SKIP
>>> s.save('input.h5') # doctest: +SKIP
In another module
>>> s = Session() # doctest: +SKIP
>>> s.load('input.h5', ['arr1', 'arr2', 'arr3']) # doctest: +SKIP
>>> arr1, arr2, arr3 = s['arr1', 'arr2', 'arr3'] # doctest: +SKIP
>>> # only if you know the order of arrays stored in session
>>> arr1, arr2, arr3 = s.values() # doctest: +SKIP
Using .csv files (assuming the same session as above)
>>> s.save('data') # doctest: +SKIP
>>> s = Session() # doctest: +SKIP
>>> # load all .csv files starting with "output" in the data directory
>>> s.load('data') # doctest: +SKIP
>>> # or equivalently in this case
>>> s.load('data/arr*.csv') # doctest: +SKIP
"""
if display:
print("opening", fname)
if fname is None:
if all([os.path.splitext(name)[1] == '.csv' for name in names]):
engine = ext_default_engine['csv']
else:
raise ValueError("List of paths to only CSV files expected. Got {}".format(names))
if engine == 'auto':
_, ext = os.path.splitext(fname)
ext = ext.strip('.') if '.' in ext else 'csv'
engine = ext_default_engine[ext]
handler_cls = handler_classes[engine]
handler = handler_cls(fname)
arrays = handler.read_arrays(names, display=display, **kwargs)
for k, v in arrays.items():
self[k] = v
[docs] def save(self, fname, names=None, engine='auto', overwrite=True, display=False, **kwargs):
"""
Dumps all array objects from the current session to a file.
Parameters
----------
fname : str
Path for the dump.
names : list of str or None, optional
List of names of objects to dump. If `fname` is None, list of paths to CSV files.
Defaults to all objects present in the Session.
engine : {'auto', 'pandas_csv', 'pandas_hdf', 'pandas_excel', 'xlwings_excel', 'pickle'}, optional
Dump using `engine`. Defaults to 'auto' (use default engine for the format guessed from the file extension).
overwrite: bool, optional
Whether or not to overwrite an existing file, if any. Ignored for CSV files. If False, file is updated.
Defaults to True.
display : bool, optional
Whether or not to display which file is being worked on. Defaults to False.
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2)) # doctest: +SKIP
>>> s = Session([('arr1', arr1), ('arr2', arr2), ('arr3', arr3)]) # doctest: +SKIP
Save all arrays
>>> s.save('output.h5') # doctest: +SKIP
Save only some arrays
>>> s.save('output.h5', ['arr1', 'arr3']) # doctest: +SKIP
Update file
>>> arr1, arr4 = ndtest((3, 3)), ndtest((2, 3)) # doctest: +SKIP
>>> s2 = Session([('arr1', arr1), ('arr4', arr4)]) # doctest: +SKIP
>>> # replace arr1 and add arr4 in file output.h5
>>> s2.save('output.h5', overwrite=False) # doctest: +SKIP
"""
if engine == 'auto':
_, ext = os.path.splitext(fname)
ext = ext.strip('.') if '.' in ext else 'csv'
engine = ext_default_engine[ext]
handler_cls = handler_classes[engine]
handler = handler_cls(fname, overwrite)
items = self.filter(kind=LArray).items()
if names is not None:
names_set = set(names)
items = [(k, v) for k, v in items if k in names_set]
handler.dump_arrays(items, display=display, **kwargs)
def to_globals(self, names=None, depth=0, warn=True, inplace=False):
"""
Create global variables out of objects in the session.
Parameters
----------
names : list of str or None, optional
List of names of objects to convert to globals. Defaults to all objects present in the Session.
depth : int, optional
depth of call stack where to create the variables. 0 is where to_globals was called, 1 the caller of
to_globals, etc. Defaults to 0.
warn : bool, optional
Whether or not to warn the user that this method should only be used in an interactive console (see below).
Defaults to True.
inplace : bool, optional
If True, to_globals will assume all arrays already exist and have the same axes and will replace their
content instead of creating new variables. Non array variables in the session will be ignored.
Defaults to False.
Notes
-----
This method should usually only be used in an interactive console and not in a script. Code editors are
confused by this kind of manipulation and will likely consider as invalid the code using variables created in
this way. Additionally, when using this method auto-completion, "show definition", "go to declaration" and
other similar code editor features will probably not work for the variables created in this way and any
variable derived from them.
Examples
--------
>>> s = Session(arr1=ndtest(3), arr2=ndtest((2, 2)))
>>> s.to_globals()
>>> arr1
a a0 a1 a2
0 1 2
>>> arr2
a\\b b0 b1
a0 0 1
a1 2 3
"""
# noinspection PyProtectedMember
if warn and not is_interactive_interpreter():
warnings.warn("Session.to_globals should usually only be used in interactive consoles and not in scripts. "
"Use warn=False to deactivate this warning.",
RuntimeWarning, stacklevel=2)
d = sys._getframe(depth + 1).f_globals
items = self.items()
if names is not None:
names_set = set(names)
items = [(k, v) for k, v in items if k in names_set]
if inplace:
for k, v in items:
if k not in d:
raise ValueError("'{}' not found in current namespace. Session.to_globals(inplace=True) requires "
"all arrays to already exist.".format(k))
if not isinstance(v, LArray):
continue
if not d[k].axes == v.axes:
raise ValueError("Session.to_globals(inplace=True) requires the existing (destination) arrays "
"to have the same axes than those stored in the session and this is not the case "
"for '{}'.\nexisting: {}\nsession: {}".format(k, d[k].info, v.info))
d[k][:] = v
else:
for k, v in items:
d[k] = v
[docs] def to_pickle(self, fname, names=None, overwrite=True, display=False, **kwargs):
"""
Dumps all array objects from the current session to a file using pickle.
WARNING: never load a pickle file (.pkl or .pickle) from an untrusted source, as it can lead to arbitrary code
execution.
Parameters
----------
fname : str
Path for the dump.
names : list of str or None, optional
List of names of objects to dump. Defaults to all objects present in the Session.
overwrite: bool, optional
Whether or not to overwrite an existing file, if any.
If False, file is updated. Defaults to True.
display : bool, optional
Whether or not to display which file is being worked on. Defaults to False.
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2)) # doctest: +SKIP
>>> s = Session([('arr1', arr1), ('arr2', arr2), ('arr3', arr3)]) # doctest: +SKIP
Save all arrays
>>> s.to_pickle('output.pkl') # doctest: +SKIP
Save only some arrays
>>> s.to_pickle('output.pkl', ['arr1', 'arr3']) # doctest: +SKIP
"""
self.save(fname, names, ext_default_engine['pkl'], overwrite, display, **kwargs)
dump = renamed_to(save, 'dump')
[docs] def to_hdf(self, fname, names=None, overwrite=True, display=False, **kwargs):
"""
Dumps all array objects from the current session to an HDF file.
Parameters
----------
fname : str
Path for the dump.
names : list of str or None, optional
List of names of objects to dump. Defaults to all objects present in the Session.
overwrite: bool, optional
Whether or not to overwrite an existing file, if any.
If False, file is updated. Defaults to True.
display : bool, optional
Whether or not to display which file is being worked on. Defaults to False.
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2)) # doctest: +SKIP
>>> s = Session([('arr1', arr1), ('arr2', arr2), ('arr3', arr3)]) # doctest: +SKIP
Save all arrays
>>> s.to_hdf('output.h5') # doctest: +SKIP
Save only some arrays
>>> s.to_hdf('output.h5', ['arr1', 'arr3']) # doctest: +SKIP
"""
self.save(fname, names, ext_default_engine['hdf'], overwrite, display, **kwargs)
dump_hdf = renamed_to(to_hdf, 'dump_hdf')
[docs] def to_excel(self, fname, names=None, overwrite=True, display=False, **kwargs):
"""
Dumps all array objects from the current session to an Excel file.
Parameters
----------
fname : str
Path for the dump.
names : list of str or None, optional
List of names of objects to dump. Defaults to all objects present in the Session.
overwrite: bool, optional
Whether or not to overwrite an existing file, if any. If False, file is updated. Defaults to True.
display : bool, optional
Whether or not to display which file is being worked on. Defaults to False.
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2)) # doctest: +SKIP
>>> s = Session([('arr1', arr1), ('arr2', arr2), ('arr3', arr3)]) # doctest: +SKIP
Save all arrays
>>> s.to_excel('output.xlsx') # doctest: +SKIP
Save only some arrays
>>> s.to_excel('output.xlsx', ['arr1', 'arr3']) # doctest: +SKIP
"""
self.save(fname, names, ext_default_engine['xlsx'], overwrite, display, **kwargs)
dump_excel = renamed_to(to_excel, 'dump_excel')
[docs] def to_csv(self, fname, names=None, display=False, **kwargs):
"""
Dumps all array objects from the current session to CSV files.
Parameters
----------
fname : str
Path for the directory that will contain CSV files.
names : list of str or None, optional
List of names of objects to dump. Defaults to all objects present in the Session.
display : bool, optional
Whether or not to display which file is being worked on. Defaults to False.
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2)) # doctest: +SKIP
>>> s = Session([('arr1', arr1), ('arr2', arr2), ('arr3', arr3)]) # doctest: +SKIP
Save all arrays
>>> s.to_csv('./Output') # doctest: +SKIP
Save only some arrays
>>> s.to_csv('./Output', ['arr1', 'arr3']) # doctest: +SKIP
"""
self.save(fname, names, ext_default_engine['csv'], display=display, **kwargs)
dump_csv = renamed_to(to_csv, 'dump_csv')
[docs] def filter(self, pattern=None, kind=None):
"""
Returns a new session with array objects which match some criteria.
Parameters
----------
pattern : str, optional
Only keep arrays whose key match `pattern`.
kind : type, optional
Only keep arrays which are instances of type `kind`.
Returns
-------
Session
The filtered session.
Examples
--------
>>> axis = Axis('a=a0..a2')
>>> test1, test2, zero1 = ndtest((2, 2)), ndtest(4), zeros((3, 2))
>>> s = Session([('test1', test1), ('test2', test2), ('zero1', zero1), ('axis', axis)])
Filter using a pattern argument
>>> s.filter(pattern='test').names
['test1', 'test2']
Filter using kind argument
>>> s.filter(kind=Axis).names
['axis']
"""
items = self._objects.items()
if pattern is not None:
items = [(k, v) for k, v in items if check_pattern(k, pattern)]
if kind is not None:
items = [(k, v) for k, v in items if isinstance(v, kind)]
return Session(items)
@property
def names(self):
"""
Returns the list of names of the array objects in the session.
The list is sorted alphabetically and does not follow the internal order.
Returns
-------
list of str
See Also
--------
Session.keys
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2))
>>> s = Session([('arr2', arr2), ('arr1', arr1), ('arr3', arr3)])
>>> # print array's names in the alphabetical order
>>> s.names
['arr1', 'arr2', 'arr3']
>>> # keys() follows the internal order
>>> list(s.keys())
['arr2', 'arr1', 'arr3']
"""
return sorted(self._objects.keys())
[docs] def copy(self):
"""Returns a copy of the session.
"""
# this actually *does* a copy of the internal mapping (the mapping is not reused-as is)
return Session(self._objects)
[docs] def keys(self):
"""
Returns a view on the session's keys.
Returns
-------
View on the session's keys.
See Also
--------
Session.names
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2))
>>> s = Session([('arr2', arr2), ('arr1', arr1), ('arr3', arr3)])
>>> # similar to names by follows the internal order
>>> list(s.keys())
['arr2', 'arr1', 'arr3']
>>> # gives the names of arrays in alphabetical order
>>> s.names
['arr1', 'arr2', 'arr3']
"""
return self._objects.keys()
[docs] def values(self):
"""
Returns a view on the session's values.
Returns
-------
View on the session's values.
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2))
>>> s = Session([('arr2', arr2), ('arr1', arr1), ('arr3', arr3)])
>>> # assuming you know the order of arrays stored in the session
>>> arr2, arr1, arr3 = s.values()
>>> # otherwise, prefer the following syntax
>>> arr1, arr2, arr3 = s['arr1', 'arr2', 'arr3']
>>> arr1
a\\b b0 b1
a0 0 1
a1 2 3
"""
return self._objects.values()
[docs] def items(self):
"""
Returns a view of the session’s items ((key, value) pairs).
Returns
-------
View on the session's items.
Examples
--------
>>> arr1, arr2, arr3 = ndtest((2, 2)), ndtest(4), ndtest((3, 2))
>>> # make the test pass on both Windows and Linux
>>> arr1, arr2, arr3 = arr1.astype(np.int64), arr2.astype(np.int64), arr3.astype(np.int64)
>>> s = Session([('arr2', arr2), ('arr1', arr1), ('arr3', arr3)])
>>> for k, v in s.items():
... print("{}: {}".format(k, v.info))
arr2: 4
a [4]: 'a0' 'a1' 'a2' 'a3'
dtype: int64
arr1: 2 x 2
a [2]: 'a0' 'a1'
b [2]: 'b0' 'b1'
dtype: int64
arr3: 3 x 2
a [3]: 'a0' 'a1' 'a2'
b [2]: 'b0' 'b1'
dtype: int64
"""
return self._objects.items()
def __repr__(self):
return 'Session({})'.format(', '.join(self.keys()))
def __len__(self):
return len(self._objects)
# binary operations are dispatched element-wise to all arrays (we consider Session as an array-like)
def _binop(opname):
opfullname = '__%s__' % opname
def opmethod(self, other):
self_keys = set(self.keys())
all_keys = list(self.keys()) + [n for n in other.keys() if n not in self_keys]
with np.errstate(call=_session_float_error_handler):
res = []
for name in all_keys:
self_array = self.get(name, np.nan)
other_array = other.get(name, np.nan)
try:
res_array = getattr(self_array, opfullname)(other_array)
# TypeError for str arrays, ValueError for incompatible axes, ...
except Exception:
res_array = np.nan
# this should only ever happen when self_array is a non Array (eg. nan)
if res_array is NotImplemented:
try:
res_array = getattr(other_array, '__%s__' % inverseop(opname))(self_array)
# TypeError for str arrays, ValueError for incompatible axes, ...
except Exception:
res_array = np.nan
res.append((name, res_array))
return Session(res)
opmethod.__name__ = opfullname
return opmethod
__add__ = _binop('add')
__sub__ = _binop('sub')
__mul__ = _binop('mul')
__truediv__ = _binop('truediv')
# element-wise method factory
# unary operations are (also) dispatched element-wise to all arrays
def _unaryop(opname):
opfullname = '__%s__' % opname
def opmethod(self):
with np.errstate(call=_session_float_error_handler):
res = []
for k, v in self.items():
try:
res_array = getattr(v, opfullname)()
except Exception:
res_array = np.nan
res.append((k, res_array))
return Session(res)
opmethod.__name__ = opfullname
return opmethod
__neg__ = _unaryop('neg')
__pos__ = _unaryop('pos')
__abs__ = _unaryop('abs')
__invert__ = _unaryop('invert')
# XXX: use _binop (ie elementwise comparison instead of aggregating directly?)
def __eq__(self, other):
self_keys = set(self.keys())
all_keys = list(self.keys()) + [n for n in other.keys() if n not in self_keys]
res = [larray_nan_equal(self.get(key), other.get(key)) for key in all_keys]
return LArray(res, [Axis(all_keys, 'name')])
def __ne__(self, other):
return ~(self == other)
[docs] def transpose(self, *args):
"""Reorder axes of arrays in session, ignoring missing axes for each array.
Parameters
----------
*args
Accepts either a tuple of axes specs or axes specs as `*args`. Omitted axes keep their order.
Use ... to avoid specifying intermediate axes. Axes missing in an array are ignored.
Returns
-------
Session
Session with each array with reordered axes where appropriate.
See Also
--------
LArray.transpose
Examples
--------
Let us create a test session and a small helper function to display sessions as a short summary.
>>> arr1 = ndtest((2, 2, 2))
>>> arr2 = ndtest((2, 2))
>>> sess = Session([('arr1', arr1), ('arr2', arr2)])
>>> def print_summary(s):
... print(s.summary("{name} -> {axes_names}"))
>>> print_summary(sess)
arr1 -> a, b, c
arr2 -> a, b
Put 'b' axis in front of all arrays
>>> print_summary(sess.transpose('b'))
arr1 -> b, a, c
arr2 -> b, a
Axes missing on an array are ignored ('c' for arr2 in this case)
>>> print_summary(sess.transpose('c', 'b'))
arr1 -> c, b, a
arr2 -> b, a
Use ... to move axes to the end
>>> print_summary(sess.transpose(..., 'a')) # doctest: +SKIP
arr1 -> b, c, a
arr2 -> b, a
"""
def lenient_transpose(v, axes):
# filter out axes not in arr.axes
return v.transpose([a for a in axes if a in v.axes or a is Ellipsis])
return self.apply(lenient_transpose, args)
[docs] def compact(self, display=False):
"""
Detects and removes "useless" axes (ie axes for which values are constant over the whole axis) for all array
objects in session
Parameters
----------
display : bool, optional
Whether or not to display a message for each array that is compacted
Returns
-------
Session
A new session containing all compacted arrays
Examples
--------
>>> arr1 = sequence('b=b0..b2', ndtest(3), zeros_like(ndtest(3)))
>>> arr1
a\\b b0 b1 b2
a0 0 0 0
a1 1 1 1
a2 2 2 2
>>> compact_ses = Session(arr1=arr1).compact(display=True)
arr1 was constant over {b}
>>> compact_ses.arr1
a a0 a1 a2
0 1 2
"""
new_items = []
for k, v in self._objects.items():
compacted = v.compact()
if compacted is not v and display:
print(k, "was constant over", get_axes(v) - get_axes(compacted))
new_items.append((k, compacted))
return Session(new_items)
[docs] def apply(self, func, *args, **kwargs):
"""
Apply function `func` on elements of the session and return a new session.
Parameters
----------
func : function
Function to apply to each element of the session. It should take a single `element` argument and return
a single value.
*args : any
Any extra arguments are passed to the function
kind : type or tuple of types, optional
Type(s) of elements `func` will be applied to. Other elements will be left intact. Use ´kind=object´ to
apply to all kinds of objects. Defaults to LArray.
**kwargs : any
Any extra keyword arguments are passed to the function
Returns
-------
Session
A new session containing all processed elements
Examples
--------
>>> arr1 = ndtest(2)
>>> arr1
a a0 a1
0 1
>>> arr2 = ndtest(3)
>>> arr2
a a0 a1 a2
0 1 2
>>> sess1 = Session([('arr1', arr1), ('arr2', arr2)])
>>> sess1
Session(arr1, arr2)
>>> def increment(array):
... return array + 1
>>> sess2 = sess1.apply(increment)
>>> sess2.arr1
a a0 a1
1 2
>>> sess2.arr2
a a0 a1 a2
1 2 3
You may also pass extra arguments or keyword arguments to the function
>>> def change(array, increment=1, multiplier=1):
... return (array + increment) * multiplier
>>> sess2 = sess1.apply(change, 2, 2)
>>> sess2 = sess1.apply(change, 2, multiplier=2)
>>> sess2.arr1
a a0 a1
4 6
>>> sess2.arr2
a a0 a1 a2
4 6 8
"""
kind = kwargs.pop('kind', LArray)
return Session([(k, func(v, *args, **kwargs) if isinstance(v, kind) else v) for k, v in self.items()])
[docs] def summary(self, template=None):
"""
Returns a summary of the content of the session.
Parameters
----------
template: str
Template describing how items are summarized (see examples).
Available arguments are 'name', 'axes_names' and 'title'
Returns
-------
str
Short representation of the content of the session.
.
Examples
--------
>>> arr1 = ndtest((2, 2), title='array 1')
>>> arr2 = ndtest(4, title='array 2')
>>> arr3 = ndtest((3, 2), title='array 3')
>>> s = Session([('arr1', arr1), ('arr2', arr2), ('arr3', arr3)])
>>> print(s.summary()) # doctest: +NORMALIZE_WHITESPACE
arr1: a, b
array 1
arr2: a
array 2
arr3: a, b
array 3
>>> print(s.summary("{name} -> {axes_names}"))
arr1 -> a, b
arr2 -> a
arr3 -> a, b
"""
if template is None:
template = "{name}: {axes_names}\n {title}\n"
templ_kwargs = [{'name': k,
'axes_names': ', '.join(v.axes.display_names),
'title': v.title} for k, v in self.items()]
return '\n'.join(template.format(**kwargs) for kwargs in templ_kwargs)
[docs]def local_arrays(depth=0):
"""
Returns a session containing all local arrays sorted in alphabetical order.
Parameters
----------
depth: int
depth of call frame to inspect. 0 is where `local_arrays` was called, 1 the caller of `local_arrays`, etc.
Returns
-------
Session
"""
# noinspection PyProtectedMember
d = sys._getframe(depth + 1).f_locals
return Session((k, d[k]) for k in sorted(d.keys()) if isinstance(d[k], LArray))
[docs]def global_arrays(depth=0):
"""
Returns a session containing all global arrays sorted in alphabetical order.
Parameters
----------
depth: int
depth of call frame to inspect. 0 is where `global_arrays` was called, 1 the caller of `global_arrays`, etc.
Returns
-------
Session
"""
# noinspection PyProtectedMember
d = sys._getframe(depth + 1).f_globals
return Session((k, d[k]) for k in sorted(d.keys()) if isinstance(d[k], LArray))
[docs]def arrays(depth=0):
"""
Returns a session containing all available arrays (whether they are defined in local or global variables) sorted in
alphabetical order. Local arrays take precedence over global ones (if a name corresponds to both a local
and a global variable, the local array will be returned).
Parameters
----------
depth: int
depth of call frame to inspect. 0 is where `arrays` was called, 1 the caller of `arrays`, etc.
Returns
-------
Session
"""
# noinspection PyProtectedMember
caller_frame = sys._getframe(depth + 1)
global_vars = caller_frame.f_globals
local_vars = caller_frame.f_locals
# We must first get all variables *then* filter by type, otherwise we could return a global array which is not
# currently available because it is shadowed by a local non-array variable.
all_keys = sorted(set(global_vars.keys()) | set(local_vars.keys()))
combined_vars = [(k, local_vars[k] if k in local_vars else global_vars[k])
for k in all_keys]
return Session((k, v) for k, v in combined_vars if isinstance(v, LArray))
_session_float_error_handler = float_error_handler_factory(4)