From 81a610d9bb036392c53e23b6546365c65b2ad68f Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Wed, 6 Sep 2023 09:41:23 +0200 Subject: [PATCH 1/6] add neuroexplorer --- .../extractors/neoextractors/__init__.py | 2 + .../extractors/neoextractors/neuroexplorer.py | 66 +++++++++++++++++++ .../extractors/tests/test_neoextractors.py | 11 ++++ 3 files changed, 79 insertions(+) create mode 100644 src/spikeinterface/extractors/neoextractors/neuroexplorer.py diff --git a/src/spikeinterface/extractors/neoextractors/__init__.py b/src/spikeinterface/extractors/neoextractors/__init__.py index 0d9da1960a..a6c8f27ac3 100644 --- a/src/spikeinterface/extractors/neoextractors/__init__.py +++ b/src/spikeinterface/extractors/neoextractors/__init__.py @@ -16,6 +16,7 @@ read_neuroscope_sorting, read_neuroscope, ) +from .neuroexplorer import NeuroExplorerRecordingExtractor, read_neuroexplorer from .nix import NixRecordingExtractor, read_nix from .openephys import ( OpenEphysLegacyRecordingExtractor, @@ -53,6 +54,7 @@ SpikeGadgetsRecordingExtractor, SpikeGLXRecordingExtractor, TdtRecordingExtractor, + NeuroExplorerRecordingExtractor, ] neo_sorting_extractors_list = [BlackrockSortingExtractor, MEArecSortingExtractor, NeuralynxSortingExtractor] diff --git a/src/spikeinterface/extractors/neoextractors/neuroexplorer.py b/src/spikeinterface/extractors/neoextractors/neuroexplorer.py new file mode 100644 index 0000000000..e936d91fbf --- /dev/null +++ b/src/spikeinterface/extractors/neoextractors/neuroexplorer.py @@ -0,0 +1,66 @@ +from pathlib import Path + +from spikeinterface.core.core_tools import define_function_from_class + +from .neobaseextractor import NeoBaseRecordingExtractor + + +class NeuroExplorerRecordingExtractor(NeoBaseRecordingExtractor): + """ + Class for reading NEX (NeuroExplorer data format) files. + + Based on :py:class:`neo.rawio.NeuroExplorerRawIO` + + Importantly, at the moment, this recorder only extracts one channel of the recording. + This is because the NeuroExplorerRawIO class does not support multi-channel recordings + as in the NeuroExplorer format they might have different sampling rates. + + Consider exctracting all the channels and then concatenating them with the concatenate_recordings function. + + >>> from spikeinterface.extractors.neoextractors.neuroexplorer import NeuroExplorerRecordingExtractor + >>> from spikeinterface.core import aggregate_channels + >>> + >>> file_path="/home/heberto/spikeinterface_datasets/ephy_testing_data/neuroexplorer/File_neuroexplorer_1.nex" + >>> + >>> streams = NeuroExplorerRecordingExtractor.get_streams(file_path=file_path) + >>> stream_names = streams[0] + >>> + >>> your_signal_stream_names = "Here goes the logic to filter from stream names the ones that you know have the same sampling rate and you want to aggregate" + >>> + >>> recording_list = [NeuroExplorerRecordingExtractor(file_path=file_path, stream_name=stream_name) for stream_name in your_signal_stream_names] + >>> recording = aggregate_channels(recording_list) + + + + Parameters + ---------- + file_path: str + The file path to load the recordings from. + stream_id: str, optional + If there are several streams, specify the stream id you want to load. + For this neo reader streams are defined by their sampling frequency. + stream_name: str, optional + If there are several streams, specify the stream name you want to load. + all_annotations: bool, default: False + Load exhaustively all annotations from neo. + """ + + mode = "file" + NeoRawIOClass = "NeuroExplorerRawIO" + name = "neuroexplorer" + + def __init__(self, file_path, stream_id=None, stream_name=None, all_annotations=False): + neo_kwargs = {"filename": str(file_path)} + NeoBaseRecordingExtractor.__init__( + self, stream_id=stream_id, stream_name=stream_name, all_annotations=all_annotations, **neo_kwargs + ) + self._kwargs.update({"file_path": str(Path(file_path).absolute())}) + self.extra_requirements.append("neo[edf]") + + @classmethod + def map_to_neo_kwargs(cls, file_path): + neo_kwargs = {"filename": str(file_path)} + return neo_kwargs + + +read_neuroexplorer = define_function_from_class(source_class=NeuroExplorerRecordingExtractor, name="read_neuroexplorer") diff --git a/src/spikeinterface/extractors/tests/test_neoextractors.py b/src/spikeinterface/extractors/tests/test_neoextractors.py index 900bdec06e..a62c81fc00 100644 --- a/src/spikeinterface/extractors/tests/test_neoextractors.py +++ b/src/spikeinterface/extractors/tests/test_neoextractors.py @@ -109,6 +109,17 @@ class NeuroScopeRecordingTest(RecordingCommonTestSuite, unittest.TestCase): ] +class NeuroExplorerRecordingTest(RecordingCommonTestSuite, unittest.TestCase): + ExtractorClass = NeuroExplorerRecordingExtractor + downloads = ["neuroexplorer"] + entities = [ + ("neuroexplorer/File_neuroexplorer_1.nex", {"stream_name": "ContChannel01"}), + ("neuroexplorer/File_neuroexplorer_1.nex", {"stream_name": "ContChannel02"}), + ("neuroexplorer/File_neuroexplorer_2.nex", {"stream_name": "ContChannel01"}), + ("neuroexplorer/File_neuroexplorer_2.nex", {"stream_name": "ContChannel02"}), + ] + + class NeuroScopeSortingTest(SortingCommonTestSuite, unittest.TestCase): ExtractorClass = NeuroScopeSortingExtractor downloads = ["neuroscope"] From c974ac034cfdb2b54f76e4ae7910e8f8957e6591 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Mon, 11 Sep 2023 11:06:14 +0200 Subject: [PATCH 2/6] Update src/spikeinterface/extractors/neoextractors/neuroexplorer.py Co-authored-by: Alessio Buccino --- src/spikeinterface/extractors/neoextractors/neuroexplorer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spikeinterface/extractors/neoextractors/neuroexplorer.py b/src/spikeinterface/extractors/neoextractors/neuroexplorer.py index e936d91fbf..0be65dd5cb 100644 --- a/src/spikeinterface/extractors/neoextractors/neuroexplorer.py +++ b/src/spikeinterface/extractors/neoextractors/neuroexplorer.py @@ -15,7 +15,7 @@ class NeuroExplorerRecordingExtractor(NeoBaseRecordingExtractor): This is because the NeuroExplorerRawIO class does not support multi-channel recordings as in the NeuroExplorer format they might have different sampling rates. - Consider exctracting all the channels and then concatenating them with the concatenate_recordings function. + Consider exctracting all the channels and then concatenating them with the aggregate_channels function. >>> from spikeinterface.extractors.neoextractors.neuroexplorer import NeuroExplorerRecordingExtractor >>> from spikeinterface.core import aggregate_channels From e733afe6eb103d954eea1e8992fac02f72bb51ba Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Mon, 11 Sep 2023 11:08:00 +0200 Subject: [PATCH 3/6] Update src/spikeinterface/extractors/neoextractors/neuroexplorer.py --- src/spikeinterface/extractors/neoextractors/neuroexplorer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spikeinterface/extractors/neoextractors/neuroexplorer.py b/src/spikeinterface/extractors/neoextractors/neuroexplorer.py index 0be65dd5cb..b430e45232 100644 --- a/src/spikeinterface/extractors/neoextractors/neuroexplorer.py +++ b/src/spikeinterface/extractors/neoextractors/neuroexplorer.py @@ -20,7 +20,7 @@ class NeuroExplorerRecordingExtractor(NeoBaseRecordingExtractor): >>> from spikeinterface.extractors.neoextractors.neuroexplorer import NeuroExplorerRecordingExtractor >>> from spikeinterface.core import aggregate_channels >>> - >>> file_path="/home/heberto/spikeinterface_datasets/ephy_testing_data/neuroexplorer/File_neuroexplorer_1.nex" + >>> file_path="/the/path/to/your/nex/file.nex" >>> >>> streams = NeuroExplorerRecordingExtractor.get_streams(file_path=file_path) >>> stream_names = streams[0] From 4ea63ef901fb9d05308179b3d465e85dc777ab16 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Tue, 12 Sep 2023 16:54:37 +0200 Subject: [PATCH 4/6] Update src/spikeinterface/extractors/neoextractors/neuroexplorer.py Co-authored-by: Zach McKenzie <92116279+zm711@users.noreply.github.com> --- src/spikeinterface/extractors/neoextractors/neuroexplorer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spikeinterface/extractors/neoextractors/neuroexplorer.py b/src/spikeinterface/extractors/neoextractors/neuroexplorer.py index b430e45232..2c8603cb9c 100644 --- a/src/spikeinterface/extractors/neoextractors/neuroexplorer.py +++ b/src/spikeinterface/extractors/neoextractors/neuroexplorer.py @@ -15,7 +15,7 @@ class NeuroExplorerRecordingExtractor(NeoBaseRecordingExtractor): This is because the NeuroExplorerRawIO class does not support multi-channel recordings as in the NeuroExplorer format they might have different sampling rates. - Consider exctracting all the channels and then concatenating them with the aggregate_channels function. + Consider extracting all the channels and then concatenating them with the aggregate_channels function. >>> from spikeinterface.extractors.neoextractors.neuroexplorer import NeuroExplorerRecordingExtractor >>> from spikeinterface.core import aggregate_channels From 84f7e21d1a48385a0f4c86ee886eea793325bb09 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Tue, 12 Sep 2023 17:51:17 +0200 Subject: [PATCH 5/6] add to the API --- doc/api.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/api.rst b/doc/api.rst index 2e9fc1567a..fdef00c928 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -101,6 +101,8 @@ NEO-based .. autofunction:: read_spikegadgets .. autofunction:: read_spikeglx .. autofunction:: read_tdt + .. autofunction:: read_neuroexplorer + Non-NEO-based ~~~~~~~~~~~~~ From 40d304b26572c26caecefed5084c190d9a76c3ec Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Wed, 13 Sep 2023 12:14:42 +0200 Subject: [PATCH 6/6] aphabetical order in API --- doc/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api.rst b/doc/api.rst index fdef00c928..7a72ead33f 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -91,6 +91,7 @@ NEO-based .. autofunction:: read_mcsraw .. autofunction:: read_neuralynx .. autofunction:: read_neuralynx_sorting + .. autofunction:: read_neuroexplorer .. autofunction:: read_neuroscope .. autofunction:: read_nix .. autofunction:: read_openephys @@ -101,7 +102,6 @@ NEO-based .. autofunction:: read_spikegadgets .. autofunction:: read_spikeglx .. autofunction:: read_tdt - .. autofunction:: read_neuroexplorer Non-NEO-based