From a0b928d646318c9726ed2547f265b377d64388ad Mon Sep 17 00:00:00 2001 From: bendichter Date: Mon, 18 Feb 2019 17:16:55 -0800 Subject: [PATCH 1/3] add link spike_event_series to UnitSeries --- src/pynwb/data/nwb.ecephys.yaml | 5 +++++ src/pynwb/ecephys.py | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/pynwb/data/nwb.ecephys.yaml b/src/pynwb/data/nwb.ecephys.yaml index 6cb485afa..68b1222ef 100644 --- a/src/pynwb/data/nwb.ecephys.yaml +++ b/src/pynwb/data/nwb.ecephys.yaml @@ -98,6 +98,11 @@ groups: - num_times shape: - null + links: + - name: unit_series + doc: the UnitSeries that holds the unit ids for each waveform + quantity: '?' + target_type: UnitSeries - neurodata_type_def: ClusterWaveforms neurodata_type_inc: NWBDataInterface doc: 'DEPRECATED The mean waveform shape, including standard deviation, of the diff --git a/src/pynwb/ecephys.py b/src/pynwb/ecephys.py index c2972dc3e..e77ec3168 100644 --- a/src/pynwb/ecephys.py +++ b/src/pynwb/ecephys.py @@ -8,7 +8,7 @@ from .base import TimeSeries, _default_resolution, _default_conversion from .core import NWBContainer, NWBDataInterface, MultiContainerInterface, DynamicTableRegion from .device import Device -from .misc import DecompositionSeries +from .misc import DecompositionSeries, UnitSeries @register_class('ElectrodeGroup', CORE_NAMESPACE) @@ -108,7 +108,7 @@ class SpikeEventSeries(ElectricalSeries): electrode). """ - __nwbfields__ = () + __nwbfields__ = ({'name': 'unit_series', 'doc': 'doc', 'child': False},) __help = "Snapshots of spike events from data." @@ -119,6 +119,8 @@ class SpikeEventSeries(ElectricalSeries): 'doc': 'Timestamps for samples stored in data'}, {'name': 'electrodes', 'type': DynamicTableRegion, 'doc': 'the table region corresponding to the electrodes from which this series was recorded'}, + {'name': 'unit_series', 'type': UnitSeries, 'default': None, + 'doc': 'UnitSeries that matches waveforms to spikes in Units table'}, {'name': 'resolution', 'type': float, 'doc': 'The smallest meaningful difference (in specified unit) between values in data', 'default': _default_resolution}, @@ -135,7 +137,7 @@ class SpikeEventSeries(ElectricalSeries): {'name': 'parent', 'type': 'NWBContainer', 'doc': 'The parent NWBContainer for this NWBContainer', 'default': None}) def __init__(self, **kwargs): - name, data, electrodes = popargs('name', 'data', 'electrodes', kwargs) + name, data, electrodes, unit_series = popargs('name', 'data', 'electrodes', 'unit_series', kwargs) timestamps = getargs('timestamps', kwargs) if not (isinstance(data, TimeSeries) and isinstance(timestamps, TimeSeries)): if not (isinstance(data, DataChunkIterator) and isinstance(timestamps, DataChunkIterator)): @@ -145,6 +147,7 @@ def __init__(self, **kwargs): # TODO: add check when we have DataChunkIterators pass super(SpikeEventSeries, self).__init__(name, data, electrodes, **kwargs) + self.unit_series = unit_series @register_class('EventDetection', CORE_NAMESPACE) From a9ff80c82c3bd5663963f1a0c8d0cc9b7f3937e3 Mon Sep 17 00:00:00 2001 From: Ben Dichter Date: Tue, 19 Feb 2019 10:26:09 -0800 Subject: [PATCH 2/3] fox doc for SpikeEventSeries.unit_series --- src/pynwb/ecephys.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pynwb/ecephys.py b/src/pynwb/ecephys.py index e77ec3168..6d99a8343 100644 --- a/src/pynwb/ecephys.py +++ b/src/pynwb/ecephys.py @@ -108,7 +108,9 @@ class SpikeEventSeries(ElectricalSeries): electrode). """ - __nwbfields__ = ({'name': 'unit_series', 'doc': 'doc', 'child': False},) + __nwbfields__ = ({'name': 'unit_series', + 'doc': 'the UnitSeries that holds the unit ids for each waveform', + 'child': False},) __help = "Snapshots of spike events from data." From eba429a9d6dd16358cc36cf0499666b403ffec22 Mon Sep 17 00:00:00 2001 From: Andrew Tritt Date: Tue, 19 Feb 2019 10:42:14 -0800 Subject: [PATCH 3/3] Support variable-name link specifications --- src/pynwb/form/build/map.py | 21 +++++++++++++++------ src/pynwb/form/spec/spec.py | 9 ++++++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/pynwb/form/build/map.py b/src/pynwb/form/build/map.py index 0d95e8abc..815b74b72 100644 --- a/src/pynwb/form/build/map.py +++ b/src/pynwb/form/build/map.py @@ -540,12 +540,15 @@ def get_container_name(self, *args): def convert_dt_name(cls, **kwargs): '''Get the attribute name corresponding to a specification''' spec = getargs('spec', kwargs) - if spec.data_type_def is not None: - name = spec.data_type_def - elif spec.data_type_inc is not None: - name = spec.data_type_inc + if isinstance(spec, LinkSpec): + name = spec.target_type else: - raise ValueError('found spec without name or data_type') + if spec.data_type_def is not None: + name = spec.data_type_def + elif spec.data_type_inc is not None: + name = spec.data_type_inc + else: + raise ValueError('found spec without name or data_type') s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() if name[-1] != 's' and spec.is_many(): @@ -1051,7 +1054,9 @@ def __get_subspec_values(self, builder, spec, manager): # now assign links to their respective specification for subspec in spec.links: if subspec.name is not None: - ret[subspec] = manager.construct(links[subspec.name].builder) + sub_builder = links.get(subspec.name) + if sub_builder is not None: + ret[subspec] = manager.construct(sub_builder.builder) else: sub_builder = link_dt.get(subspec.target_type) if sub_builder is not None: @@ -1100,6 +1105,10 @@ def __get_sub_builders(self, sub_builders, subspecs, manager, ret): ret[subspec] = manager.construct(sub_builder) def __flatten(self, sub_builder, subspec, manager): + """ + Convert one-or-many to a single object or a list, + depending on the spec + """ tmp = [manager.construct(b) for b in sub_builder] if len(tmp) == 1 and not subspec.is_many(): tmp = tmp[0] diff --git a/src/pynwb/form/spec/spec.py b/src/pynwb/form/spec/spec.py index a11ad1242..23f5072ae 100644 --- a/src/pynwb/form/spec/spec.py +++ b/src/pynwb/form/spec/spec.py @@ -945,9 +945,12 @@ def is_inherited_spec(self, **kwargs): if isinstance(spec, Spec): name = spec.name if name is None: - name = spec.data_type_def - if name is None: - name = spec.data_type_inc + if isinstance(spec, LinkSpec): + name = spec.target_type + else: + name = spec.data_type_def + if name is None: + name = spec.data_type_inc if name is None: raise ValueError('received Spec with wildcard name but no data_type_inc or data_type_def') spec = name