From bbf7daf6fb34c831dafc6111e9f51221b028b396 Mon Sep 17 00:00:00 2001 From: Zach McKenzie <92116279+zm711@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:40:29 -0400 Subject: [PATCH] Add neuronexus allego recording Extractor (#3235) * add neuronexus allego * add tests * fix neuronexus name * Heberto feedback * Fix capitalization * oops * add assert messaging * Update src/spikeinterface/extractors/neoextractors/neuronexus.py Co-authored-by: Zach McKenzie <92116279+zm711@users.noreply.github.com> --------- Co-authored-by: Heberto Mayorquin --- .../extractors/neoextractors/__init__.py | 2 + .../extractors/neoextractors/neuronexus.py | 66 +++++++++++++++++++ .../extractors/tests/common_tests.py | 7 +- .../extractors/tests/test_neoextractors.py | 8 +++ 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/spikeinterface/extractors/neoextractors/neuronexus.py diff --git a/src/spikeinterface/extractors/neoextractors/__init__.py b/src/spikeinterface/extractors/neoextractors/__init__.py index bf52de7c1d..03d517b46e 100644 --- a/src/spikeinterface/extractors/neoextractors/__init__.py +++ b/src/spikeinterface/extractors/neoextractors/__init__.py @@ -9,6 +9,7 @@ from .mearec import MEArecRecordingExtractor, MEArecSortingExtractor, read_mearec from .mcsraw import MCSRawRecordingExtractor, read_mcsraw from .neuralynx import NeuralynxRecordingExtractor, NeuralynxSortingExtractor, read_neuralynx, read_neuralynx_sorting +from .neuronexus import NeuroNexusRecordingExtractor, read_neuronexus from .neuroscope import ( NeuroScopeRecordingExtractor, NeuroScopeSortingExtractor, @@ -54,6 +55,7 @@ MCSRawRecordingExtractor, NeuralynxRecordingExtractor, NeuroScopeRecordingExtractor, + NeuroNexusRecordingExtractor, NixRecordingExtractor, OpenEphysBinaryRecordingExtractor, OpenEphysLegacyRecordingExtractor, diff --git a/src/spikeinterface/extractors/neoextractors/neuronexus.py b/src/spikeinterface/extractors/neoextractors/neuronexus.py new file mode 100644 index 0000000000..dca482b28a --- /dev/null +++ b/src/spikeinterface/extractors/neoextractors/neuronexus.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +from pathlib import Path + +from spikeinterface.core.core_tools import define_function_from_class + +from .neobaseextractor import NeoBaseRecordingExtractor, NeoBaseSortingExtractor + + +class NeuroNexusRecordingExtractor(NeoBaseRecordingExtractor): + """ + Class for reading data from NeuroNexus Allego. + + Based on :py:class:`neo.rawio.NeuronexusRawIO` + + Parameters + ---------- + file_path : str | Path + The file path to the metadata .xdat.json file of an Allego session + stream_id : str | None, default: None + If there are several streams, specify the stream id you want to load. + stream_name : str | None, default: None + If there are several streams, specify the stream name you want to load. + all_annotations : bool, default: False + Load exhaustively all annotations from neo. + use_names_as_ids : bool, default: False + Determines the format of the channel IDs used by the extractor. If set to True, the channel IDs will be the + names from NeoRawIO. If set to False, the channel IDs will be the ids provided by NeoRawIO. + + In Neuronexus the ids provided by NeoRawIO are the hardware channel ids stored as `ntv_chan_name` within + the metada and the names are the `chan_names` + + + """ + + NeoRawIOClass = "NeuroNexusRawIO" + + def __init__( + self, + file_path: str | Path, + stream_id: str | None = None, + stream_name: str | None = None, + all_annotations: bool = False, + use_names_as_ids: bool = False, + ): + neo_kwargs = self.map_to_neo_kwargs(file_path) + NeoBaseRecordingExtractor.__init__( + self, + stream_id=stream_id, + stream_name=stream_name, + all_annotations=all_annotations, + use_names_as_ids=use_names_as_ids, + **neo_kwargs, + ) + + self._kwargs.update(dict(file_path=str(Path(file_path).resolve()))) + + @classmethod + def map_to_neo_kwargs(cls, file_path): + + neo_kwargs = {"filename": str(file_path)} + + return neo_kwargs + + +read_neuronexus = define_function_from_class(source_class=NeuroNexusRecordingExtractor, name="read_neuronexus") diff --git a/src/spikeinterface/extractors/tests/common_tests.py b/src/spikeinterface/extractors/tests/common_tests.py index 5432efa9f3..61cfc2a153 100644 --- a/src/spikeinterface/extractors/tests/common_tests.py +++ b/src/spikeinterface/extractors/tests/common_tests.py @@ -52,8 +52,11 @@ def test_open(self): num_samples = rec.get_num_samples(segment_index=segment_index) full_traces = rec.get_traces(segment_index=segment_index) - assert full_traces.shape == (num_samples, num_chans) - assert full_traces.dtype == dtype + assert full_traces.shape == ( + num_samples, + num_chans, + ), f"{full_traces.shape} != {(num_samples, num_chans)}" + assert full_traces.dtype == dtype, f"{full_traces.dtype} != {dtype=}" traces_sample_first = rec.get_traces(segment_index=segment_index, start_frame=0, end_frame=1) assert traces_sample_first.shape == (1, num_chans) diff --git a/src/spikeinterface/extractors/tests/test_neoextractors.py b/src/spikeinterface/extractors/tests/test_neoextractors.py index 3f73161218..fcdd766f4f 100644 --- a/src/spikeinterface/extractors/tests/test_neoextractors.py +++ b/src/spikeinterface/extractors/tests/test_neoextractors.py @@ -181,6 +181,14 @@ class NeuroScopeSortingTest(SortingCommonTestSuite, unittest.TestCase): ] +class NeuroNexusRecordingTest(RecordingCommonTestSuite, unittest.TestCase): + ExtractorClass = NeuroNexusRecordingExtractor + downloads = ["neuronexus"] + entities = [ + ("neuronexus/allego_1/allego_2__uid0701-13-04-49.xdat.json", {"stream_id": "0"}), + ] + + class PlexonRecordingTest(RecordingCommonTestSuite, unittest.TestCase): ExtractorClass = PlexonRecordingExtractor downloads = ["plexon"]