Skip to content

Alter how quality metrics check extension dependencies #4080

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

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 5 additions & 2 deletions doc/modules/postprocessing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ and doesn't save anything. If we wanted to save the extension we should have sta
Available postprocessing extensions
-----------------------------------

.. _postprocessing_noise_levels:

noise_levels
^^^^^^^^^^^^

Expand All @@ -192,7 +194,7 @@ As an extension, this expects the :code:`Recording` as input and the computed va




.. _postprocessing_principal_components:

principal_components
^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -232,7 +234,7 @@ and is not well suited for high-density probes.

For more information, see :py:func:`~spikeinterface.postprocessing.compute_template_similarity`


.. _postprocessing_spike_amplitudes:

spike_amplitudes
^^^^^^^^^^^^^^^^
Expand All @@ -249,6 +251,7 @@ each spike.

For more information, see :py:func:`~spikeinterface.postprocessing.compute_spike_amplitudes`

.. _postprocessing_spike_locations:

spike_locations
^^^^^^^^^^^^^^^
Expand Down
117 changes: 87 additions & 30 deletions doc/modules/qualitymetrics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,99 @@ Completeness metrics (or 'false negative'/'type II' metrics) aim to identify whe
Examples include: presence ratio, amplitude cutoff, NN-miss rate.
Drift metrics aim to identify changes in waveforms which occur when spike sorters fail to successfully track neurons in the case of electrode drift.

Some metrics make use of principal component analysis (PCA) to reduce the dimensionality of computations.
The quality metrics are saved as an extension of a :doc:`SortingAnalyzer <postprocessing>`. Some metrics can only be computed if certain extensions have been computed first. For example the drift metrics can only be computed the spike locations extension has been computed. By default, as many metrics as possible are computed. Which ones are computed depends on which other extensions have
been computed.

In detail, the default metrics are (click on each metric to find out more about them!):

- :doc:`qualitymetrics/firing_rate`
- :doc:`qualitymetrics/presence_ratio`
- :doc:`qualitymetrics/isi_violations`
- :doc:`qualitymetrics/sliding_rp_violations`
- :doc:`qualitymetrics/synchrony`
- :doc:`qualitymetrics/firing_range`

If :ref:`postprocessing_spike_locations` are computed, add:

- :doc:`qualitymetrics/drift`

If :ref:`postprocessing_spike_amplitudes` and ``templates`` are computed, add:

- :doc:`qualitymetrics/amplitude_cutoff`
- :doc:`qualitymetrics/amplitude_median`
- :doc:`qualitymetrics/amplitude_cv`
- :doc:`qualitymetrics/noise_cutoff`

If :ref:`postprocessing_noise_levels` and ``templates`` are computed, add:

- :doc:`qualitymetrics/snr`

If the recording, :ref:`postprocessing_spike_amplitudes`and ``templates`` are available, add:

- :doc:`qualitymetrics/sd_ratio`

If :ref:`postprocessing_principal_components` are computed, add:

- :doc:`qualitymetrics/isolation_distance`
- :doc:`qualitymetrics/l_ratio`
- :doc:`qualitymetrics/d_prime`
- :doc:`qualitymetrics/silhouette_score`
- :doc:`qualitymetrics/nearest_neighbor` (note: excluding the ``nn_noise_overlap`` metric)

You can compute the default metrics using the following code snippet:

.. code-block:: python

# load or create a sorting analyzer
sorting_analyzer = si.load_sorting_analyzer(folder='my_sorting_analyzer')

# compute the metrics
sorting_analyzer.compute("quality_metrics")

# get the metrics in the form as a pandas DataFrame
quality_metrics = sorting_analyzer.get_extension("quality_metrics").get_data()

# print the metrics that have been computed
print(quality_metrics.columns)

Some metrics are very slow to compute when the number of units it large. So by default, the following metrics are not computed:

- :doc:`qualitymetrics/isolation_distance`
- The ``nn_noise_overlap`` from :doc:`qualitymetrics/nearest_neighbor`

