Skip to content

Commit 9175fa1

Browse files
authored
Merge branch 'main' into expose_attempts_in_plexon2
2 parents 045874c + 298e57a commit 9175fa1

File tree

74 files changed

+2524
-2473
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+2524
-2473
lines changed

doc/development/development.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ Miscelleaneous Stylistic Conventions
192192
#. Avoid using abbreviations in variable names (e.g. use :code:`recording` instead of :code:`rec`). It is especially important to avoid single letter variables.
193193
#. Use index as singular and indices for plural following the NumPy convention. Avoid idx or indexes. Plus, id and ids are reserved for identifiers (i.e. channel_ids)
194194
#. We use file_path and folder_path (instead of file_name and folder_name) for clarity.
195+
#. For the titles of documentation pages, only capitalize the first letter of the first word and classes or software packages. For example, "How to use a SortingAnalyzer in SpikeInterface".
195196
#. For creating headers to divide sections of code we use the following convention (see issue `#3019 <https://github.com/SpikeInterface/spikeinterface/issues/3019>`_):
196197

197198

doc/how_to/combine_recordings.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Combine Recordings in SpikeInterface
1+
Combine recordings in SpikeInterface
22
====================================
33

44
In this tutorial we will walk through combining multiple recording objects. Sometimes this occurs due to hardware

doc/how_to/load_matlab_data.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Export MATLAB Data to Binary & Load in SpikeInterface
1+
Export MATLAB data to binary & load in SpikeInterface
22
========================================================
33

44
In this tutorial, we will walk through the process of exporting data from MATLAB in a binary format and subsequently loading it using SpikeInterface in Python.

doc/how_to/load_your_data_into_sorting.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
Load Your Own Data into a Sorting
2-
=================================
1+
Load your own data into a Sorting object
2+
========================================
33

44
Why make a :code:`Sorting`?
55

doc/how_to/process_by_channel_group.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Process a Recording by Channel Group
1+
Process a recording by channel group
22
====================================
33

44
In this tutorial, we will walk through how to preprocess and sort a recording

doc/how_to/viewers.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Visualize Data
1+
Visualize data
22
==============
33

44
There are several ways to plot signals (raw, preprocessed) and spikes.

doc/images/overview.png

-227 KB
Loading

