[REQ] Support signal events, especially neural spikes
Opened this issue · 2 comments
Currently ezmsg-sigproc has SampleTriggerMessage
.
@dataclass(unsafe_hash=True)
class SampleTriggerMessage:
timestamp: float = field(default_factory=time.time)
period: Optional[Tuple[float, float]] = None
value: Any = None
I think we need something different to represent signal events, especially signal spikes like neural firing, heart beats, etc. The reason I don't like SampleTriggerMessage
is because it has a pair of floats that are optional, and it has a value
payload that can be anything (incl. None), thus it is difficult / slow to convert many instances of this struct to e.g. a sparse array.
I haven't put too much thought into it, but I'm thinking something like the following:
class EventMessage:
timestamp: float
ch_idx: int
sub_idx: int # e.g., 'unit id'
value: numeric
When a graph has a source that generates events like this, the next step might be a node to accumulate a bunch of events in a given window and convert it to a sparse matrix (e.g., scipy.sparse.csr) with a given sampling rate. Then, assuming we have nodes compatible with sparse matrices, we can convolve with some kernel to get event rate.
As support for event processing develops, I think there will be some mix of making ezmsg-sigproc nodes compatible with sparse matrices, and making custom nodes.
I guess my first question for the team is where do we want to define EventMessage
? Here? ezmsg-sigproc? Or a new package called ezmsg-event
that will contain other utilities for dealing with events and sparse matrices?
My only objection to putting the message structure in ezmsg-event
is that data source modules that can provide events (e.g., ezmsg-blackrock) shouldn't have scipy as a transient dependency.
SampleTriggerMessage was pretty purpose-made for that and only that unit, but I'm very on board with making a base-event class. ez.Flag is also an event
when you think about it this way. I support a PR to bring EventMessage into ezmsg.util.messages so that we can share these messages across multiple extensions.
Could possibly support some number of base-classes? Current ezmsg-event EventMessage
looks like:
@dataclass
class EventMessage:
offset: float
"""The temporal offset at which the event occurred. This is a float in seconds. The reference point is
unspecified and depends on the clock the application uses. Most applications will use time.time."""
ch_idx: int
sub_idx: int = 0
"""The sub-index of the channel. For Blackrock multi-unit data: 0=unsorted, 1-5 sorted unit, >5=noise"""
value: Number = 1
"""The value of the event. This can be any number, but is usually an integer, and is often 1 for spikes."""
To bring this into ezmsg.util, I could see making it much more general. Existing EventMessage
renamed to SpikeMessage
because it really seems to deal with spikes, documentation making explicit reference to Blackrock NSP...
The EventMessage
that could be rolled into core could just have the offset
and value
fields, with a default-value for offset
= 0. ch_idx
and sub_idx
could be additionally defined in the SpikeMessage subclass. Only forseeable problem is that defining default values for a base-class, all derived dataclasses would need default values for all parameters as well (which is a known issue/bummer re: dataclasses).
At the moment, most of my ezmsg-event
needs are being served by AxisArray with a pydata sparse
array in the .data field. So solving EventMessage
has decreased priority for me for now.