Source code for gala.util

""" General utilities. """

# Standard library
from collections.abc import Mapping

# Third-party
import numpy as np

__all__ = ["rolling_window", "atleast_2d", "assert_angles_allclose"]


class ImmutableDict(Mapping):

    @classmethod
    def from_dict(cls, somedict):
        return cls(**somedict)

    def __init__(self, **kwargs):
        self._dict = kwargs
        self._hash = None

    def __getitem__(self, key):
        return self._dict[key]

    def __len__(self):
        return len(self._dict)

    def __iter__(self):
        return iter(self._dict)

    def __hash__(self):
        if self._hash is None:
            self._hash = hash(frozenset(self._dict.items()))
        return self._hash

    def __eq__(self, other):
        return self._dict == other._dict

    def __repr__(self):
        return f"<ImmutableDict {self._dict.__repr__()}>"

    def __str__(self):
        return self._dict.__str__()

    def copy(self):
        import copy

        return copy.deepcopy(self._dict)


[docs] def rolling_window(arr, window_size, stride=1, return_idx=False): """ There is an example of an iterator for pure-Python objects in: http://stackoverflow.com/questions/6822725/rolling-or-sliding-window-iterator-in-python This is a rolling-window iterator Numpy arrays, with window size and stride control. See examples below for demos. Parameters ---------- arr : array_like Input numpy array. window_size : int Width of the window. stride : int (optional) Number of indices to advance the window each iteration step. return_idx : bool (optional) Whether to return the slice indices alone with the array segment. Examples -------- >>> a = np.array([1, 2, 3, 4, 5, 6]) >>> for x in rolling_window(a, 3): ... print(x) [1 2 3] [2 3 4] [3 4 5] [4 5 6] >>> for x in rolling_window(a, 2, stride=2): ... print(x) [1 2] [3 4] [5 6] >>> for (i1, i2), x in rolling_window(a, 2, stride=2, return_idx=True): # doctest: +SKIP ... print(i1, i2, x) (0, 2, array([1, 2])) (2, 4, array([3, 4])) (4, 6, array([5, 6])) """ window_size = int(window_size) stride = int(stride) if window_size < 0 or stride < 1: raise ValueError arr_len = len(arr) if arr_len < window_size: if return_idx: yield (0, arr_len), arr else: yield arr ix1 = 0 while ix1 < arr_len: ix2 = ix1 + window_size result = arr[ix1:ix2] if return_idx: yield (ix1, ix2), result else: yield result if len(result) < window_size or ix2 >= arr_len: break ix1 += stride
[docs] def atleast_2d(*arys, **kwargs): """ View inputs as arrays with at least two dimensions. Parameters ---------- arys1, arys2, ... : array_like One or more array-like sequences. Non-array inputs are converted to arrays. Arrays that already have two or more dimensions are preserved. insert_axis : int (optional) Where to create a new axis if input array(s) have <2 dim. Returns ------- res, res2, ... : ndarray An array, or tuple of arrays, each with ``a.ndim >= 2``. Copies are avoided where possible, and views with two or more dimensions are returned. Examples -------- >>> atleast_2d(3.0) # doctest: +FLOAT_CMP array([[3.]]) >>> x = np.arange(3.0) >>> atleast_2d(x) # doctest: +FLOAT_CMP array([[0., 1., 2.]]) >>> atleast_2d(x, insert_axis=-1) # doctest: +FLOAT_CMP array([[0.], [1.], [2.]]) >>> atleast_2d(x).base is x True >>> atleast_2d(1, [1, 2], [[1, 2]]) [array([[1]]), array([[1, 2]]), array([[1, 2]])] """ insert_axis = kwargs.pop("insert_axis", 0) slc = [slice(None)] * 2 slc[insert_axis] = None slc = tuple(slc) res = [] for ary in arys: ary = np.asanyarray(ary) if len(ary.shape) == 0: result = ary.reshape(1, 1) elif len(ary.shape) == 1: result = ary[slc] else: result = ary res.append(result) if len(res) == 1: return res[0] else: return res
[docs] def assert_angles_allclose(x, y, **kwargs): """ Like numpy's assert_allclose, but for angles (in radians). """ c2 = (np.sin(x) - np.sin(y)) ** 2 + (np.cos(x) - np.cos(y)) ** 2 diff = np.arccos((2.0 - c2) / 2.0) # a = b = 1 assert np.allclose(diff, 0.0, **kwargs)
class GalaDeprecationWarning(DeprecationWarning): """ A warning class to indicate a deprecated feature. """