"""General-purpose module for shared utilities across the package."""
import numpy as np
__all__ = ['RichResult',]
# RichResult and its formatters are modified copies from scipy._lib._util
[docs]
class RichResult:
"""Output container with pretty printing."""
_order_keys = []
def __init__(self, **kwargs):
"""
A container class based off the `_RichResult` class in the `scipy`
library. It combines a series of formatting functions to make the
printed 'repr' easy to read. Use this class directly by passing in
any number of keyword arguments, or use it as a base class to have
custom classes for different result types.
Inheriting classes should define the class attribute `_order_keys`
which is a list of strings that defines how output fields are sorted
when an instance is printed.
Parameters
----------
**kwargs : dict, optional
User-specified keyword arguments. Any number of arguments can be
given as input. The class simply stores the key/value pairs, makes
them accessible as attributes, and provides pretty printing.
Examples
--------
The example below demonstrates how to define the `_order_keys` class
attribute for custom sorting. If arguments are not in the list, they
are placed at the end based on the order they were given. Note that
`_order_keys` only provides sorting support and that no errors are
raised if an argument is not present, e.g., `third` below.
.. code-block:: python
import sksundae as sun
class CustomResult(sun.utils.RichResult):
_order_keys = ['first', 'second', 'third',]
result = CustomResult(second=None, last=None, first=None)
print(result)
`RichResult` can also be used directly, without any custom sorting.
Arguments will print based on the order they were input. Instances will
still have a fully formatted 'repr', including formatted arrays.
.. code-block:: python
import numpy as np
from sksundae.utils import RichResult
t = np.linspace(0, 1, 1000)
y = np.random.rand(1000, 5)
y[0] = np.inf
y[-1] = np.nan
result = RichResult(message='Example.', status=0, t=t, y=y)
print(result)
After initialization, all key/value pairs are accessible as instance
attributes.
.. code-block:: python
from sksundae.utils import RichResult
result = RichResult(a=10, b=20, c=30)
print(result.a*(result.b + result.c))
"""
for k, v in kwargs.items():
setattr(self, k, v)
def __repr__(self):
order_keys = getattr(self, '_order_keys')
def key(item):
try:
return order_keys.index(item[0].lower())
except ValueError: # item not in list, move to end
return np.inf
def sorter(d):
return sorted(d.items(), key=key)
if self.__dict__.keys():
return '\n' + _format_dict(self.__dict__, sorter=sorter) + '\n'
else:
return self.__class__.__name__ + '()'
def _indenter(s, n=0):
"""Ensures lines after the first are indented by the specified amount."""
split = s.split('\n')
return ('\n' + ' '*n).join(split)
def _format_float_10(x):
"""Returns string representation of floats with exactly ten characters."""
if np.isposinf(x):
return ' inf'
elif np.isneginf(x):
return ' -inf'
elif np.isnan(x):
return ' nan'
return np.format_float_scientific(x, precision=3, pad_left=2, unique=False)
def _format_dict(d, n=0, mplus=1, sorter=None):
"""Pretty printer for dictionaries."""
if isinstance(d, dict):
m = max(map(len, list(d.keys()))) + mplus # width to print keys
# indent and format each value... + 2 for ': '
line_items = []
for k, v in sorter(d):
formatted_v = _indenter(_format_dict(v, m+n+2, 0, sorter), m+2)
line_items.append(k.rjust(m) + ': ' + formatted_v)
output_str = '\n'.join(line_items)
else:
with np.printoptions(linewidth=76-n, edgeitems=2, threshold=12,
formatter={'float_kind': _format_float_10}):
output_str = str(d)
return output_str