Skip to content

Commit 506ef5f

Browse files
authored
Merge branch 'main' into isosplit
2 parents 58222e3 + b771821 commit 506ef5f

29 files changed

Lines changed: 544 additions & 366 deletions

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v5.0.0
3+
rev: v6.0.0
44
hooks:
55
- id: check-yaml
66
- id: end-of-file-fixer

doc/how_to/customize_a_plot.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _customize-a-plot:
2+
13
Customize a plot
24
================
35

doc/how_to/handle_drift.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
%load_ext autoreload
55
%autoreload 2
66
7+
8+
.. _handle-drift-in-your-recording:
9+
710
Handle motion/drift in your recording
811
=====================================
912

doc/how_to/physical_units.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
.. _physical_units:
23

34
Work with physical units in SpikeInterface recordings

doc/modules/benchmark.rst

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
Benchmark module
22
================
33

4-
This module contains machinery to compare some sorters against ground truth in many multiple situtation.
5-
4+
This module contains machinery to compare some sorters against ground truth in multiple situations.
65

76
..notes::
87

@@ -13,22 +12,21 @@ This module contains machinery to compare some sorters against ground truth in m
1312
This module also aims to benchmark sorting components (detection, clustering, motion, template matching) using the
1413
same base class :py:func:`~spikeinterface.benchmark.BenchmarkStudy()` but specialized to a targeted component.
1514

16-
By design, the main class handle the concept of "levels" : this allows to compare several complexities at the same time.
15+
By design, the main class handles the concept of "levels" : this allows you to compare several complexities at the same time.
1716
For instance, compare kilosort4 vs kilsort2.5 (level 0) for different noises amplitudes (level 1) combined with
18-
several motion vectors (leevel 2).
17+
several motion vectors (level 2).
1918

2019
**Example: compare many sorters : a ground truth study**
2120

2221
We have a high level class to compare many sorters against ground truth: :py:func:`~spikeinterface.benchmark.SorterStudy()`
2322

24-
25-
A study is a systematic performance comparison of several ground truth recordings with several sorters or several cases
23+
A study is a systematic performance comparison of several ground truth recordings with several sorters or several cases,
2624
like the different parameter sets.
2725

2826
The study class proposes high-level tool functions to run many ground truth comparisons with many "cases"
2927
on many recordings and then collect and aggregate results in an easy way.
3028

31-
The all mechanism is based on an intrinsic organization into a "study_folder" with several subfolders:
29+
The mechanism is based on an intrinsic organization into a "study_folder" with several subfolders:
3230

3331
* datasets: contains ground truth datasets
3432
* sorters : contains outputs of sorters
@@ -39,24 +37,20 @@ The all mechanism is based on an intrinsic organization into a "study_folder" wi
3937

