Skip to content
Merged
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
1 change: 1 addition & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ NEO-based
.. autofunction:: read_neuralynx
.. autofunction:: read_neuralynx_sorting
.. autofunction:: read_neuroexplorer
.. autofunction:: read_neuroexplorer_sorting
.. autofunction:: read_neuroscope
.. autofunction:: read_nix
.. autofunction:: read_openephys
Expand Down
1 change: 1 addition & 0 deletions doc/modules/extractors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ For sorted data formats, we currently support:
* **Neurodata Without Borders** :py:func:`~spikeinterface.extractors.read_nwb_sorting()`
* **Neuroscope** :py:func:`~spikeinterface.extractors.read_neuroscope_sorting()`
* **Neuralynx spikes** :py:func:`~spikeinterface.extractors.read_neuralynx_sorting()`
* **NeuroExplorer spikes** :py:func:`~spikeinterface.extractors.read_neuroexplorer_sorting()`
* **NPZ (created by SpikeInterface)** :py:func:`~spikeinterface.core.read_npz_sorting()`
* **Plexon spikes** :py:func:`~spikeinterface.extractors.read_plexon_sorting()`
* **Plexon 2 spikes** :py:func:`~spikeinterface.extractors.read_plexon2_sorting()`
Expand Down
10 changes: 9 additions & 1 deletion src/spikeinterface/extractors/neoextractors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
read_neuroscope_sorting,
read_neuroscope,
)
from .neuroexplorer import NeuroExplorerRecordingExtractor, read_neuroexplorer
from .neuroexplorer import (
NeuroExplorerRecordingExtractor,
NeuroExplorerSortingExtractor,
read_neuroexplorer,
read_neuroexplorer_sorting,
)
from .nix import NixRecordingExtractor, read_nix
from .openephys import (
OpenEphysLegacyRecordingExtractor,
Expand Down Expand Up @@ -79,6 +84,9 @@
PlexonSortingExtractor: dict(wrapper_string="read_plexon_sorting", wrapper_class=read_plexon_sorting),
Plexon2SortingExtractor: dict(wrapper_string="read_plexon2_sorting", wrapper_class=read_plexon2_sorting),
NeuroScopeSortingExtractor: dict(wrapper_string="read_neuroscope_sorting", wrapper_class=read_neuroscope_sorting),
NeuroExplorerSortingExtractor: dict(
wrapper_string="read_neuroexplorer_sorting", wrapper_class=read_neuroexplorer_sorting
),
}

neo_event_extractors_dict = {
Expand Down
68 changes: 67 additions & 1 deletion src/spikeinterface/extractors/neoextractors/neuroexplorer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from pathlib import Path

from spikeinterface.core import BaseSorting
from spikeinterface.core.core_tools import define_function_from_class

from .neobaseextractor import NeoBaseRecordingExtractor
from .neobaseextractor import (
NeoBaseRecordingExtractor,
NeoBaseSortingExtractor,
NeoSortingSegment,
_NeoBaseExtractor,
)


class NeuroExplorerRecordingExtractor(NeoBaseRecordingExtractor):
Expand Down Expand Up @@ -72,3 +78,63 @@ def map_to_neo_kwargs(cls, file_path):


read_neuroexplorer = define_function_from_class(source_class=NeuroExplorerRecordingExtractor, name="read_neuroexplorer")


class NeuroExplorerSortingExtractor(NeoBaseSortingExtractor):
"""
Class for reading the sorted units of NEX (NeuroExplorer data format) files.

Based on :py:class:`neo.rawio.NeuroExplorerRawIO`

Units are the neuron variables of the file and the unit ids are their names. Waveform variables
(for example the ``_wf`` and ``_template`` companions of each unit in Plexon exports) repeat
the spike times of a neuron variable or hold a mean waveform, so they are not units. They
remain available through ``neo_reader``.

Parameters
----------
file_path : str | Path
The file path to load the sorting from.
"""

NeoRawIOClass = "NeuroExplorerRawIO"
neo_returns_frames = True

def __init__(self, file_path):
neo_kwargs = self.map_to_neo_kwargs(file_path)
_NeoBaseExtractor.__init__(self, block_index=None, **neo_kwargs)

# Only neuron variables are units: they are the spike channels without waveforms
spike_channels = self.neo_reader.header["spike_channels"]
unit_ids = spike_channels["name"][spike_channels["wf_sampling_rate"] == 0]
# Spike timestamps are ticks of the file's global clock
sampling_frequency = self.neo_reader.global_header["freq"]
BaseSorting.__init__(self, sampling_frequency, unit_ids)

sorting_segment = NeuroExplorerSortingSegment(
neo_reader=self.neo_reader,
block_index=self.block_index,
segment_index=0,
t_start=None,
sampling_frequency=sampling_frequency,
neo_returns_frames=self.neo_returns_frames,
)
self.add_sorting_segment(sorting_segment)
self._kwargs = {"file_path": str(Path(file_path).absolute())}

@classmethod
def map_to_neo_kwargs(cls, file_path):
neo_kwargs = {"filename": str(file_path)}
return neo_kwargs


class NeuroExplorerSortingSegment(NeoSortingSegment):
def map_from_unit_id_to_spike_channel_index(self, unit_id):
# Units are a subset of the spike channels, so the position of a unit id is not its spike channel index
spike_channel_names = list(self.neo_reader.header["spike_channels"]["name"])
return spike_channel_names.index(unit_id)


read_neuroexplorer_sorting = define_function_from_class(
source_class=NeuroExplorerSortingExtractor, name="read_neuroexplorer_sorting"
)
10 changes: 10 additions & 0 deletions src/spikeinterface/extractors/tests/test_neoextractors.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
IntanRecordingExtractor,
NeuroScopeRecordingExtractor,
NeuroExplorerRecordingExtractor,
NeuroExplorerSortingExtractor,
NeuroScopeSortingExtractor,
NeuroNexusRecordingExtractor,
PlexonRecordingExtractor,
Expand Down Expand Up @@ -270,6 +271,15 @@ class NeuroExplorerRecordingTest(RecordingCommonTestSuite, unittest.TestCase):
]


class NeuroExplorerSortingTest(SortingCommonTestSuite, unittest.TestCase):
ExtractorClass = NeuroExplorerSortingExtractor
downloads = ["neuroexplorer"]
entities = [
"neuroexplorer/File_neuroexplorer_1.nex",
"neuroexplorer/File_neuroexplorer_2.nex",
]


class NeuroScopeSortingTest(SortingCommonTestSuite, unittest.TestCase):
ExtractorClass = NeuroScopeSortingExtractor
downloads = ["neuroscope"]
Expand Down
Loading