Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] add links for referencing SpikeEventWaveforms from Units table #113

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 deletions schema/core/nwb.ecephys.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ groups:
- null
- neurodata_type_inc: DynamicTableRegion
name: electrodes
quantity: '?'
doc: the electrodes that this series was generated from
links:
- name: electrode_group
quantity: '?'
target_type: ElectrodeGroup
- neurodata_type_def: SpikeEventSeries
neurodata_type_inc: ElectricalSeries
doc: 'Stores ''snapshots'' of spike events (i.e., threshold crossings) in data. This
Expand Down Expand Up @@ -92,20 +97,25 @@ 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
different clusters. Ideally, the waveform analysis should be performed on data that
is only high-pass filtered. This is a separate module because it is expected to require
updating. For example, IMEC probes may require different storage requirements to
store/display mean waveforms, requiring a new interface or an extension of this one.'
doc: 'DEPRECATED. The mean waveform shape, including standard deviation, of the different clusters.
Ideally, the waveform analysis should be performed on data that is only high-pass
filtered. This is a separate module because it is expected to require updating.
For example, IMEC probes may require different storage requirements to store/display
mean waveforms, requiring a new interface or an extension of this one.'
attributes:
- name: help
dtype: text
doc: Value is 'Mean waveform shape of clusters. Waveforms should be high-pass
filtered (ie, not the same bandpass filter used waveform analysis and clustering)'
value: DEPRECATED Mean waveform shape of clusters. Waveforms should be high-pass
filtered (ie, not the same bandpass filter used waveform analysis and clustering)
value: Mean waveform shape of clusters. Waveforms should be high-pass filtered
(ie, not the same bandpass filter used waveform analysis and clustering)
datasets:
- name: waveform_filtering
dtype: text
Expand Down Expand Up @@ -138,15 +148,15 @@ groups:
default_name: ClusterWaveforms
- neurodata_type_def: Clustering
neurodata_type_inc: NWBDataInterface
doc: DEPRECATED Clustered spike data, whether from automatic clustering tools (e.g.,
klustakwik) or as a result of manual sorting.
doc: 'DEPRECATED. Clustered spike data, whether from automatic clustering tools (e.g., klustakwik)
or as a result of manual sorting.'
attributes:
- name: help
dtype: text
doc: Value is 'Clustered spike data, whether from automatic clustering tools (eg,
klustakwik) or as a result of manual sorting'
value: DEPRECATED Clustered spike data, whether from automatic clustering tools
(eg, klustakwik) or as a result of manual sorting
value: Clustered spike data, whether from automatic clustering tools (eg, klustakwik)
or as a result of manual sorting
datasets:
- name: description
dtype: text
Expand Down Expand Up @@ -334,3 +344,7 @@ groups:
doc: the device that was used to record from this electrode group
quantity: '?'
target_type: Device
- name: spike_event_series
doc: the SpikeEventSeries that holds the recorded spike snippets for this electrode group
quantity: '?'
target_type: SpikeEventSeries
33 changes: 33 additions & 0 deletions schema/core/nwb.misc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,36 @@ groups:
- null
quantity: '?'
default_name: Units
- neurodata_type_def: UnitSeries
neurodata_type_inc: TimeSeries
doc: Unit spike times a stream of IDs of spiking units
attributes:
- name: help
dtype: text
doc: Value is 'Unit spike times a stream of IDs of spiking units'
value: Unit spike times a stream of IDs of spiking units
datasets:
- name: data
dtype: int
doc: the index of the spike unit in the DynamicTableRegion "units"
attributes:
- name: resolution
dtype: float
doc: Value is -1.0. Indices do not have resolution
value: -1.0
- name: unit
dtype: text
doc: Value is 'index'
value: index
dims:
- num_times
shape:
- null
links:
- name: units
doc: The units table that is being indexed
target_type: Units
- name: spike_event_series
doc: The SpikeEventSeries that holds the recorded spike snippets for each spike event
quantity: '?'
target_type: SpikeEventSeries
2 changes: 1 addition & 1 deletion schema/core/nwb.namespace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ namespaces:
- source: nwb.ogen.yaml
- source: nwb.ophys.yaml
- source: nwb.retinotopy.yaml
version: 2.0.0
version: 2.0.0b
1 change: 0 additions & 1 deletion schema/core/nwb.ophys.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,6 @@ groups:
or vectors needed to rotate to common anotomical axis (eg, AP/DV/ML). COMMENT:
This field is necessary to interpret manifold. If manifold is not present then
this field is not required'
quantity: '?'
groups:
- neurodata_type_def: OpticalChannel
neurodata_type_inc: NWBContainer
Expand Down
81 changes: 58 additions & 23 deletions tutorials/ecephys.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,27 @@
%
%% Electrode Table
% Electrode tables hold the position and group information about each
% electrode and the brain region and filtering. Groups organize electrodes
% within a single device. Devices can have 1 or more groups. In this example,
% we have 2 devices that each only have a single group.
% electrode and the brain region and filtering. |ElectrodeGroup|s organize
% electrodes within a single device into groups that are close together and
% could record the same unitDevices can have 1 or more groups. In this
% example, we have 2 devices that each only have a single group.

device_labels = {'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b'};

udevice_labels = unique(device_labels, 'stable');

variables = {'x', 'y', 'z', 'imp', 'location', 'filtering', 'group', ...
'label'};

groups = {};
for i_device = 1:length(udevice_labels)
device_label = udevice_labels{i_device};

