Source code for osl_ephys.utils.spmio._events

"""Classes relating to the format and storage of MEEG events and trials."""

from ._spmmeeg_utils import empty_to_zero

from logging import getLogger

import numpy as np

[docs]_logger = getLogger("py_spm")
[docs]class Trial: ## This works for continuous data but not epoched. # Epoched info is stored in list with dict per trial # Need to account for both # Should populate events all the time anyway # Need to populate trial info when present def __init__(self, label, events, onset, bad, tag, repl, sample_frequency=None):
[docs] self.label = label
[docs] self.events = [Event.from_dict(event_dict) for event_dict in events]
[docs] self.onset = onset
[docs] self.bad = bad
[docs] self.tag = tag
[docs] self.repl = repl
if sample_frequency is not None: self.calculate_samples(sample_frequency)
[docs] def calculate_samples(self, sample_frequency): for event in self.events: event.sample = np.floor(event.time * sample_frequency).astype(int) event.end_sample = np.floor(event.end_time * sample_frequency).astype(int)
[docs] def _event_property(self, property_): return np.array([getattr(event, property_) for event in self.events])
[docs] def _set_event_property(self, property_, values): if len(self.events) != len(values): _logger.warning( f"{len(self.events)} events, but {len(values)} values given." ) for event, value in zip(self.events, values): setattr(event, property_, value)
@property
[docs] def types(self): return self._event_property("type")
@property
[docs] def values(self): return self._event_property("value")
@property
[docs] def durations(self): return self._event_property("duration")
@property
[docs] def times(self): return self._event_property("time")
@property
[docs] def offsets(self): return self._event_property("offset")
@property
[docs] def end_times(self): return self._event_property("end_time")
@property
[docs] def samples(self): return self._event_property("sample")
@property
[docs] def end_samples(self): return self._event_property("end_sample")
@property
[docs] def good_samples(self): return self._event_property("good_sample")
@good_samples.setter def good_samples(self, values): self._set_event_property("good_sample", values) @property
[docs] def good_end_samples(self): return self._event_property("good_end_sample")
@good_end_samples.setter def good_end_samples(self, values): return self._set_event_property("good_end_sample", values) @property
[docs] def trial_starts(self): return self._event_property("trial_start")
@trial_starts.setter def trial_starts(self, values): return self._set_event_property("trial_start", values)
#%% -------------------------------------------------------------
[docs]class Event: def __init__(self, type_, value, duration, time, offset):
[docs] self.type = type_
[docs] self.value = value
[docs] self.duration = empty_to_zero(duration)
if "artefact" in self.type.lower(): self.duration *= 1000
[docs] self.time = time
[docs] self.offset = offset
[docs] self.end_time = time + self.duration / 1000
[docs] self.sample = -1
[docs] self.end_sample = -1
[docs] self.good_sample = -1
[docs] self.good_end_sample = -1
[docs] self.trial_start = -1
[docs] self.trial_end = -1
@classmethod
[docs] def from_dict(cls, event_dict): if "type" in event_dict: event_dict["type_"] = event_dict.pop("type") return cls(**event_dict)
[docs] def to_dict(self): return { "type_": self.type, "value": self.value, "duration": self.duration, "time": self.time, "offset": self.offset, }
[docs] def __repr__(self): return ( f"{self.__class__.__name__}(type_='{self.type}', " f"value={self.value}, " f"duration={self.duration}, " f"time={self.time}, " f"offset={self.offset})" )
#%% -------------------------------------------------------------