1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- """
- This module implements :class:`ChannelView`, which represents a subset of the
- channels in an :class:`AnalogSignal` or :class:`IrregularlySampledSignal`.
- It replaces the indexing function of the former :class:`ChannelIndex`.
- """
- import numpy as np
- from .baseneo import BaseNeo
- from .basesignal import BaseSignal
- from .dataobject import ArrayDict
- class ChannelView(BaseNeo):
- """
- A tool for indexing a subset of the channels within an :class:`AnalogSignal`
- or :class:`IrregularlySampledSignal`;
- *Required attributes/properties*:
- :obj: (AnalogSignal or IrregularlySampledSignal) The signal being indexed.
- :index: (list/1D-array) boolean or integer mask to select the channels of interest.
- *Recommended attributes/properties*:
- :name: (str) A label for the view.
- :description: (str) Text description.
- :file_origin: (str) Filesystem path or URL of the original data file.
- :array_annotations: (dict) Dict mapping strings to numpy arrays containing annotations
- for all data points
- Note: Any other additional arguments are assumed to be user-specific
- metadata and stored in :attr:`annotations`.
- """
- _single_parent_objects = ('Segment',)
- _single_parent_attrs = ('segment',)
- _necessary_attrs = (
- ('index', np.ndarray, 1, np.dtype('i')),
- ('obj', ('AnalogSignal', 'IrregularlySampledSignal'), 1)
- )
- # "mask" would be an alternative name, proposing "index" for
- # backwards-compatibility with ChannelIndex
- def __init__(self, obj, index, name=None, description=None, file_origin=None,
- array_annotations=None, **annotations):
- super().__init__(name=name, description=description,
- file_origin=file_origin, **annotations)
- if not (isinstance(obj, BaseSignal) or (
- hasattr(obj, "proxy_for") and issubclass(obj.proxy_for, BaseSignal))):
- raise ValueError("Can only take a ChannelView of an AnalogSignal "
- "or an IrregularlySampledSignal")
- self.obj = obj
- # check type and dtype of index and convert index to a common form
- # (accept list or array of bool or int, convert to int array)
- self.index = np.array(index)
- if len(self.index.shape) != 1:
- raise ValueError("index must be a 1D array")
- if self.index.dtype == np.bool: # convert boolean mask to integer index
- if self.index.size != self.obj.shape[-1]:
- raise ValueError("index size does not match number of channels in signal")
- self.index, = np.nonzero(self.index)
- # allow any type of integer representation
- elif self.index.dtype.char not in np.typecodes['AllInteger']:
- raise ValueError("index must be of a list or array of data type boolean or integer")
- if not hasattr(self, 'array_annotations') or not self.array_annotations:
- self.array_annotations = ArrayDict(self._get_arr_ann_length())
- if array_annotations is not None:
- self.array_annotate(**array_annotations)
- @property
- def shape(self):
- return (self.obj.shape[0], self.index.size)
- def _get_arr_ann_length(self):
- return self.shape[-1]
- def array_annotate(self, **array_annotations):
- self.array_annotations.update(array_annotations)
- def resolve(self):
- """
- Return a copy of the underlying object containing just the subset of channels
- defined by the index.
- """
- return self.obj[:, self.index]
|