1515 method : str
1616 * "best_channels" : N best channels with the largest amplitude. Use the "num_channels" argument to specify the
1717 number of channels.
18+ * "closest_channels" : N closest channels according to the distance. Use the "num_channels" argument to specify the
19+ number of channels.
1820 * "radius" : radius around the best channel. Use the "radius_um" argument to specify the radius in um.
1921 * "snr" : threshold based on template signal-to-noise ratio. Use the "threshold" argument
2022 to specify the SNR threshold (in units of noise levels) and the "amplitude_mode" argument
@@ -320,6 +322,39 @@ def from_best_channels(
320322 mask [unit_ind , chan_inds ] = True
321323 return cls (mask , templates_or_sorting_analyzer .unit_ids , templates_or_sorting_analyzer .channel_ids )
322324
325+ ## Some convinient function to compute sparsity from several strategy
326+ @classmethod
327+ def from_closest_channels (cls , templates_or_sorting_analyzer , num_channels ):
328+ """
329+ Construct sparsity from N closest channels
330+ Use the "num_channels" argument to specify the number of channels.
331+
332+ Parameters
333+ ----------
334+ templates_or_sorting_analyzer : Templates | SortingAnalyzer
335+ A Templates or a SortingAnalyzer object.
336+ num_channels : int
337+ Number of channels for "best_channels" method.
338+
339+ Returns
340+ -------
341+ sparsity : ChannelSparsity
342+ The estimated sparsity
343+ """
344+ from .template_tools import get_template_amplitudes
345+
346+ mask = np .zeros (
347+ (templates_or_sorting_analyzer .unit_ids .size , templates_or_sorting_analyzer .channel_ids .size ), dtype = "bool"
348+ )
349+ channel_locations = templates_or_sorting_analyzer .get_channel_locations ()
350+ distances = np .linalg .norm (channel_locations [:, np .newaxis ] - channel_locations [np .newaxis , :], axis = 2 )
351+
352+ for unit_ind , unit_id in enumerate (templates_or_sorting_analyzer .unit_ids ):
353+ chan_inds = np .argsort (distances [unit_ind ])
354+ chan_inds = chan_inds [:num_channels ]
355+ mask [unit_ind , chan_inds ] = True
356+ return cls (mask , templates_or_sorting_analyzer .unit_ids , templates_or_sorting_analyzer .channel_ids )
357+
323358 @classmethod
324359 def from_radius (cls , templates_or_sorting_analyzer , radius_um , peak_sign = "neg" ):
325360 """
@@ -600,7 +635,9 @@ def create_dense(cls, sorting_analyzer):
600635def compute_sparsity (
601636 templates_or_sorting_analyzer : "Templates | SortingAnalyzer" ,
602637 noise_levels : np .ndarray | None = None ,
603- method : "radius" | "best_channels" | "snr" | "amplitude" | "energy" | "by_property" | "ptp" = "radius" ,
638+ method : (
639+ "radius" | "best_channels" | "closest_channels" | "snr" | "amplitude" | "energy" | "by_property" | "ptp"
640+ ) = "radius" ,
604641 peak_sign : "neg" | "pos" | "both" = "neg" ,
605642 num_channels : int | None = 5 ,
606643 radius_um : float | None = 100.0 ,
@@ -635,7 +672,7 @@ def compute_sparsity(
635672 # to keep backward compatibility
636673 templates_or_sorting_analyzer = templates_or_sorting_analyzer .sorting_analyzer
637674
638- if method in ("best_channels" , "radius" , "snr" , "amplitude" , "ptp" ):
675+ if method in ("best_channels" , "closest_channels" , " radius" , "snr" , "amplitude" , "ptp" ):
639676 assert isinstance (
640677 templates_or_sorting_analyzer , (Templates , SortingAnalyzer )
641678 ), f"compute_sparsity(method='{ method } ') need Templates or SortingAnalyzer"
@@ -647,6 +684,9 @@ def compute_sparsity(
647684 if method == "best_channels" :
648685 assert num_channels is not None , "For the 'best_channels' method, 'num_channels' needs to be given"
649686 sparsity = ChannelSparsity .from_best_channels (templates_or_sorting_analyzer , num_channels , peak_sign = peak_sign )
687+ elif method == "closest_channels" :
688+ assert num_channels is not None , "For the 'closest_channels' method, 'num_channels' needs to be given"
689+ sparsity = ChannelSparsity .from_closest_channels (templates_or_sorting_analyzer , num_channels )
650690 elif method == "radius" :
651691 assert radius_um is not None , "For the 'radius' method, 'radius_um' needs to be given"
652692 sparsity = ChannelSparsity .from_radius (templates_or_sorting_analyzer , radius_um , peak_sign = peak_sign )
@@ -698,7 +738,7 @@ def estimate_sparsity(
698738 num_spikes_for_sparsity : int = 100 ,
699739 ms_before : float = 1.0 ,
700740 ms_after : float = 2.5 ,
701- method : "radius" | "best_channels" | "amplitude" | "snr" | "by_property" | "ptp" = "radius" ,
741+ method : "radius" | "best_channels" | "closest_channels" | " amplitude" | "snr" | "by_property" | "ptp" = "radius" ,
702742 peak_sign : "neg" | "pos" | "both" = "neg" ,
703743 radius_um : float = 100.0 ,
704744 num_channels : int = 5 ,
@@ -747,7 +787,7 @@ def estimate_sparsity(
747787 # Can't be done at module because this is a cyclic import, too bad
748788 from .template import Templates
749789
750- assert method in ("radius" , "best_channels" , "snr" , "amplitude" , "by_property" , "ptp" ), (
790+ assert method in ("radius" , "best_channels" , "closest_channels" , " snr" , "amplitude" , "by_property" , "ptp" ), (
751791 f"method={ method } is not available for `estimate_sparsity()`. "
752792 "Available methods are 'radius', 'best_channels', 'snr', 'amplitude', 'by_property', 'ptp' (deprecated)"
753793 )
@@ -802,6 +842,9 @@ def estimate_sparsity(
802842 sparsity = ChannelSparsity .from_best_channels (
803843 templates , num_channels , peak_sign = peak_sign , amplitude_mode = amplitude_mode
804844 )
845+ elif method == "closest_channels" :
846+ assert num_channels is not None , "For the 'closest_channels' method, 'num_channels' needs to be given"
847+ sparsity = ChannelSparsity .from_closest_channels (templates , num_channels )
805848 elif method == "radius" :
806849 assert radius_um is not None , "For the 'radius' method, 'radius_um' needs to be given"
807850 sparsity = ChannelSparsity .from_radius (templates , radius_um , peak_sign = peak_sign )
0 commit comments