Source code for neuralib.signal.segement

from __future__ import annotations

import numpy as np

from neuralib.util.deprecation import deprecated_class, deprecated_func


[docs] @deprecated_class(new='neuralib.util.segments.*', removal_version="0.4.0") class Segment: """Segment container class"""
[docs] def __init__(self, data: Segment | np.ndarray, copy: bool = True): if isinstance(data, Segment): if copy: self._data = data._data.copy() else: self._data = data._data else: self._data = check_segments(data, copy)
def __repr__(self): """Return string representation of Segment object.""" return f'Segment(" + {repr(self._data)} + ")' __str__ = __repr__ def __len__(self): """Return the number of segments in the container.""" return int(self._data.shape[0]) def __getitem__(self, item: slice | int) -> Segment: """ :param item: slice or index :return: """ return Segment(self._data[item, :]) def __iter__(self): """Iterate through segments in container.""" idx = 0 while idx < self._data.shape[0]: yield self._data[idx, 0], self._data[idx, 1] idx += 1
[docs] @classmethod def from_logical(cls, y: np.ndarray, x: np.ndarray | None = None, interpolate=False) -> Segment: """Construct Segment from logical vector :param y: 1d logical array Any sequence of True values that is flanked by False values is converted into a segment :param x: 1d array like, optional The segment indices from y will be used to index into x :param interpolate: bool, optional if true, segments of duration 0 are extent to have a minimal duration """ y = np.where(y, 1, 0).ravel() if len(y) == 0 or np.all(y == 0): return cls(np.zeros((0, 2), dtype=np.int64)) offset = 0 if interpolate: offset = 0.5 d = np.diff(np.concatenate(([0], y, [0]))) segstart = np.nonzero(d[0:-1] == 1)[0] - offset segend = np.nonzero(d[1:] == -1)[0] + offset seg = np.vstack((segstart, segend)).T if x is not None: if interpolate: from scipy.interpolate import interp1d seg = interp1d( np.arange(len(y)), x, kind="linear", bounds_error=False, fill_value=(x[0], x[-1]), )(seg) else: seg = x[seg] return cls(seg)
@property def start(self) -> np.ndarray: """Get/Set vector of segment start values """ return self._data[:, 0].copy() @property def stop(self) -> np.ndarray: return self._data[:, 1]
[docs] @deprecated_func(new='neuralib.util.segments.*', removal_version="0.4.0") def check_segments(x: np.ndarray, copy=False) -> np.ndarray: """Convert to segment array. :param x: 1d array-like or (n,2) array-like :param copy: the output will always be a copy of the input :return: (n,2) array """ try: x = np.array(x, copy=copy) except TypeError: raise ValueError("Cannot convert data to numpy array") if not np.issubdtype(x.dtype, np.number): raise ValueError("Values are not real numbers") # The array has to have two dimensions of shape(X,2), where X>=0. # As a special case, a one dimensional vector of at least length two is considered # a valid list of segments, e.g. when data is specified as a list [0,2,3]. if x.shape == (0,): x = np.zeros([0, 2]) elif x.ndim == 1 and len(x) > 1: x = np.vstack((x[0:-1], x[1:])).T elif x.ndim != 2 or x.shape[1] != 2: raise ValueError("Incorrect array size") # Negative duration segments are not allowed. if np.any(np.diff(x, axis=1) < 0): raise ValueError("Segment durations cannot be negative") return x