Skip to content

Commit 54c655f

Browse files
bendichteroruebel
andauthored
add EventDetection and FeatureExtraction code to ecephys tutorial (#2002)
* add EventDetection code to ecephys tutorial * add code for FeatureExtraction * fix FeatureExtraction link * Add code for FilteredEphys * Update docs/gallery/domain/ecephys.py Co-authored-by: Oliver Ruebel <[email protected]> --------- Co-authored-by: Oliver Ruebel <[email protected]>
1 parent 8559eb2 commit 54c655f

File tree

1 file changed

+78
-41
lines changed

1 file changed

+78
-41
lines changed

docs/gallery/domain/ecephys.py

Lines changed: 78 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,27 @@
243243
)
244244
ecephys_module.add(lfp)
245245

246-
####################
246+
#######################
247+
# If the derived data is filtered but not downsampled, you can store the data in an
248+
# :py:class:`~pynwb.ecephys.ElectricalSeries` object in a :py:class:`~pynwb.ecephys.FilteredEphys` object
249+
# instead of a :py:class:`~pynwb.ecephys.LFP` object.
250+
251+
from pynwb.ecephys import FilteredEphys
252+
253+
filtered_data = np.random.randn(50, 12)
254+
filtered_electrical_series = ElectricalSeries(
255+
name="FilteredElectricalSeries",
256+
description="Filtered data",
257+
data=filtered_data,
258+
electrodes=all_table_region,
259+
starting_time=0.0,
260+
rate=200.0,
261+
)
262+
263+
filtered_ephys = FilteredEphys(electrical_series=filtered_electrical_series)
264+
ecephys_module.add(filtered_ephys)
265+
266+
################################
247267
# In some cases, you may want to further process the LFP data and decompose the signal into different frequency bands
248268
# to use for other downstream analyses. You can store the processed data from these spectral analyses using a
249269
# :py:class:`~pynwb.misc.DecompositionSeries` object. This object allows you to include metadata about the frequency
@@ -260,16 +280,23 @@
260280
gamma=(30.0, 80.0)) # in Hz
261281
phase_data = np.random.randn(50, 12, len(bands)) # 50 samples, 12 channels, 3 frequency bands
262282

263-
decomp_series = DecompositionSeries(name="theta",
264-
description="phase of bandpass filtered LFP data",
265-
data=phase_data,
266-
metric='phase',
267-
rate=200.0,
268-
source_channels=all_table_region,
269-
source_timeseries=lfp_electrical_series)
283+
decomp_series = DecompositionSeries(
284+
name="theta",
285+
description="phase of bandpass filtered LFP data",
286+
data=phase_data,
287+
metric='phase',
288+
rate=200.0,
289+
source_channels=all_table_region,
290+
source_timeseries=lfp_electrical_series,
291+
)
270292

271293
for band_name, band_limits in bands.items():
272-
decomp_series.add_band(band_name=band_name, band_limits=band_limits, band_mean=np.nan, band_stdev=np.nan)
294+
decomp_series.add_band(
295+
band_name=band_name,
296+
band_limits=band_limits,
297+
band_mean=np.nan,
298+
band_stdev=np.nan,
299+
)
273300

274301
ecephys_module.add(decomp_series)
275302

@@ -306,6 +333,10 @@
306333

307334
#######################
308335
# The :py:class:`~pynwb.misc.Units` table can also be converted to a pandas :py:class:`~pandas.DataFrame`.
336+
#
337+
# The :py:class:`~pynwb.misc.Units` table can contain simply the spike times of sorted units, or you can also include
338+
# individual and mean waveform information in some of the optional, predefined :py:class:`~pynwb.misc.Units` table
339+
# columns: ``waveform_mean``, ``waveform_sd``, or ``waveforms``.
309340

310341
nwbfile.units.to_dataframe()
311342

@@ -324,44 +355,50 @@
324355
description="shank0",
325356
)
326357

