11from __future__ import annotations
22
33from pathlib import Path
4- import copy
54from packaging import version
65
76from ..basesorter import BaseSorter
8- from spikeinterface .core .old_api_utils import NewToOldRecording
97
10- from spikeinterface .core import load_extractor
118from spikeinterface .extractors import HerdingspikesSortingExtractor
129
1310
@@ -19,90 +16,72 @@ class HerdingspikesSorter(BaseSorter):
1916 requires_locations = True
2017 compatible_with_parallel = {"loky" : True , "multiprocessing" : True , "threading" : False }
2118 _default_params = {
22- # core params
23- "clustering_bandwidth" : 5.5 , # 5.0,
24- "clustering_alpha" : 5.5 , # 5.0,
19+ "chunk_size" : None ,
20+ "rescale" : True ,
21+ "rescale_value" : - 1280.0 ,
22+ "common_reference" : "median" ,
23+ "spike_duration" : 1.0 ,
24+ "amp_avg_duration" : 0.4 ,
25+ "threshold" : 8.0 ,
26+ "min_avg_amp" : 1.0 ,
27+ "AHP_thr" : 0.0 ,
28+ "neighbor_radius" : 90.0 ,
29+ "inner_radius" : 70.0 ,
30+ "peak_jitter" : 0.25 ,
31+ "rise_duration" : 0.26 ,
32+ "decay_filtering" : False ,
33+ "decay_ratio" : 1.0 ,
34+ "localize" : True ,
35+ "save_shape" : True ,
36+ "out_file" : "HS2_detected" ,
37+ "left_cutout_time" : 0.3 ,
38+ "right_cutout_time" : 1.8 ,
39+ "verbose" : True ,
40+ "clustering_bandwidth" : 4.0 ,
41+ "clustering_alpha" : 4.5 ,
2542 "clustering_n_jobs" : - 1 ,
2643 "clustering_bin_seeding" : True ,
27- "clustering_min_bin_freq" : 16 , # 10 ,
44+ "clustering_min_bin_freq" : 4 ,
2845 "clustering_subset" : None ,
29- "left_cutout_time" : 0.3 , # 0.2,
30- "right_cutout_time" : 1.8 , # 0.8,
31- "detect_threshold" : 20 , # 24, #15,
32- # extra probe params
33- "probe_masked_channels" : [],
34- "probe_inner_radius" : 70 ,
35- "probe_neighbor_radius" : 90 ,
36- "probe_event_length" : 0.26 ,
37- "probe_peak_jitter" : 0.2 ,
38- # extra detection params
39- "t_inc" : 100000 ,
40- "num_com_centers" : 1 ,
41- "maa" : 12 ,
42- "ahpthr" : 11 ,
43- "out_file_name" : "HS2_detected" ,
44- "decay_filtering" : False ,
45- "save_all" : False ,
46- "amp_evaluation_time" : 0.4 , # 0.14,
47- "spk_evaluation_time" : 1.0 ,
48- # extra pca params
4946 "pca_ncomponents" : 2 ,
5047 "pca_whiten" : True ,
51- # bandpass filter
52- "freq_min" : 300.0 ,
53- "freq_max" : 6000.0 ,
54- "filter" : True ,
55- # rescale traces
56- "pre_scale" : True ,
57- "pre_scale_value" : 20.0 ,
58- # remove duplicates (based on spk_evaluation_time)
59- "filter_duplicates" : True ,
6048 }
6149
6250 _params_description = {
63- # core params
64- "clustering_bandwidth" : "Meanshift bandwidth, average spatial extent of spike clusters (um)" ,
65- "clustering_alpha" : "Scalar for the waveform PC features when clustering." ,
66- "clustering_n_jobs" : "Number of cores to use for clustering." ,
67- "clustering_bin_seeding" : "Enable clustering bin seeding." ,
68- "clustering_min_bin_freq" : "Minimum spikes per bin for bin seeding." ,
69- "clustering_subset" : "Number of spikes used to build clusters. All by default." ,
70- "left_cutout_time" : "Cutout size before peak (ms)." ,
71- "right_cutout_time" : "Cutout size after peak (ms)." ,
72- "detect_threshold" : "Detection threshold" ,
73- # extra probe params
74- "probe_masked_channels" : "Masked channels" ,
75- "probe_inner_radius" : "Radius of area around probe channel for localization" ,
76- "probe_neighbor_radius" : "Radius of area around probe channel for neighbor classification." ,
77- "probe_event_length" : "Duration of a spike event (ms)" ,
78- "probe_peak_jitter" : "Maximum peak misalignment for synchronous spike (ms)" ,
79- # extra detection params
80- "t_inc" : "Number of samples per chunk during detection." ,
81- "num_com_centers" : "Number of centroids to average when localizing." ,
82- "maa" : "Minimum summed spike amplitude for spike acceptance." ,
83- "ahpthr" : "Requires magnitude of spike rebound for acceptance" ,
84- "out_file_name" : "File name for storage of unclustered detected spikes" ,
85- "decay_filtering" : "Experimental: Set to True at your risk" ,
86- "save_all" : "Save all working files after sorting (slow)" ,
87- "amp_evaluation_time" : "Amplitude evaluation time (ms)" ,
88- "spk_evaluation_time" : "Spike evaluation time (ms)" ,
89- # extra pca params
90- "pca_ncomponents" : "Number of principal components to use when clustering" ,
91- "pca_whiten" : "If true, whiten data for pca" ,
92- # bandpass filter
93- "freq_min" : "High-pass filter cutoff frequency" ,
94- "freq_max" : "Low-pass filter cutoff frequency" ,
95- "filter" : "Enable or disable filter" ,
96- # rescale traces
97- "pre_scale" : "Scales recording traces to optimize HerdingSpikes performance" ,
98- "pre_scale_value" : "Scale to apply in case of pre-scaling of traces" ,
99- # remove duplicates (based on spk_evaluation_time)
100- "filter_duplicates" : "Remove spike duplicates (based on spk_evaluation_time)" ,
51+ "localize" : "Perform spike localization. (`bool`, `True`)" ,
52+ "save_shape" : "Save spike shape. (`bool`, `True`)" ,
53+ "out_file" : "Path and filename to store detection and clustering results. (`str`, `HS2_detected`)" ,
54+ "verbose" : "Print progress information. (`bool`, `True`)" ,
55+ "chunk_size" : " Number of samples per chunk during detection. If `None`, a suitable value will be estimated. (`int`, `None`)" ,
56+ "common_reference" : "Method for common reference filtering, can be `average` or `median` (`str`, `median`)" ,
57+ "rescale" : "Automatically re-scale the data. (`bool`, `True`)" ,
58+ "rescale_value" : "Factor by which data is re-scaled. (`float`, `-1280.0`)" ,
59+ "threshold" : "Spike detection threshold. (`float`, `8.0`)" ,
60+ "spike_duration" : "Maximum duration over which a spike is evaluated (ms). (`float`, `1.0`)" ,
61+ "amp_avg_duration" : "Maximum duration over which the spike amplitude is evaluated (ms). (`float`, `0.4`)" ,
62+ "min_avg_amp" : "Minimum integrated spike amplitude for a true spike. (`float`, `1.0`)" ,
63+ "AHP_thr" : "Minimum value of the spike repolarisation for a true spike. (`float`, `0.0`)" ,
64+ "neighbor_radius" : "Radius of area around probe channel for neighbor classification (microns). (`float`, `90.0`)" ,
65+ "inner_radius" : "Radius of area around probe channel for spike localisation (microns). (`float`, `70.0`)" ,
66+ "peak_jitter" : "Maximum peak misalignment for synchronous spike (ms). (`float`, `0.25`)" ,
67+ "rise_duration" : "Maximum spike rise time, in milliseconds. (`float`, `0.26`)" ,
68+ "decay_filtering" : "Exclude duplicate spikes based on spatial decay pattern, experimental. (`bool`,`False`)" ,
69+ "decay_ratio" : "Spatial decay rate for `decay_filtering`. (`float`,`1.0`)" ,
70+ "left_cutout_time" : "Length of cutout before peak (ms). (`float`, `0.3`)" ,
71+ "right_cutout_time" : "Length of cutout after peak (ms). (`float`, `1.8`)" ,
72+ "pca_ncomponents" : "Number of principal components to use when clustering. (`int`, `2`)" ,
73+ "pca_whiten" : "If `True`, whiten data for PCA. (`bool`, `True`)" ,
74+ "clustering_bandwidth" : "Meanshift bandwidth, average spatial extent of spike clusters (microns). (`float`, `4.0`)" ,
75+ "clustering_alpha" : "Scalar for the waveform PC features when clustering. (`float`, `4.5`)" ,
76+ "clustering_n_jobs" : "Number of cores to use for clustering, use `-1` for all available cores. (`int`, `-1`)" ,
77+ "clustering_bin_seeding" : "Enable clustering bin seeding. (`bool`, `True`)" ,
78+ "clustering_min_bin_freq" : "Minimum spikes per bin for bin seeding. (`int`, `4`)" ,
79+ "clustering_subset" : "Number of spikes used to build clusters. All by default. (`int`, `None`)" ,
10180 }
10281
103- sorter_description = """Herding Spikes is a density-based spike sorter designed for high-density retinal recordings.
82+ sorter_description = """Herding Spikes is a density-based spike sorter designed for large-scale high-density recordings.
10483 It uses both PCA features and an estimate of the spike location to cluster different units.
105- For more information see https://doi.org/10.1016/j.jneumeth.2016.06.006 """
84+ For more information see https://www.sciencedirect.com/science/article/pii/S221112471730236X """
10685
10786 installation_mesg = """\n To use HerdingSpikes run:\n
10887 >>> pip install herdingspikes
@@ -130,100 +109,50 @@ def get_sorter_version(cls):
130109
131110 @classmethod
132111 def _check_apply_filter_in_params (cls , params ):
133- return params [ "filter" ]
112+ return False
134113
135114 @classmethod
136115 def _setup_recording (cls , recording , sorter_output_folder , params , verbose ):
137- # nothing to copy inside the folder : Herdingspikes used natively spikeinterface
116+ # nothing to copy inside the folder : Herdingspikes uses spikeinterface natively
138117 pass
139118
140119 @classmethod
141120 def _run_from_folder (cls , sorter_output_folder , params , verbose ):
142121 import herdingspikes as hs
143- from spikeinterface .preprocessing import bandpass_filter , normalize_by_quantile
144122
145123 hs_version = version .parse (hs .__version__ )
146124
147- if hs_version >= version .parse ("0.3.99 " ):
148- new_api = True
125+ if hs_version >= version .parse ("0.4.001 " ):
126+ lightning_api = True
149127 else :
150- new_api = False
128+ lightning_api = False
129+
130+ assert (
131+ lightning_api
132+ ), "HerdingSpikes version <0.4.001 is no longer supported. run:\n >>> pip install --upgrade herdingspikes"
151133
152134 recording = cls .load_recording_from_folder (sorter_output_folder .parent , with_warnings = False )
153135
136+ sorted_file = str (sorter_output_folder / "HS2_sorted.hdf5" )
137+ params ["out_file" ] = str (sorter_output_folder / "HS2_detected" )
154138 p = params
155139
156- # Bandpass filter
157- if p ["filter" ] and p ["freq_min" ] is not None and p ["freq_max" ] is not None :
158- recording = bandpass_filter (recording = recording , freq_min = p ["freq_min" ], freq_max = p ["freq_max" ])
159-
160- if p ["pre_scale" ]:
161- recording = normalize_by_quantile (
162- recording = recording , scale = p ["pre_scale_value" ], median = 0.0 , q1 = 0.05 , q2 = 0.95
163- )
164-
165- if new_api :
166- recording_to_hs = recording
167- else :
168- print (
169- "herdingspikes version<0.3.99 uses the OLD spikeextractors with NewToOldRecording.\n "
170- "Consider updating herdingspikes (pip install herdingspikes>=0.3.99)"
171- )
172- recording_to_hs = NewToOldRecording (recording )
173-
174- # this should have its name changed
175- Probe = hs .probe .RecordingExtractor (
176- recording_to_hs ,
177- masked_channels = p ["probe_masked_channels" ],
178- inner_radius = p ["probe_inner_radius" ],
179- neighbor_radius = p ["probe_neighbor_radius" ],
180- event_length = p ["probe_event_length" ],
181- peak_jitter = p ["probe_peak_jitter" ],
182- )
183-
184- H = hs .HSDetection (
185- Probe ,
186- file_directory_name = str (sorter_output_folder ),
187- left_cutout_time = p ["left_cutout_time" ],
188- right_cutout_time = p ["right_cutout_time" ],
189- threshold = p ["detect_threshold" ],
190- to_localize = True ,
191- num_com_centers = p ["num_com_centers" ],
192- maa = p ["maa" ],
193- ahpthr = p ["ahpthr" ],
194- out_file_name = p ["out_file_name" ],
195- decay_filtering = p ["decay_filtering" ],
196- save_all = p ["save_all" ],
197- amp_evaluation_time = p ["amp_evaluation_time" ],
198- spk_evaluation_time = p ["spk_evaluation_time" ],
140+ det = hs .HSDetectionLightning (recording , p )
141+ det .DetectFromRaw ()
142+ C = hs .HSClustering (det )
143+ C .ShapePCA ()
144+ C .CombinedClustering (
145+ alpha = p ["clustering_alpha" ],
146+ cluster_subset = p ["clustering_subset" ],
147+ bandwidth = p ["clustering_bandwidth" ],
148+ bin_seeding = p ["clustering_bin_seeding" ],
149+ min_bin_freq = p ["clustering_min_bin_freq" ],
150+ n_jobs = p ["clustering_n_jobs" ],
199151 )
200152
201- H .DetectFromRaw (load = True , tInc = int (p ["t_inc" ]))
202-
203- sorted_file = str (sorter_output_folder / "HS2_sorted.hdf5" )
204- if not H .spikes .empty :
205- C = hs .HSClustering (H )
206- C .ShapePCA (pca_ncomponents = p ["pca_ncomponents" ], pca_whiten = p ["pca_whiten" ])
207- C .CombinedClustering (
208- alpha = p ["clustering_alpha" ],
209- cluster_subset = p ["clustering_subset" ],
210- bandwidth = p ["clustering_bandwidth" ],
211- bin_seeding = p ["clustering_bin_seeding" ],
212- n_jobs = p ["clustering_n_jobs" ],
213- min_bin_freq = p ["clustering_min_bin_freq" ],
214- )
215- else :
216- C = hs .HSClustering (H )
217-
218- if p ["filter_duplicates" ]:
219- uids = C .spikes .cl .unique ()
220- for u in uids :
221- s = C .spikes [C .spikes .cl == u ].t .diff () < p ["spk_evaluation_time" ] / 1000 * Probe .fps
222- C .spikes = C .spikes .drop (s .index [s ])
223-
224153 if verbose :
225154 print ("Saving to" , sorted_file )
226- C .SaveHDF5 (sorted_file , sampling = Probe . fps )
155+ C .SaveHDF5 (sorted_file , sampling = recording . get_sampling_frequency () )
227156
228157 @classmethod
229158 def _get_result_from_folder (cls , sorter_output_folder ):
0 commit comments