doc/modules/benchmark.rst

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
Benchmark module
2+
================
3+
4+
This module contains machinery to compare some sorters against ground truth in many multiple situtation.
5+
6+
7+
..notes::
8+
9+
In 0.102.0 The previous :py:func:`~spikeinterface.comparison.GroundTruthStudy()` has been replaced by
10+
:py:func:`~spikeinterface.benchmark.SorterStudy()`
11+
12+
13+
This module also aims to benchmark sorting components (detection, clustering, motion, template matching) using the
14+
same base class :py:func:`~spikeinterface.benchmark.BenchmarkStudy()` but specialized to a targeted component.
15+
16+
By design, the main class handle the concept of "levels" : this allows to compare several complexities at the same time.
17+
For instance, compare kilosort4 vs kilsort2.5 (level 0) for different noises amplitudes (level 1) combined with
18+
several motion vectors (leevel 2).
19+
20+
**Example: compare many sorters : a ground truth study**
21+
22+
We have a high level class to compare many sorters against ground truth: :py:func:`~spikeinterface.benchmark.SorterStudy()`
23+
24+
25+
A study is a systematic performance comparison of several ground truth recordings with several sorters or several cases
26+
like the different parameter sets.
27+
28+
The study class proposes high-level tool functions to run many ground truth comparisons with many "cases"
29+
on many recordings and then collect and aggregate results in an easy way.
30+
31+
The all mechanism is based on an intrinsic organization into a "study_folder" with several subfolders:
32+
33+
* datasets: contains ground truth datasets
34+
* sorters : contains outputs of sorters
35+
* sortings: contains light copy of all sorting
36+
* metrics: contains metrics
37+
* ...
38+
39+
40+
.. code-block:: python
41+
42+
import matplotlib.pyplot as plt
43+
import seaborn as sns
44+
45+
import spikeinterface.extractors as se
46+
import spikeinterface.widgets as sw
47+
from spikeinterface.benchmark import SorterStudy
48+
49+
50+
# generate 2 simulated datasets (could be also mearec files)
51+
rec0, gt_sorting0 = generate_ground_truth_recording(num_channels=4, durations=[30.], seed=42)
52+
rec1, gt_sorting1 = generate_ground_truth_recording(num_channels=4, durations=[30.], seed=91)
53+
54+
datasets = {
55+
"toy0": (rec0, gt_sorting0),
56+
"toy1": (rec1, gt_sorting1),
57+
}
58+
59+
# define some "cases" here we want to test tridesclous2 on 2 datasets and spykingcircus2 on one dataset
60+
# so it is a two level study (sorter_name, dataset)
61+
# this could be more complicated like (sorter_name, dataset, params)
62+
cases = {
63+
("tdc2", "toy0"): {
64+
"label": "tridesclous2 on tetrode0",
65+
"dataset": "toy0",
66+
"params": {"sorter_name": "tridesclous2"}
67+
},
68+
("tdc2", "toy1"): {
69+
"label": "tridesclous2 on tetrode1",
70+
"dataset": "toy1",
71+
"params": {"sorter_name": "tridesclous2"}
72+
},
73+
("sc", "toy0"): {
74+
"label": "spykingcircus2 on tetrode0",
75+
"dataset": "toy0",
76+
"params": {
77+
"sorter_name": "spykingcircus",
78+
"docker_image": True
79+
},
80+
},
81+
}
82+
# this initilizes a folder
83+
study = SorterStudy.create(study_folder=study_folder, datasets=datasets, cases=cases,
84+
levels=["sorter_name", "dataset"])
85+
86+
87+
# This internally do run_sorter() for all cases in one function
88+
study.run()
89+
90+
# Run the benchmark : this internanly do compare_sorter_to_ground_truth() for all cases
91+
study.compute_results()
92+
93+
# Collect comparisons one by one
94+
for case_key in study.cases:
95+
print('*' * 10)
96+
print(case_key)
97+
# raw counting of tp/fp/...
98+
comp = study.get_result(case_key)["gt_comparison"]
99+
# summary
100+
comp.print_summary()
101+
perf_unit = comp.get_performance(method='by_unit')
102+
perf_avg = comp.get_performance(method='pooled_with_average')
103+
# some plots
104+
m = comp.get_confusion_matrix()
105+
w_comp = sw.plot_agreement_matrix(sorting_comparison=comp)
106+
107+
# Collect synthetic dataframes and display
108+
# As shown previously, the performance is returned as a pandas dataframe.
109+
# The spikeinterface.comparison.get_performance_by_unit() function,
110+
# gathers all the outputs in the study folder and merges them into a single dataframe.
111+
# Same idea for spikeinterface.comparison.get_count_units()
112+
113+
# this is a dataframe
114+
perfs = study.get_performance_by_unit()
115+
116+
# this is a dataframe
117+
unit_counts = study.get_count_units()
118+
119+
# Study also have several plotting methods for plotting the result
120+
study.plot_agreement_matrix()
121+
study.plot_unit_counts()
122+
study.plot_performances(mode="ordered")
123+
study.plot_performances(mode="snr")
124+
125+
126+
127+
128+
Benchmark spike collisions
129+
--------------------------
130+
131+
SpikeInterface also has a specific toolset to benchmark how well sorters are at recovering spikes in "collision".
132+
133+
We have three classes to handle collision-specific comparisons, and also to quantify the effects on correlogram
134+
estimation:
135+
136+
* :py:class:`~spikeinterface.comparison.CollisionGTComparison`
137+
* :py:class:`~spikeinterface.comparison.CorrelogramGTComparison`
138+
139+
For more details, checkout the following paper:
140+
141+
`Samuel Garcia, Alessio P. Buccino and Pierre Yger. "How Do Spike Collisions Affect Spike Sorting Performance?" <https://doi.org/10.1523/ENEURO.0105-22.2022>`_