327-
328-
spike_events = SpikeEventSeries(name='SpikeEvents_Shank0',
329-
description="events detected with 100uV threshold",
330-
data=spike_snippets,
331-
timestamps=np.arange(20),
332-
electrodes=shank0)
358+
spike_events = SpikeEventSeries(
359+
name='SpikeEvents_Shank0',
360+
description="events detected with 100uV threshold",
361+
data=spike_snippets,
362+
timestamps=np.arange(20),
363+
electrodes=shank0,
364+
)
333365
nwbfile.add_acquisition(spike_events)
334366

335-
#######################
336-
# Designating electrophysiology data
337-
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
338-
#
339-
# As mentioned above, :py:class:`~pynwb.ecephys.ElectricalSeries` objects
340-
# are meant for storing specific types of extracellular recordings. In addition to this
341-
# :py:class:`~pynwb.base.TimeSeries` class, NWB provides some :ref:`modules_overview`
342-
# for designating the type of data you are storing. We will briefly discuss them here, and refer the reader to
343-
# :py:mod:`API documentation <pynwb.ecephys>` and :ref:`basics` for more details on
344-
# using these objects.
345-
#
346-
# For storing unsorted spiking data, there are two options. Which one you choose depends on what data you
347-
# have available. If you need to store the complete, continuous raw voltage traces, you should store the traces with
348-
# :py:class:`~pynwb.ecephys.ElectricalSeries` objects as :ref:`acquisition <basic_timeseries>` data, and use
349-
# the :py:class:`~pynwb.ecephys.EventDetection` class for identifying the spike events in your raw traces.
367+
############################################
368+
# If you need to store the complete, continuous raw voltage traces, along with unsorted spike times, you should store
369+
# the traces with :py:class:`~pynwb.ecephys.ElectricalSeries` objects as :ref:`acquisition <basic_timeseries>` data,
370+
# and use the :py:class:`~pynwb.ecephys.EventDetection` class to identify the spike events in your raw traces.
371+
372+
from pynwb.ecephys import EventDetection
373+
374+
event_detection = EventDetection(
375+
name="threshold_events",
376+
detection_method="thresholding, 1.5 * std",
377+
source_electricalseries=raw_electrical_series,
378+
source_idx=[1000, 2000, 3000],
379+
times=[.033, .066, .099],
380+
)
381+
382+
ecephys_module.add(event_detection)
383+
384+
######################################
350385
# If you do not want to store the raw voltage traces and only the waveform 'snippets' surrounding spike events,
351386
# you should use :py:class:`~pynwb.ecephys.SpikeEventSeries` objects.
352387
#
353-
# The results of spike sorting (or clustering) should be stored in the top-level :py:class:`~pynwb.misc.Units` table.
354-
# The :py:class:`~pynwb.misc.Units` table can contain simply the spike times of sorted units, or you can also include
355-
# individual and mean waveform information in some of the optional, predefined :py:class:`~pynwb.misc.Units` table
356-
# columns: ``waveform_mean``, ``waveform_sd``, or ``waveforms``.
357-
#
358-
# For local field potential data, there are two options. Again, which one you choose depends on what data you
359-
# have available. With both options, you should store your traces with :py:class:`~pynwb.ecephys.ElectricalSeries`
360-
# objects. If you are storing unfiltered local field potential data, you should store
361-
# the :py:class:`~pynwb.ecephys.ElectricalSeries` objects in :py:class:`~pynwb.ecephys.LFP` data interface object(s).
362-
# If you have filtered LFP data, you should store the :py:class:`~pynwb.ecephys.ElectricalSeries` objects in
363-
# :py:class:`~pynwb.ecephys.FilteredEphys` data interface object(s).
388+
# NWB also provides a way to store features of spikes, such as principal components, using the
389+
# :py:class:`~pynwb.ecephys.FeatureExtraction` class.
390+
391+
from pynwb.ecephys import FeatureExtraction
392+
393+
feature_extraction = FeatureExtraction(
394+
name="PCA_features",
395+
electrodes=all_table_region,
396+
description=["PC1", "PC2", "PC3", "PC4"],
397+
times=[.033, .066, .099],
398+
features=np.random.rand(3, 12, 4), # time, channel, feature
399+
)
364400

401+
ecephys_module.add(feature_extraction)
365402

366403
####################
367404
# .. _ecephys_writing:

0 commit comments

Comments
 (0)