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

Switch exporters docs to SortingAnalyzer #2762

Merged
merged 2 commits into from
Apr 29, 2024
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
30 changes: 17 additions & 13 deletions doc/modules/exporters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,31 @@ The :py:func:`~spikeinterface.exporters.export_to_phy` function allows you to us
results.

**Note** : :py:func:`~spikeinterface.exporters.export_to_phy` speed and the size of the folder will highly depend
on the sparsity of the :code:`WaveformExtractor` itself or the external specified sparsity.
on the sparsity of the :code:`SortingAnalyzer` itself or the external specified sparsity.
The Phy viewer enables one to explore PCA projections, spike amplitudes, waveforms and quality of spike sorting results.
So if these pieces of information have already been computed as extensions (see :ref:`waveform_extensions`),
So if these pieces of information have already been computed as extensions (see :ref:`analyzer_extensions`),
then exporting to Phy should be fast (and the user has better control of the parameters for the extensions).
If not pre-computed, then the required extensions (e.g., :code:`spike_amplitudes`, :code:`principal_components`)
can be computed directly at export time.

The input of the :py:func:`~spikeinterface.exporters.export_to_phy` is a :code:`WaveformExtractor` object.
The input of the :py:func:`~spikeinterface.exporters.export_to_phy` is a :code:`SortingAnalyzer` object.

.. code-block:: python

import spikeinterface as si # core module only
from spikeinterface.postprocessing import compute_spike_amplitudes, compute_principal_components
from spikeinterface.exporters import export_to_phy

# the waveforms are sparse so it is faster to export to phy
we = extract_waveforms(recording=recording, sorting=sorting, folder='waveforms')
sorting_analyzer = si.create_sorting_analyzer(sorting=sorting, recording=recording)

# some computations are done before to control all options
_ = compute_spike_amplitudes(waveform_extractor=we)
_ = compute_principal_components(waveform_extractor=we, n_components=3, mode='by_channel_global')
sorting_analyzer.compute(['random_spikes', 'waveforms', 'templates', 'noise_levels'])
_ = sorting_analyzer.compute('spike_amplitudes')
_ = sorting_analyzer.compute('principal_components', n_components = 5, mode="by_channel_local")

# the export process is fast because everything is pre-computed
export_to_phy(waveform_extractor=we, output_folder='path/to/phy_folder')
export_to_phy(sorting_analyzer=sorting_analyzer, output_folder='path/to/phy_folder')



Expand All @@ -54,7 +56,7 @@ depth VS amplitude) as well as unit-specific reports, that include waveforms, te
ISI distributions, and more.

**Note** : similarly to :py:func:`~spikeinterface.exporters.export_to_phy` the
:py:func:`~spikeinterface.exporters.export_report` depends on the sparsity of the :code:`WaveformExtractor` itself and
:py:func:`~spikeinterface.exporters.export_report` depends on the sparsity of the :code:`SortingAnalyzer` itself and
on which extensions have been computed. For example, :code:`spike_amplitudes` and :code:`correlograms` related plots
will be automatically included in the report if the associated extensions are computed in advance.
The function can perform these computations as well, but it is a better practice to compute everything that's needed
Expand All @@ -65,18 +67,20 @@ with many units!

.. code-block:: python

import spikeinterface as si # core module only
from spikeinterface.postprocessing import compute_spike_amplitudes, compute_correlograms
from spikeinterface.qualitymetrics import compute_quality_metrics
from spikeinterface.exporters import export_report


# the waveforms are sparse for more interpretable figures
we = extract_waveforms(recording=recording, sorting=sorting, folder='path/to/wf',)
sorting_analyzer = si.create_sorting_analyzer(sorting=sorting, recording=recording,)

# some computations are done before to control all options
_ = compute_spike_amplitudes(waveform_extractor=we)
- = compute_correlograms(waveform_extractor=we)
_ = compute_quality_metrics(waveform_extractor=we, metric_names=['snr', 'isi_violation', 'presence_ratio'])
sorting_analyzer.compute(['random_spikes', 'waveforms', 'templates', 'noise_levels'])
sorting_analyzer.compute(['spike_amplitudes', 'correlograms', 'template_similarity', 'quality_metrics'],
extension_params=dict(quality_metrics=dict(metric_names=['snr', 'isi_violation', 'presence_ratio']))
)

# the export process
export_report(waveform_extractor=we, output_folder='path/to/spikeinterface-report-folder')
export_report(sorting_analyzer=sorting_analyzer, output_folder='path/to/spikeinterface-report-folder')
10 changes: 5 additions & 5 deletions src/spikeinterface/exporters/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def export_report(
else:
spike_amplitudes = None
print(
"export_report(): spike_amplitudes will not be exported. Use compute_spike_amplitudes() if you want to include them."
"export_report(): spike_amplitudes will not be exported. Use sorting_analyzer.compute('spike_amplitudes') if you want to include them."
)

# load or compute quality_metrics
Expand All @@ -72,7 +72,7 @@ def export_report(
else:
metrics = None
print(
"export_report(): quality metrics will not be exported. Use compute_quality_metrics() if you want to include them."
"export_report(): quality metrics will not be exported. Use sorting_analyzer.compute('quality_metrics') if you want to include them."
)

# load or compute correlograms
Expand All @@ -83,7 +83,7 @@ def export_report(
else:
correlograms = None
print(
"export_report(): correlograms will not be exported. Use compute_correlograms() if you want to include them."
"export_report(): correlograms will not be exported. Use sorting_anlyzer.compute('correlograms') if you want to include them."
)

# pre-compute unit locations if not done
Expand All @@ -102,9 +102,9 @@ def export_report(
units = pd.DataFrame(index=unit_ids) #  , columns=['max_on_channel_id', 'amplitude'])
units.index.name = "unit_id"
units["max_on_channel_id"] = pd.Series(
get_template_extremum_channel(sorting_analyzer, peak_sign="neg", outputs="id")
get_template_extremum_channel(sorting_analyzer, peak_sign=peak_sign, outputs="id")
)
units["amplitude"] = pd.Series(get_template_extremum_amplitude(sorting_analyzer, peak_sign="neg"))
units["amplitude"] = pd.Series(get_template_extremum_amplitude(sorting_analyzer, peak_sign=peak_sign))
units.to_csv(output_folder / "unit list.csv", sep="\t")

unit_colors = sw.get_unit_colors(sorting)
Expand Down
4 changes: 2 additions & 2 deletions src/spikeinterface/exporters/to_phy.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def export_to_phy(
rec_path = "None"
else: # don't save recording.dat
if copy_binary:
warnings.warn("Recording will not be copied since waveform extractor is recordingless.")
warnings.warn("Recording will not be copied since sorting_analyzer is recordingless.")
rec_path = "None"

dtype_str = np.dtype(dtype).name
Expand Down Expand Up @@ -181,7 +181,7 @@ def export_to_phy(
# export templates/templates_ind/similar_templates
# shape (num_units, num_samples, max_num_channels)
templates_ext = sorting_analyzer.get_extension("templates")
assert templates_ext is not None, "export_to_phy need SortingAnalyzer with extension 'templates'"
assert templates_ext is not None, "export_to_phy requires a SortingAnalyzer with the extension 'templates'"
max_num_channels = max(len(chan_inds) for chan_inds in sparse_dict.values())
dense_templates = templates_ext.get_templates(unit_ids=unit_ids, operator=template_mode)
num_samples = dense_templates.shape[1]
Expand Down
Loading