nwb.general_devices.set(device_label, ...
types.core.Device());

nwb.general_extracellular_ephys.set(device_label, ...
types.core.ElectrodeGroup( ...
nwb.general_extracellular_ephys.set(device_label, ...
types.core.ElectrodeGroup( ...
'description', 'a test ElectrodeGroup', ...
'location', 'unknown', ...
'device', types.untyped.SoftLink(['/general/devices/' device_label])));
Expand Down Expand Up @@ -123,6 +126,28 @@
'electrodes', electrode_table_region,...
'data_unit','V');

%% Spike Waveforms
% Individual spike waveforms are stored in |SpikeEventSeries| objects.
% Associate these objects with an |ElectrodeGroup| by adding a link to the
% appropriate |ElectrodeGroup| object.

data = ones(8, 2, 30); % nspikes x [nchannels] x nsamples

spike_event_series = types.core.SpikeEventSeries( ...
'data', data, ...
'data_unit', 'V', ...
'data_conversion', .001, ...
'data_resolution', NaN, ...
'description', 'spike events for shank 1', ...
'electrode_group', types.untyped.SoftLink('/general/extracellular_ephys/a'), ...
'timestamps', [0.1, 0.21, 0.34, 0.36, 0.5, 0.61, 0.66, 0.69]);

nwb.acquisition.set('SpikeEventSeries_a', spike_event_series);

group_a = nwb.general_extracellular_ephys.get('a');
group_a.spike_event_series = types.untyped.SoftLink('/acquisition/SpikeEventSeries_a');
nwb.general_extracellular_ephys.set('a', group_a);

%% Trials
% You can store trial information in the trials table

Expand Down Expand Up @@ -191,18 +216,19 @@
% value is indexed, only the column name is included, not the index. For
% instance, |'spike_times_index'| is not added to the array.
nwb.units = types.core.Units( ...
'colnames', {'spike_times', 'waveform_mean', 'quality', 'electrodes'}, ...
'colnames', {'spike_times', 'waveform_mean', 'quality', 'electrodes' ...
'electrode_group'}, ...
'description', 'units table', ...
'id', types.core.ElementIdentifiers('data', int64(0:2)));

% Then you can add the data column-by-column:
waveform_mean = types.core.VectorData('data', ones(30, 3), ...
nwb.units.waveform_mean = types.core.VectorData( ...
'data', ones(30, 3), ...
'description', 'mean of waveform');
nwb.units.waveform_mean = waveform_mean;

quality = types.core.VectorData('data', [.9, .1, .2],...
'description', 'sorting quality score out of 1');
nwb.units.vectordata.set('quality', quality);
nwb.units.vectordata.set('quality', types.core.VectorData( ...
'data', [.9, .1, .2],...
'description', 'sorting quality score out of 1'));

spike_times_cells = {[0.1, 0.21, 0.5, 0.61], [0.34, 0.36, 0.66, 0.69], [0.4, 0.43]};
[spike_times_vector, spike_times_index] = util.create_indexed_column( ...
Expand All @@ -216,6 +242,11 @@
nwb.units.electrodes = electrodes;
nwb.units.electrodes_index = electrodes_index;

nwb.units.electrode_group = types.core.VectorData( ...
'data', repmat(types.untyped.ObjectView('/general/extracellular_ephys/a'), 2, 1), ...
'description', 'electrode group');


%% Side note about electrodes
% In the above example, we assigned multiple electrodes to each unit. This
% is useful in some recording setups, where electrodes are close together
Expand All @@ -233,6 +264,7 @@
'description', 'single electrodes', ...
'data', int64([0, 0, 1]));


%% Processing Modules
% Measurements go in |acquisition| and subject or session data goes in
% |general|, but if you have the intermediate processing results, you
Expand All @@ -241,18 +273,21 @@
ecephys_mod = types.core.ProcessingModule('description', 'contains clustering data');

%%
% The |Clustering| data structure holds information about the spike-sorting
% process. |units| should be used in most cases, but |Clustering| can be
% useful for some specific applications like streaming spike sorting results
% directly from an acquisition system.

clustering = types.core.Clustering( ...
'description', 'my_description', ...
'peak_over_rms', [1, 2, 3], ...
'times', [0.1, 0.2, 0.3, 0.4, 0.5], ...
'num', int64([0, 0, 1, 1, 2]));

ecephys_mod.nwbdatainterface.set('clustering', clustering);
% Once you have a |SpikeEventSeries| and a |Units| table, you can link
% them with a |UnitSeries| object

ecephys_mod.nwbdatainterface.set('UnitSeries_a', types.core.UnitSeries( ...
'data', int64([0, 0, 1, 1, 0, 0, 1, 1]), ...
'data_conversion', NaN, ...
'data_resolution', NaN, ...
'data_unit', 'NA', ...
'description', 'links SpikeEventSeries_a to units table', ...
'spike_event_series', types.untyped.SoftLink('/acquisition/SpikeEventSeries_a'), ...
'timestamps', [0.1, 0.21, 0.34, 0.36, 0.5, 0.61, 0.66, 0.69], ...
'units', types.untyped.SoftLink('/units')));

ses = nwb.acquisition.get('SpikeEventSeries_a');
ses.unit_series = types.untyped.SoftLink('/processing/ecephys/nwbdatainterface/UnitSeries_a');

%%
% I am going to call this processing module "ecephys." As a convention, I
Expand Down