doc/modules/comparison.rst

Lines changed: 4 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ Comparison module
55
SpikeInterface has a :py:mod:`~spikeinterface.comparison` module, which contains functions and tools to compare
66
spike trains and templates (useful for tracking units over multiple sessions).
77

8+
.. note::
9+
10+
In version 0.102.0 the benchmark part of comparison has moved in the new :py:mod:`~spikeinterface.benchmark`
11+
812
In addition, the :py:mod:`~spikeinterface.comparison` module contains advanced benchmarking tools to evaluate
913
the effects of spike collisions on spike sorting results, and to construct hybrid recordings for comparison.
1014

@@ -242,135 +246,6 @@ An **over-merged** unit has a relatively high agreement (>= 0.2 by default) for
242246
243247
cmp_gt_HS.get_redundant_units(redundant_score=0.2)
244248
245-
246-
**Example: compare many sorters with a Ground Truth Study**
247-
248-
We also have a high level class to compare many sorters against ground truth:
249-
:py:func:`~spikeinterface.comparison.GroundTruthStudy()`
250-
251-
A study is a systematic performance comparison of several ground truth recordings with several sorters or several cases
252-
like the different parameter sets.
253-
254-
The study class proposes high-level tool functions to run many ground truth comparisons with many "cases"
255-
on many recordings and then collect and aggregate results in an easy way.
256-
257-
The all mechanism is based on an intrinsic organization into a "study_folder" with several subfolders:
258-
259-
* datasets: contains ground truth datasets
260-
* sorters : contains outputs of sorters
261-
* sortings: contains light copy of all sorting
262-
* metrics: contains metrics
263-
* ...
264-
265-
266-
.. code-block:: python
267-
268-
import matplotlib.pyplot as plt
269-
import seaborn as sns
270-
271-
import spikeinterface.extractors as se
272-
import spikeinterface.widgets as sw
273-
from spikeinterface.comparison import GroundTruthStudy
274-
275-
276-
# generate 2 simulated datasets (could be also mearec files)
277-
rec0, gt_sorting0 = generate_ground_truth_recording(num_channels=4, durations=[30.], seed=42)
278-
rec1, gt_sorting1 = generate_ground_truth_recording(num_channels=4, durations=[30.], seed=91)
279-
280-
datasets = {
281-
"toy0": (rec0, gt_sorting0),
282-
"toy1": (rec1, gt_sorting1),
283-
}
284-
285-
# define some "cases" here we want to test tridesclous2 on 2 datasets and spykingcircus2 on one dataset
286-
# so it is a two level study (sorter_name, dataset)
287-
# this could be more complicated like (sorter_name, dataset, params)
288-
cases = {
289-
("tdc2", "toy0"): {
290-
"label": "tridesclous2 on tetrode0",
291-
"dataset": "toy0",
292-
"run_sorter_params": {
293-
"sorter_name": "tridesclous2",
294-
},
295-
},
296-
("tdc2", "toy1"): {
297-
"label": "tridesclous2 on tetrode1",
298-
"dataset": "toy1",
299-
"run_sorter_params": {
300-
"sorter_name": "tridesclous2",
301-
},
302-
},
303-
304-
("sc", "toy0"): {
305-
"label": "spykingcircus2 on tetrode0",
306-
"dataset": "toy0",
307-
"run_sorter_params": {
308-
"sorter_name": "spykingcircus",
309-
"docker_image": True
310-
},
311-
},
312-
}
313-
# this initilizes a folder
314-
study = GroundTruthStudy.create(study_folder=study_folder, datasets=datasets, cases=cases,
315-
levels=["sorter_name", "dataset"])
316-
317-
318-
# all cases in one function
319-
study.run_sorters()
320-
321-
# Collect comparisons
322-
#
323-
# You can collect in one shot all results and run the
324-
# GroundTruthComparison on it.
325-
# So you can have fine access to all individual results.
326-
#
327-
# Note: use exhaustive_gt=True when you know exactly how many
328-
# units in the ground truth (for synthetic datasets)
329-
330-
# run all comparisons and loop over the results
331-
study.run_comparisons(exhaustive_gt=True)
332-
for key, comp in study.comparisons.items():
333-
print('*' * 10)
334-
print(key)
335-
# raw counting of tp/fp/...
336-
print(comp.count_score)
337-
# summary
338-
comp.print_summary()
339-
perf_unit = comp.get_performance(method='by_unit')
340-
perf_avg = comp.get_performance(method='pooled_with_average')
341-
# some plots
342-
m = comp.get_confusion_matrix()
343-
w_comp = sw.plot_agreement_matrix(sorting_comparison=comp)
344-
345-
# Collect synthetic dataframes and display
346-
# As shown previously, the performance is returned as a pandas dataframe.
347-
# The spikeinterface.comparison.get_performance_by_unit() function,
348-
# gathers all the outputs in the study folder and merges them into a single dataframe.
349-
# Same idea for spikeinterface.comparison.get_count_units()
350-
351-
# this is a dataframe
352-
perfs = study.get_performance_by_unit()
353-
354-
# this is a dataframe
355-
unit_counts = study.get_count_units()
356-
357-
# we can also access run times
358-
run_times = study.get_run_times()
359-
print(run_times)
360-
361-
# Easy plotting with seaborn
362-
fig1, ax1 = plt.subplots()
363-
sns.barplot(data=run_times, x='rec_name', y='run_time', hue='sorter_name', ax=ax1)
364-
ax1.set_title('Run times')
365-
366-
##############################################################################
367-
368-
fig2, ax2 = plt.subplots()
369-
sns.swarmplot(data=perfs, x='sorter_name', y='recall', hue='rec_name', ax=ax2)
370-
ax2.set_title('Recall')
371-
ax2.set_ylim(-0.1, 1.1)
372-
373-
374249
.. _symmetric:
375250