Some metrics make use of :ref:`principal component analysis <postprocessing_principal_components>` (PCA) to reduce the dimensionality of computations.
Various approaches to computing the principal components are possible, and choice should be carefully considered in relation to the recording equipment used.
The following metrics make use of PCA: isolation distance, L-ratio, D-prime, Silhouette score and NN-metrics.
By contrast, the following metrics are based on spike times only: firing rate, ISI violations, presence ratio.
And amplitude cutoff and SNR are based on spike times as well as waveforms.

For more details about each metric and it's availability and use within SpikeInterface, see the individual pages for each metrics.
If you only want to compute a subset of metrics, you can use convenience functions to compute each one,

.. code-block:: python

from spikeinterface.quality_metrics import compute_isi_violations
compute_isi_violations(sorting_analyzer, isi_threshold_ms=3.0)

or use the ``compute`` method

.. code-block:: python

sorting_analyzer.compute(
"quality_metrics",
metric_names = ["isi_violation", "snr"],
extension_params = {
"isi_violation": {"isi_threshold_ms": 3.0},
}
)

Note that if you request a specific metric using ``metric_names`` and you do not have the required extension computed, this will error.

For more information about quality metrics, check out this excellent
`documentation <https://allensdk.readthedocs.io/en/latest/_static/examples/nb/ecephys_quality_metrics.html>`_
from the Allen Institute.


.. toctree::
:maxdepth: 1
:glob:
:hidden:

qualitymetrics/amplitude_cutoff
qualitymetrics/amplitude_cv
Expand All @@ -42,28 +124,3 @@ For more details about each metric and it's availability and use within SpikeInt
qualitymetrics/sliding_rp_violations
qualitymetrics/snr
qualitymetrics/synchrony


This code snippet shows how to compute quality metrics (with or without principal components) in SpikeInterface:

.. code-block:: python

sorting_analyzer = si.load_sorting_analyzer(folder='waveforms') # start from a sorting_analyzer

# without PC (depends on "waveforms", "templates", and "noise_levels")
qm_ext = sorting_analyzer.compute(input="quality_metrics", metric_names=['snr'], skip_pc_metrics=True)
metrics = qm_ext.get_data()
assert 'snr' in metrics.columns

# with PCs (depends on "pca" in addition to the above metrics)

qm_ext = sorting_analyzer.compute(input={"principal_components": dict(n_components=5, mode="by_channel_local"),
"quality_metrics": dict(skip_pc_metrics=False)})
metrics = qm_ext.get_data()
assert 'isolation_distance' in metrics.columns



For more information about quality metrics, check out this excellent
`documentation <https://allensdk.readthedocs.io/en/latest/_static/examples/nb/ecephys_quality_metrics.html>`_
from the Allen Institute.
10 changes: 5 additions & 5 deletions src/spikeinterface/core/tests/test_sortinganalyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,14 +618,14 @@ def test_extensions_sorting():
assert list(sorted_extensions_2.keys()) == list(extensions_in_order.keys())

# doing two movements
extensions_qm_left = {"quality_metrics": {}, "waveforms": {}, "templates": {}}
extensions_qm_correct = {"waveforms": {}, "templates": {}, "quality_metrics": {}}
extensions_qm_left = {"template_metrics": {}, "waveforms": {}, "templates": {}}
extensions_qm_correct = {"waveforms": {}, "templates": {}, "template_metrics": {}}
sorted_extensions_3 = _sort_extensions_by_dependency(extensions_qm_left)
assert list(sorted_extensions_3.keys()) == list(extensions_qm_correct.keys())

# should move parent (waveforms) left of child (quality_metrics), and move grandparent (random_spikes) left of parent
extensions_qm_left = {"quality_metrics": {}, "waveforms": {}, "templates": {}, "random_spikes": {}}
extensions_qm_correct = {"random_spikes": {}, "waveforms": {}, "templates": {}, "quality_metrics": {}}
# should move parent (waveforms) left of child (template_metrics), and move grandparent (random_spikes) left of parent
extensions_qm_left = {"template_metrics": {}, "waveforms": {}, "templates": {}, "random_spikes": {}}
extensions_qm_correct = {"random_spikes": {}, "waveforms": {}, "templates": {}, "template_metrics": {}}
sorted_extensions_4 = _sort_extensions_by_dependency(extensions_qm_left)
assert list(sorted_extensions_4.keys()) == list(extensions_qm_correct.keys())

Expand Down
Loading
Loading