Try to remove get_template_extremum_channel() and peak_sign in many place#4374
Try to remove get_template_extremum_channel() and peak_sign in many place#4374samuelgarcia wants to merge 2 commits intoSpikeInterface:mainfrom
Conversation
ecobost
left a comment
There was a problem hiding this comment.
Hi @samuelgarcia Thanks for taking time to work in this; I checked the code from analyzer-> metrics->widgets (anything before analyzer I don't really know).
After going through the code, the only extensions that still receive peak_sign are spike_amplitudes, quality metrics and template_metrics; and that argument can be dropped from all of them (in xxxxxx_metrics is not used at all, while in spike_amplitudes is used once but could be replaced with analyzer.main_channel_peak_sign). Given that, I would vote to rename main_channel_peak_sign to just peak_sign in the sorting analyzer (and essentially use it as the global peak_sign that will be used if any extension needs it, similar to sparsity), including ofc to find the main_channel.
In theory, main_channel_peak_mode falls in the same camp but in truth that seems to only be used to pick the main_channel so main_channel_peak_mode as name might be ok.
| overwrite=False, | ||
| backend_options=None, | ||
| **sparsity_kwargs, | ||
| sparsity_kwargs=None, |
There was a problem hiding this comment.
This argument could be dropped. It's not the responsibility of this function to create all types of sparsity. You could either provide an Sparsity object or let create_sorting_analyzer create it with the default method (from_radius_and_main_channel_index). I feel like that's fair enough (it's more of a convenience) and simplifies the sparsity logic here (curently there are three or four different ways to get sparsity). It also avoids some possible inconsistencies (like main_channel_peak_sign != sparsity_kwargs['peak_sign']) or num_spikes_for_main_channel vs sparsity_kwargs['num_spikes_for_sparsity']
The one common use case that will be affected is people that want to create a sorting analyzer with a bigger or smaller radius (the only sparsity_kwargs that would affect the radius_um method + main_index); but maybe they'll just have to estimate sparsity explicitly (and feed it here).
| ) | ||
|
|
||
| elif method != "by_property": | ||
| nbefore = int(ms_before * recording.sampling_frequency / 1000.0) |
There was a problem hiding this comment.
This code (L.816-849) is a duplicate of template_tools.estimate_main_channel_from_recording (used in create_sorting_analyzer to find the main_channel). If it's too bothersome to unify both pieces of code somehow, maybe we can at least refactor so radius method here uses estimate_main_channel_from_recording so we can guarantee that:
sparsity = create_sparsity(peak_sign=my_peak_sign, num_spikes_for_sparsity=my_num_spikes)
analyzer1 = create_sorting_analyzer(sparsity=sparsity)
analyzer2 = create_sorting_analyzer(main_channel_peak_sign=my_peak_sign, num_spikes_for_main_channel=my_num_spikes)
assert analyzer1.sparsity == analyzer2.sparsity Essentially refactor it as
if method== radius:
if main_channel_index is None:
main_channel_index = estimate_main_channel_from_recording(...)
sparsity = .... from_radius_and_main_channel(...)
elif method != 'property'
# the repeated code here (any changes won't affect radius method)| sparsity = ChannelSparsity.from_closest_channels(templates, num_channels) | ||
| elif method == "radius": | ||
| assert radius_um is not None, "For the 'radius' method, 'radius_um' needs to be given" | ||
| sparsity = ChannelSparsity.from_radius(templates, radius_um, peak_sign=peak_sign) |
There was a problem hiding this comment.
this does not receive peak_sign anymore
| sparsity = ChannelSparsity.from_closest_channels(templates_or_sorting_analyzer, num_channels) | ||
| elif method == "radius": | ||
| assert radius_um is not None, "For the 'radius' method, 'radius_um' needs to be given" | ||
| sparsity = ChannelSparsity.from_radius(templates_or_sorting_analyzer, radius_um, peak_sign=peak_sign) |
There was a problem hiding this comment.
from_radius does not receive peak_sign anymore
| ms_before: float = 1.0, | ||
| ms_after: float = 2.5, | ||
| method: "radius" | "best_channels" | "closest_channels" | "amplitude" | "snr" | "by_property" = "radius", | ||
| peak_sign: "neg" | "pos" | "both" = "neg", |
There was a problem hiding this comment.
consider changing default to "both" to agree with default in create_sorting_analyzer (main_channel_peak_sign)
|
|
||
| if sorting_analyzer_or_templates.sparsity is None: | ||
| sparsity = compute_sparsity( | ||
| sorting_analyzer_or_templates, peak_sign=peak_sign, method="radius", radius_um=radius_um |
There was a problem hiding this comment.
peak_sign is no longer used for method radius ((compute_sparsity uses analyzer/templates.get_main_channels) so not needed here. Can also be dropped as an argument (only was used in this line)
| unit_ids: str | int | None | ||
| A list of unit_id to restrci the computation | ||
| peak_sign : "neg" | "pos" | "both", default: "neg" | ||
| Sign of the template to compute best channels |
There was a problem hiding this comment.
not true anymore; now it is only used to invert the gaussian prototype template (in l. 275); I think peak_sign='neg' default makes sense in this case
|
|
||
| if unit_ids is None: | ||
| unit_ids = sorting_analyzer.unit_ids | ||
| peak_sign = self.params["peak_sign"] |
| metrics_to_compute: list[str] | None = None, | ||
| periods=None, | ||
| # common extension kwargs | ||
| peak_sign="neg", |
There was a problem hiding this comment.
In theory you can drop peak_sign (it is no longer used anywhere here); in practice, it will be used in the bombcell PR (and probably by Alessio/Chris when they re-work the template metrics) but perhaps rather than as an argument, it can be read from analyzer
| use_valid_periods=False, | ||
| periods=None, | ||
| # common extension kwargs | ||
| peak_sign=None, |
There was a problem hiding this comment.
this is no longer needed (it was only used for the extremum_channel in the PCA metrics)
|
Also, given that you are touching this code anyway, do you mind sending peak_sign here too I don't mean to make it work with the analyzer (although maybe it makes sense to forward the analyzer.main_channel_peak_sign here too) but just to forward the peak_sign argument like unit_peak_shifts = get_template_extremum_channel_peak_shift(sorting_analyzer, peak_sign=peak_sign)thanks 👍🏽 |
| templates_array = self.get_dense_templates() | ||
| main_channel_index = _get_main_channel_from_template_array(templates_array, mode, main_channel_peak_sign, self.nbefore) | ||
|
|
||
| if outputs is "index": |
There was a problem hiding this comment.
this should use == (rather than is)
|
|
||
| if outputs is "index": | ||
| main_chans = main_channel_index | ||
| elif outputs is "id": |
There was a problem hiding this comment.
this should use == (rather than is)
| """ | ||
| main_channel_index = self.get_sorting_property("main_channel_index") | ||
| if outputs is "index": |
There was a problem hiding this comment.
this should use == (rather than is)
| main_channel_index = self.get_sorting_property("main_channel_index") | ||
| if outputs is "index": | ||
| main_chans = main_channel_index | ||
| elif outputs is "id": |
There was a problem hiding this comment.
this should use == (rather than is)
This PR try to
extremum_channeltomain_channelget_template_extremum_channel()to use insteadanalyzer.get_main_channel()which is now a propertytemplates.get_main_channel()which compute on the fly the old behaviorpeak_signwhen it was use to get the main channelmain_channel_peak_signinstead ofpeak_signwhen it is for main channelpeak_sigwhen it makes sens, like peak detection or in sorter context.The main consequences:
it is provided
@alejoe91
@chrishalcrow
@ecobost
@yger