376251
2. Compare the output of two spike sorters (symmetric comparison)
@@ -537,35 +412,3 @@ sorting analyzers from day 1 (:code:`analyzer_day1`) to day 5 (:code:`analyzer_d
537412
# match all
538413
m_tcmp = sc.compare_multiple_templates(waveform_list=analyzer_list,
539414
name_list=["D1", "D2", "D3", "D4", "D5"])
540-
541-
542-
543-
Benchmark spike collisions
544-
--------------------------
545-
546-
SpikeInterface also has a specific toolset to benchmark how well sorters are at recovering spikes in "collision".
547-
548-
We have three classes to handle collision-specific comparisons, and also to quantify the effects on correlogram
549-
estimation:
550-
551-
* :py:class:`~spikeinterface.comparison.CollisionGTComparison`
552-
* :py:class:`~spikeinterface.comparison.CorrelogramGTComparison`
553-
* :py:class:`~spikeinterface.comparison.CollisionGTStudy`
554-
* :py:class:`~spikeinterface.comparison.CorrelogramGTStudy`
555-
556-
For more details, checkout the following paper:
557-
558-
`Samuel Garcia, Alessio P. Buccino and Pierre Yger. "How Do Spike Collisions Affect Spike Sorting Performance?" <https://doi.org/10.1523/ENEURO.0105-22.2022>`_
559-
560-
561-
Hybrid recording
562-
----------------
563-
564-
To benchmark spike sorting results, we need ground-truth spiking activity.
565-
This can be generated with artificial simulations, e.g., using `MEArec <https://mearec.readthedocs.io/>`_, or
566-
alternatively by generating so-called "hybrid" recordings.
567-
568-
The :py:mod:`~spikeinterface.comparison` module includes functions to generate such "hybrid" recordings:
569-
570-
* :py:func:`~spikeinterface.comparison.create_hybrid_units_recording`: add new units to an existing recording
571-
* :py:func:`~spikeinterface.comparison.create_hybrid_spikes_recording`: add new spikes to existing units in a recording

0 commit comments

Comments
 (0)