-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
749 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,96 @@ | ||
groups: | ||
- neurodata_type_def: TetrodeSeries | ||
neurodata_type_inc: ElectricalSeries | ||
doc: An extension of ElectricalSeries to include the tetrode ID for each time series. | ||
- neurodata_type_def: Events | ||
neurodata_type_inc: NWBDataInterface | ||
doc: A list of timestamps, stored in seconds, of an event. | ||
attributes: | ||
- name: trode_id | ||
dtype: int32 | ||
doc: The tetrode ID. | ||
- name: description | ||
dtype: text | ||
doc: Description of the event. | ||
datasets: | ||
- name: timestamps | ||
dtype: float32 | ||
dims: | ||
- num_events | ||
shape: | ||
- null | ||
doc: Event timestamps, in seconds, relative to the common experiment master-clock | ||
stored in NWBFile.timestamps_reference_time. | ||
attributes: | ||
- name: unit | ||
dtype: text | ||
value: seconds | ||
doc: Unit of measurement for timestamps, which is fixed to 'seconds'. | ||
- name: resolution | ||
dtype: float32 | ||
doc: The smallest possible difference between two event times. Usually 1 divided | ||
by the event time sampling rate on the data acquisition system. | ||
required: false | ||
- neurodata_type_def: LabeledEvents | ||
neurodata_type_inc: Events | ||
doc: A list of timestamps, stored in seconds, of an event that can have different | ||
labels. For example, this type could represent the times that reward was given, | ||
as well as which of three different types of reward was given. In this case, the | ||
'label_keys' dataset would contain values {0, 1, 2}, and the 'labels' dataset | ||
would contain three text elements, where the first (index 0) specifies the name | ||
of the reward associated with a label_keys = 0, the second (index 1) specifies | ||
the name of the reward associated with a label_keys = 1, etc. The labels do not | ||
have to start at 0 and do not need to be sequential, e.g. the 'label_keys' dataset | ||
could contain values {0, 10, 100}, and the 'labels' dataset could contain 101 | ||
values, where labels[0] is 'No reward', labels[10] is '10% reward', labels[100] | ||
is 'Full reward', and all other entries in 'labels' are the empty string. | ||
datasets: | ||
- name: label_keys | ||
dtype: uint8 | ||
dims: | ||
- num_events | ||
shape: | ||
- null | ||
doc: Integer labels that map onto strings using the mapping in the 'labels' dataset. | ||
Values must be 0 or greater and need not be sequential. This dataset should | ||
have the same number of elements as the 'timestamps' dataset. | ||
- name: labels | ||
dtype: text | ||
dims: | ||
- num_labels | ||
shape: | ||
- null | ||
doc: Mapping from an integer (the zero-based index) to a string, used to understand | ||
the integer values in the 'label_keys' dataset. Use an empty string to represent | ||
a label value that is not mapped to any text. Use '' to represent any values | ||
that are None or empty. | ||
- neurodata_type_def: TTLs | ||
neurodata_type_inc: LabeledEvents | ||
doc: Data type to hold timestamps of TTL pulses. The 'label_keys' dataset contains | ||
the integer pulse values, and the 'labels' dataset contains user-defined labels | ||
associated with each pulse value. The value at index n of the 'labels' dataset | ||
corresponds to a pulse value of n. For example, the first value (index 0) of the | ||
'labels' dataset corresponds to a pulse value of 0. See the LabeledEvents type | ||
for more details. | ||
- neurodata_type_def: AnnotatedEvents | ||
neurodata_type_inc: DynamicTable | ||
doc: Table to hold event timestamps and event metadata relevant to data preprocessing | ||
and analysis. Each row corresponds to a different event type. Use the 'event_time' | ||
dataset to store timestamps for each event type. Add user-defined columns to add | ||
metadata for each event type or event time. | ||
datasets: | ||
- name: event_times_index | ||
neurodata_type_inc: VectorIndex | ||
doc: Index into the event_times dataset. | ||
- name: event_times | ||
neurodata_type_inc: VectorData | ||
dtype: float32 | ||
doc: Event times for each event type. | ||
attributes: | ||
- name: resolution | ||
dtype: float32 | ||
doc: The smallest possible difference between two event times. Usually 1 divided | ||
by the event time sampling rate on the data acquisition system. | ||
required: false | ||
- name: label | ||
neurodata_type_inc: VectorData | ||
dtype: text | ||
doc: Label for each event type. | ||
- name: description | ||
neurodata_type_inc: VectorData | ||
dtype: text | ||
doc: Description for each event type. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import numpy as np | ||
|
||
from pynwb import register_class | ||
from pynwb.core import NWBDataInterface, DynamicTable | ||
from hdmf.utils import docval, getargs, popargs, call_docval_func, get_docval | ||
|
||
|
||
@register_class('Events', 'ndx-events') | ||
class Events(NWBDataInterface): | ||
""" | ||
A list of timestamps, stored in seconds, of an event. | ||
""" | ||
|
||
__nwbfields__ = ('description', | ||
'timestamps', | ||
'resolution', | ||
{'name': 'unit', 'settable': False}) | ||
|
||
@docval({'name': 'name', 'type': str, 'doc': 'The name of this Events object'}, # required | ||
{'name': 'description', 'type': str, 'doc': 'The name of this Events object'}, # required | ||
{'name': 'timestamps', 'type': ('array_data', 'data'), # required | ||
'doc': ('Event timestamps, in seconds, relative to the common experiment master-clock ' | ||
'stored in NWBFile.timestamps_reference_time.'), | ||
'shape': (None,)}, | ||
{'name': 'resolution', 'type': float, | ||
'doc': ('The smallest possible difference between two event times. Usually 1 divided ' | ||
'by the event time sampling rate on the data acquisition system.'), | ||
'default': None}) | ||
def __init__(self, **kwargs): | ||
description, timestamps, resolution = popargs('description', 'timestamps', 'resolution', kwargs) | ||
call_docval_func(super().__init__, kwargs) | ||
self.description = description | ||
self.timestamps = timestamps | ||
self.resolution = resolution | ||
self.fields['unit'] = 'seconds' | ||
|
||
|
||
@register_class('LabeledEvents', 'ndx-events') | ||
class LabeledEvents(Events): | ||
""" | ||
A list of timestamps, stored in seconds, of an event that can have different | ||
labels. For example, this type could represent the times that reward was given, | ||
as well as which of three different types of reward was given. In this case, the | ||
'label_keys' dataset would contain values {0, 1, 2}, and the 'labels' dataset | ||
would contain three text elements, where the first (index 0) specifies the name | ||
of the reward associated with a label_keys = 0, the second (index 1) specifies | ||
the name of the reward associated with a label_keys = 1, etc. The labels do not | ||
have to start at 0 and do not need to be sequential, e.g. the 'label_keys' dataset | ||
could contain values {0, 10, 100}, and the 'labels' dataset could contain 101 | ||
values, where labels[0] is 'No reward', labels[10] is '10% reward', labels[100] | ||
is 'Full reward', and all other entries in 'labels' are the empty string. | ||
""" | ||
|
||
__nwbfields__ = ('label_keys', | ||
'labels') | ||
|
||
@docval(*get_docval(Events.__init__, 'name', 'description', 'timestamps'), # required | ||
{'name': 'label_keys', 'type': ('array_data', 'data'), # required | ||
'doc': ("Integer labels that map onto strings using the mapping in the 'labels' dataset. " | ||
"Values must be 0 or greater and need not be sequential. This dataset should " | ||
"have the same number of elements as the 'timestamps' dataset."), | ||
'shape': (None,)}, | ||
{'name': 'labels', 'type': ('array_data', 'data'), | ||
'doc': ("Mapping from an integer (the zero-based index) to a string, used to understand " | ||
"the integer values in the 'label_keys' dataset. Use an empty string to represent " | ||
"a label value that is not mapped to any text. Use '' to represent any values " | ||
"that are None or empty. If the argument is not specified, the label " | ||
"will be set to the string representation of the label keys and '' for other values."), | ||
'shape': (None,), 'default': None}, | ||
*get_docval(Events.__init__, 'resolution')) | ||
def __init__(self, **kwargs): | ||
timestamps = getargs('timestamps', kwargs) | ||
label_keys, labels = popargs('label_keys', 'labels', kwargs) | ||
call_docval_func(super().__init__, kwargs) | ||
if len(timestamps) != len(label_keys): | ||
raise ValueError('Timestamps and label_keys must have the same length: %d != %d' | ||
% (len(timestamps), len(label_keys))) | ||
self.label_keys = label_keys | ||
if labels is None: | ||
unique_keys = np.unique(label_keys) | ||
self.labels = [''] * (max(unique_keys) + 1) | ||
for k in unique_keys: | ||
self.labels[k] = str(k) | ||
else: | ||
if None in labels: | ||
raise ValueError("None values are not allowed in the labels array. Please use '' for undefined label " | ||
"keys.") | ||
self.labels = labels | ||
|
||
|
||
@register_class('TTLs', 'ndx-events') | ||
class TTLs(LabeledEvents): | ||
""" | ||
Data type to hold timestamps of TTL pulses. The 'label_keys' dataset contains | ||
the integer pulse values, and the 'labels' dataset contains user-defined labels | ||
associated with each pulse value. The value at index n of the 'labels' dataset | ||
corresponds to a pulse value of n. For example, the first value (index 0) of the | ||
'labels' dataset corresponds to a pulse value of 0. See the LabeledEvents type | ||
for more details. | ||
""" | ||
pass | ||
|
||
|
||
@register_class('AnnotatedEvents', 'ndx-events') | ||
class AnnotatedEvents(DynamicTable): | ||
""" | ||
Table to hold event timestamps and event metadata relevant to data preprocessing | ||
and analysis. Each row corresponds to a different event type. Use the 'event_time' | ||
dataset to store timestamps for each event type. Add user-defined columns to add | ||
metadata for each event type or event time. | ||
""" | ||
|
||
__fields__ = ( | ||
'resolution', | ||
) | ||
|
||
__columns__ = ( | ||
{'name': 'event_times', 'description': 'Event times for each event type.', 'index': True}, | ||
{'name': 'label', 'description': 'Label for each event type.'}, | ||
{'name': 'event_description', 'description': 'Description for each event type.'} | ||
# note that the name 'description' cannot be used because it is already an attribute on VectorData | ||
) | ||
|
||
@docval({'name': 'description', 'type': str, 'doc': 'Description of what is in this table'}, | ||
{'name': 'name', 'type': str, 'doc': 'Name of this AnnotatedEvents table', 'default': 'AnnotatedEvents'}, | ||
{'name': 'resolution', 'type': float, | ||
'doc': ('The smallest possible difference between two event times. Usually 1 divided ' | ||
'by the event time sampling rate on the data acquisition system.'), | ||
'default': None}, | ||
*get_docval(DynamicTable.__init__, 'id', 'columns', 'colnames')) | ||
def __init__(self, **kwargs): | ||
resolution = popargs('resolution', kwargs) | ||
call_docval_func(super().__init__, kwargs) | ||
self.resolution = resolution | ||
|
||
@docval({'name': 'label', 'type': str, 'doc': 'Label for each event type.'}, | ||
{'name': 'event_description', 'type': str, 'doc': 'Description for each event type.'}, | ||
{'name': 'event_times', 'type': 'array_data', 'doc': 'Event times for each event type.', 'shape': (None,)}, | ||
{'name': 'id', 'type': int, 'doc': 'ID for each unit', 'default': None}, | ||
allow_extra=True) | ||
def add_event_type(self, **kwargs): | ||
"""Add an event type as a row to this table.""" | ||
# TODO columns do not exist and are hitting table.py line 377 for a name clash | ||
super().add_row(**kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import events as __events # noqa: E402,F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from pynwb import register_map | ||
from pynwb.io.core import NWBContainerMapper | ||
from hdmf.common.io.table import DynamicTableMap | ||
from hdmf.build import ObjectMapper, BuildManager | ||
from hdmf.common import VectorData | ||
from hdmf.utils import getargs, docval | ||
from hdmf.spec import AttributeSpec | ||
|
||
from ..events import Events, AnnotatedEvents | ||
|
||
|
||
@register_map(Events) | ||
class EventsMap(NWBContainerMapper): | ||
|
||
def __init__(self, spec): | ||
super().__init__(spec) | ||
timestamps_spec = self.spec.get_dataset('timestamps') | ||
self.map_spec('unit', timestamps_spec.get_attribute('unit')) | ||
self.map_spec('resolution', timestamps_spec.get_attribute('resolution')) | ||
|
||
|
||
@register_map(AnnotatedEvents) | ||
class AnnotatedEventsMap(DynamicTableMap): | ||
|
||
def __init__(self, spec): | ||
super().__init__(spec) | ||
event_times_spec = self.spec.get_dataset('event_times') | ||
self.map_spec('resolution', event_times_spec.get_attribute('resolution')) | ||
|
||
@DynamicTableMap.constructor_arg('resolution') | ||
def resolution_carg(self, builder, manager): | ||
if 'event_times' in builder: | ||
return builder['event_times'].attributes.get('resolution') | ||
return None | ||
|
||
|
||
@register_map(VectorData) | ||
class VectorDataMap(ObjectMapper): | ||
|
||
# TODO fold this into pynwb.io.core.VectorDataMap | ||
|
||
@docval({"name": "spec", "type": AttributeSpec, "doc": "the spec to get the attribute value for"}, | ||
{"name": "container", "type": VectorData, "doc": "the container to get the attribute value from"}, | ||
{"name": "manager", "type": BuildManager, "doc": "the BuildManager used for managing this build"}, | ||
returns='the value of the attribute') | ||
def get_attr_value(self, **kwargs): | ||
''' Get the value of the attribute corresponding to this spec from the given container ''' | ||
spec, container, manager = getargs('spec', 'container', 'manager', kwargs) | ||
|
||
# handle custom mapping of container AnnotatedEvents.resolution -> spec AnnotatedEvents.event_times.resolution | ||
if isinstance(container.parent, AnnotatedEvents): | ||
if container.name == 'event_times': | ||
if spec.name == 'resolution': | ||
return container.parent.resolution | ||
return super().get_attr_value(**kwargs) |
Oops, something went wrong.