4038
.. code-block:: python
4139
42-
import matplotlib.pyplot as plt
43-
import seaborn as sns
44-
45-
import spikeinterface.extractors as se
40+
import spikeinterface as si
4641
import spikeinterface.widgets as sw
4742
from spikeinterface.benchmark import SorterStudy
4843
49-
5044
# 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)
45+
rec0, gt_sorting0 = si.generate_ground_truth_recording(num_channels=4, durations=[30.], seed=42)
46+
rec1, gt_sorting1 = si.generate_ground_truth_recording(num_channels=4, durations=[30.], seed=91)
5347
5448
datasets = {
5549
"toy0": (rec0, gt_sorting0),
5650
"toy1": (rec1, gt_sorting1),
5751
}
5852
59-
# define some "cases" here we want to test tridesclous2 on 2 datasets and spykingcircus2 on one dataset
53+
# define some "cases". Here we want to test tridesclous2 on 2 datasets and spykingcircus2 on one dataset
6054
# so it is a two level study (sorter_name, dataset)
6155
# this could be more complicated like (sorter_name, dataset, params)
6256
cases = {
@@ -74,20 +68,21 @@ The all mechanism is based on an intrinsic organization into a "study_folder" wi
7468
"label": "spykingcircus2 on tetrode0",
7569
"dataset": "toy0",
7670
"params": {
77-
"sorter_name": "spykingcircus",
71+
"sorter_name": "spykingcircus2",
7872
"docker_image": True
7973
},
8074
},
8175
}
82-
# this initilizes a folder
76+
# this initializes a folder
77+
study_folder = "my_study_folder"
8378
study = SorterStudy.create(study_folder=study_folder, datasets=datasets, cases=cases,
8479
levels=["sorter_name", "dataset"])
8580
8681
87-
# This internally do run_sorter() for all cases in one function
82+
# This internally does run_sorter() for all cases in one function
8883
study.run()
8984
90-
# Run the benchmark : this internanly do compare_sorter_to_ground_truth() for all cases
85+
# Run the benchmark : this internally does compare_sorter_to_ground_truth() for all cases
9186
study.compute_results()
9287
9388
# Collect comparisons one by one
@@ -104,9 +99,9 @@ The all mechanism is based on an intrinsic organization into a "study_folder" wi
10499
m = comp.get_confusion_matrix()
105100
w_comp = sw.plot_agreement_matrix(sorting_comparison=comp)
106101
107-
# Collect synthetic dataframes and display
102+
# Collect synthetic dataframes and display.
108103
# As shown previously, the performance is returned as a pandas dataframe.
109-
# The spikeinterface.comparison.get_performance_by_unit() function,
104+
# The spikeinterface.comparison.get_performance_by_unit() function
110105
# gathers all the outputs in the study folder and merges them into a single dataframe.
111106
# Same idea for spikeinterface.comparison.get_count_units()
112107
@@ -116,13 +111,10 @@ The all mechanism is based on an intrinsic organization into a "study_folder" wi
116111
# this is a dataframe
117112
unit_counts = study.get_count_units()
118113
119-
# Study also have several plotting methods for plotting the result
114+
# Study also has several plotting methods for plotting the result
120115
study.plot_agreement_matrix()
121116
study.plot_unit_counts()
122117
study.plot_performances(mode="ordered")
123-
study.plot_performances(mode="snr")
124-
125-
126118
127119
128120
Benchmark spike collisions

doc/modules/comparison.rst

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
Comparison module
22
=================
33

4-
54
SpikeInterface has a :py:mod:`~spikeinterface.comparison` module, which contains functions and tools to compare
65
spike trains and templates (useful for tracking units over multiple sessions).
76

@@ -132,7 +131,7 @@ Given:
132131
4. **Compute performances**
133132

134133
With the list of matched units we can compute performance metrics.
135-
Given : **tp** the number of true positive events, **fp** number of false
134+
Given: **tp** the number of true positive events, **fp** number of false
136135
positive events, **fn** the number of false negative events, **num_gt** the number
137136
of events of the matched tested units, the following metrics are computed for each GT unit:
138137

@@ -142,7 +141,7 @@ Given:
142141
* false_discovery_rate = fp / (tp + fp)
143142
* miss_rate = fn / num_gt
144143

145-
The overall performances can be visualised with the **confusion matrix**, where
144+
The overall performances can be visualized with the **confusion matrix**, where
146145
the last column contains the **FN** counts and the last row contains the **FP** counts.
147146

148147
.. image:: ../images/spikecomparison_confusion.png
@@ -215,15 +214,16 @@ An **over-merged** unit has a relatively high agreement (>= 0.2 by default) for
215214

