Source code for peakdet.physio

# -*- coding: utf-8 -*-
"""
Helper class for holding physiological data and associated metadata inforamtion
"""

import numpy as np


[docs]class Physio(): """ Class to hold physiological data and relevant information Parameters ---------- data : array_like Input data array fs : float, optional Sampling rate of `data` (Hz). Default: None history : list of tuples, optional Functions performed on `data`. Default: None metadata : dict, optional Metadata associated with `data`. Default: None Attributes ---------- data : :obj:`numpy.ndarray` Physiological waveform fs : float Sampling rate of `data` in Hz history : list of tuples History of functions that have been performed on `data`, with relevant parameters provided to functions. peaks : :obj:`numpy.ndarray` Indices of peaks in `data` troughs : :obj:`numpy.ndarray` Indices of troughs in `data` """ def __init__(self, data, fs=None, history=None, metadata=None): self._data = np.asarray(data).squeeze() if self.data.ndim > 1: raise ValueError('Provided data dimensionality {} > 1.' .format(self.data.ndim)) if not np.issubdtype(self.data.dtype, np.number): raise ValueError('Provided data of type {} is not numeric.' .format(self.data.dtype)) self._fs = np.float64(fs) self._history = [] if history is None else history if (not isinstance(self._history, list) or any([not isinstance(f, tuple) for f in self._history])): raise TypeError('Provided history {} must be a list-of-tuples. ' 'Please check inputs.'.format(history)) if metadata is not None: if not isinstance(metadata, dict): raise TypeError('Provided metadata {} must be dict-like.' .format(metadata)) for k in ['peaks', 'troughs', 'reject']: metadata.setdefault(k, np.empty(0, dtype=int)) if not isinstance(metadata.get(k), np.ndarray): try: metadata[k] = np.asarray(metadata.get(k), dtype=int) except TypeError: raise TypeError('Provided metadata must be dict-like' 'with integer array entries.') self._metadata = dict(**metadata) else: self._metadata = dict(peaks=np.empty(0, dtype=int), troughs=np.empty(0, dtype=int), reject=np.empty(0, dtype=int)) def __array__(self): return self.data def __getitem__(self, slicer): return self.data[slicer] def __len__(self): return len(self.data) def __str__(self): return '{name}(size={size}, fs={fs})'.format( name=self.__class__.__name__, size=self.data.size, fs=self.fs ) __repr__ = __str__ @property def data(self): """ Physiological data """ return self._data @property def fs(self): """ Sampling rate of data (Hz) """ return self._fs @property def history(self): """ Functions that have been performed on / modified `data` """ return self._history @property def peaks(self): """ Indices of detected peaks in `data` """ return self._masked.compressed() @property def troughs(self): """ Indices of detected troughs in `data` """ return self._metadata['troughs'] @property def _masked(self): return np.ma.masked_array(self._metadata['peaks'], mask=np.isin(self._metadata['peaks'], self._metadata['reject']))