216215
.. code-block:: python
217216
218-
local_path = download_dataset(remote_path='mearec/mearec_test_10s.h5')
219-
recording, sorting_true = read_mearec(local_path)
217+
from spikeinterface.widgets import plot_agreement_matrix, plot_confusion_matrix
218+
from spikeinterface.generation import generate_ground_truth_recording
219+
from spikeinterface.sorters import run_sorter
220220
221+
recording, sorting_true = generate_ground_truth_recording()
221222
222223
# run a sorter and compare to ground truth
223-
sorting_HS = run_sorter(sorter_name='herdingspike', recording=recording)
224+
sorting_HS = run_sorter(sorter_name='herdingspikes', recording=recording)
224225
cmp_gt_HS = sc.compare_sorter_to_ground_truth(sorting_true, sorting_HS, exhaustive_gt=True)
225226
226-
227227
# To have an overview of the match we can use the ordered agreement matrix
228228
plot_agreement_matrix(cmp_gt_HS, ordered=True)
229229
@@ -232,7 +232,6 @@ An **over-merged** unit has a relatively high agreement (>= 0.2 by default) for
232232
#
233233
perf = cmp_gt_HS.get_performance()
234234
235-
236235
# The confusion matrix is also a good summary of the score as it has
237236
# the same shape as an agreement matrix, but it contains an extra column for FN
238237
# and an extra row for FP
@@ -271,20 +270,20 @@ The :py:func:`~spikeinterface.comparison.compare_two_sorters()` returns the comp
271270
.. code-block:: python
272271
273272
import spikeinterface as si
274-
import spikeinterface.extractors as se
275273
import spikeinterface.sorters as ss
276-
import spikeinterface.comparisons as sc
277-
import spikinterface.widgets as sw
274+
import spikeinterface.comparison as scmp
275+
import spikeinterface.widgets as sw
278276
279277
# First, let's generate a simulated dataset
280278
recording, sorting = si.generate_ground_truth_recording()
279+
281280
# Then run two spike sorters and compare their outputs.
282281
sorting_HS = ss.run_sorter(sorter_name='herdingspikes', recording=recording)
283282
sorting_TDC = ss.run_sorter(sorter_name='tridesclous', recording=recording)
284283
285284
# Run the comparison
286285
# Let's see how to inspect and access this matching.
287-
cmp_HS_TDC = sc.compare_two_sorters(
286+
cmp_HS_TDC = scmp.compare_two_sorters(
288287
sorting1=sorting_HS,
289288
sorting2=sorting_TDC,
290289
sorting1_name='HS',
@@ -339,7 +338,7 @@ Comparison of multiple sorters uses the following procedure:
339338
sorting_TDC = ss.run_sorter(sorter_name='tridesclous', recording=recording)
340339
341340
# Compare multiple spike sorter outputs
342-
mcmp = sc.compare_multiple_sorters(
341+
mcmp = scmp.compare_multiple_sorters(
343342
sorting_list=[sorting_MS4, sorting_HS, sorting_TDC],
344343
name_list=['MS4', 'HS', 'TDC'],
345344
verbose=True,
@@ -354,7 +353,7 @@ Comparison of multiple sorters uses the following procedure:
354353
print(mcmp.comparisons[('MS4', 'TDC')].get_matching())
355354
356355
# The global multi comparison can be visualized with this graph
357-
sw.plot_multicomp_graph(multi_comparison=mcmp)
356+
sw.plot_multicomparison_graph(multi_comparison=mcmp)
358357
359358
# Consensus-based method
360359
#

doc/modules/core.rst

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ with 16 channels:
7676
recording_slice_frames = recording.frame_slice(start_frame=0,
7777
end_frame=int(10*sampling_frequency))
7878
# get new recording with the first 4 channels
79-
recording_slice_chans = recording.channel_slice(channel_ids=channel_ids[:4])
79+
recording_slice_chans = recording.select_channels(channel_ids=channel_ids[:4])
8080
# remove last two channels
81-
recording_rm_chans = recording.remove_channels(channel_ids=channel_ids[-2:])
81+
recording_rm_chans = recording.remove_channels(remove_channel_ids=channel_ids[-2:])
8282
8383
# set channel grouping (assume we have 4 groups of 4 channels, e.g. tetrodes)
8484
groups = [0] * 4 + [1] * 4 + [2] * 4 + [3] * 4
@@ -89,13 +89,14 @@ with 16 channels:
8989
# sliced recordings as values
9090
9191
# set times (for synchronization) - assume our times start at 300 seconds
92+
num_samples = recording.get_num_samples()
9293
timestamps = np.arange(num_samples) / sampling_frequency + 300
9394
recording.set_times(times=timestamps, segment_index=0)
9495
9596
**Note**:
9697
Raw data formats often store data as integer values for memory efficiency. To give these integers meaningful physical units (uV), you can apply a gain and an offset.
9798
Many devices have their own gains and offsets necessary to convert their data and these values are handled by SpikeInterface for its extractors. This
98-
is triggered by the :code:`return_in_uV` parameter in :code:`get_traces()`, (see above example), which will return the traces in uV.
99+
is triggered by the :code:`return_in_uV` parameter in :code:`get_traces()`, (see above example), which will return the traces in uV. Read more in our how to guide, :ref:`physical_units`.
99100

100101

101102
Sorting
@@ -118,7 +119,7 @@ with 10 units:
118119
.. code-block:: python
119120
120121
unit_ids = sorting.unit_ids
121-
num_channels = sorting.get_num_units()
122+
num_units = sorting.get_num_units()
122123
sampling_frequency = sorting.sampling_frequency
123124
124125
# retrieve spike trains for a unit (returned as sample indices)
@@ -217,6 +218,7 @@ is run again with one of the backends supplied.
217218

218219
.. code-block:: python
219220
221+
from pathlib import Path
220222
# create a "processed" folder
221223
processed_folder = Path("processed")
222224
@@ -266,8 +268,7 @@ The :code:`sorting_analyzer` object implements convenient functions to access th
266268
267269
num_channels = sorting_analyzer.get_num_channels()
268270
num_units = sorting_analyzer.get_num_units()
269-
sampling_frequency = sorting_analyzer.get_sampling_frequency()
270-
# or: sampling_frequency = sorting_analyzer.sampling_frequency
271+
sampling_frequency = sorting_analyzer.sampling_frequency
271272
total_num_samples = sorting_analyzer.get_total_samples()
272273
total_duration = sorting_analyzer.get_total_duration()
273274
@@ -689,6 +690,7 @@ In this example, we create a recording and a sorting object from numpy objects:
689690
.. code-block:: python
690691
691692
import numpy as np
693+
from spikeinterface.core import NumpyRecording, NumpySorting
692694
693695
# in-memory recording
694696
sampling_frequency = 30_000.
@@ -697,7 +699,10 @@ In this example, we create a recording and a sorting object from numpy objects:
697699
num_channels = 16
698700
random_traces = np.random.randn(num_samples, num_channels)
699701
700-
recording_memory = NumpyRecording(traces_list=[random_traces])
702+
recording_memory = NumpyRecording(
703+
traces_list=[random_traces]
704+
sampling_frequency=sampling_frequency,
705+
)
701706
# with more elements in `traces_list` we can make multi-segment objects
702707
703708
# in-memory sorting
@@ -706,7 +711,7 @@ In this example, we create a recording and a sorting object from numpy objects:
706711
spike_trains = []
707712
labels = []
708713
for i in range(num_units):
709-
spike_trains_i = np.random.randint(low=0, high=num_samples, size=num_spikes_unit)
714+
spike_trains_i = list(np.random.randint(low=0, high=num_samples, size=num_spikes_unit))
710715
labels_i = [i] * num_spikes_unit
711716
spike_trains += spike_trains_i
712717
labels += labels_i
@@ -751,7 +756,7 @@ the new objects will be *views* of the original ones.
751756
752757
# keep one channel of every tenth channel
753758
keep_ids = recording.channel_ids[::10]
754-
sub_recording = recording.channel_slice(channel_ids=keep_ids)
759+
sub_recording = recording.select_channels(channel_ids=keep_ids)
755760
756761
# keep between 5min and 12min
757762
fs = recording.sampling_frequency
@@ -870,13 +875,15 @@ They are useful to make examples, tests, and small demos:
870875

871876
.. code-block:: python
872877
878+
from spikeinterface.core import generate_recording, generate_sorting, generate_snippets
879+
873880
# recording with 2 segments and 4 channels
874881
recording = generate_recording(num_channels=4, sampling_frequency=30000.,
875882
durations=[10.325, 3.5], set_probe=True)
876883
877884
# sorting with 2 segments and 5 units
878885
sorting = generate_sorting(num_units=5, sampling_frequency=30000., durations=[10.325, 3.5],
879-
firing_rate=15, refractory_period=1.5)
886+
firing_rates=15, refractory_period_ms=1.5)
880887
881888
# snippets of 60 samples on 2 channels from 5 units
882889
snippets = generate_snippets(nbefore=20, nafter=40, num_channels=2,
@@ -929,7 +936,8 @@ WaveformExtractor
929936
^^^^^^^^^^^^^^^^^
930937

931938
This is now a legacy object that can still be accessed through the :py:class:`MockWaveformExtractor`. It is kept
932-
for backward compatibility.
939+
for backward compatibility. You can convert a ``WaveformExtractor`` to a ``SortingAnalyzer``
940+
easily, :ref:`using this guide <tutorials/waveform_extractor_to_sorting_analyzer:From WaveformExtractor to SortingAnalyzer>`.
933941

934942
The :py:class:`~spikeinterface.core.WaveformExtractor` class is the core object to combine a
935943
:py:class:`~spikeinterface.core.BaseRecording` and a :py:class:`~spikeinterface.core.BaseSorting` object.

0 commit comments

Comments
 (0)