Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ibl exporter #3522

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open

Ibl exporter #3522

wants to merge 43 commits into from

Conversation

jonahpearl
Copy link
Contributor

Function for #2843
Would be great if a few others could test on their data. Thanks!

alejoe91 and others added 30 commits March 8, 2024 18:01
Co-authored-by: Garcia Samuel <[email protected]>
…_similarity_diff_units

Fix template similarity when there are multiple sizes of units
Port bug fix PRs from Main over to 0.100-bug-fixes
@zm711
Copy link
Collaborator

zm711 commented Nov 7, 2024

This looks like it is off of the bug fixes branch. @jonahpearl you want to switch this to main instead?

@jonahpearl
Copy link
Contributor Author

I'm not 100% sure what you mean. Do you want me to cherry-pick / rebase my changes onto the main branch? I branched it a while ago so I've had to merge main in a few times.

@alejoe91
Copy link
Member

alejoe91 commented Nov 7, 2024

@jonahpearl thanks for this! Is it ok if I push to this PR?

I've been working on refining the export function so I'd be good to be in sync :)

@alejoe91
Copy link
Member

alejoe91 commented Nov 7, 2024

This looks like it is off of the bug fixes branch. @jonahpearl you want to switch this to main instead?

I think it's correctly using the SortingAnalyzer. Maybe the added branch in the actions yaml was confusing, so let's just remove it

@@ -194,7 +194,7 @@ def export_to_phy(
templates[unit_ind, :, :][:, : len(chan_inds)] = template
templates_ind[unit_ind, : len(chan_inds)] = chan_inds

if not sorting_analyzer.has_extension("template_similarity"):
if sorting_analyzer.get_extension("template_similarity") is None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're suggesting we put this back to "has_extension"? Any reason this one and not the other ones? The ibl export relies on doing a Phy export, and as discussed elsewhere this "has_extension" pattern is no longer safe if the folder exists but without any data inside.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, all of them. I think it's much easier to read :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it'd be easier to read, but I disagree that it's preferable, because it will break if the extension folder exists but doesn't have data. It's not unusual to end up in that kind of state — eg if you run analyzer.compute(["random_spikes", "spike_amplitudes"]) on a new analyzer, the spike_amplitudes folder will get made but nothing will be in it because the analyzer will need "templates" to be computed. There are various ways to fix that involving try/excepts / clean-ups, but in the current context, I don't think it makes sense to not catch this case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a separate issue :) we should delay folder creation until dependencies are checked. Can you open another issue about it?

Comment on lines 28 to 32
from spikeinterface.core import extract_waveforms
from spikeinterface.extractors import toy_example
from spikeinterface.comparison import compare_templates

import numpy as np
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this belongs to a different PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, not sure how this slipped in.


import numpy as np
import numpy.typing as npt
from scipy.signal import welch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import scipy should be done within the functions using it because it's not a mandatory requirement (see failing tests)

Copy link
Collaborator

@zm711 zm711 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a docstring fix while you're at it. I believe we add this to the dev docs after this PR was started which is why this was missed.

Comment on lines +48 to +79
----------
analyzer: SortingAnalyzer
The sorting analyzer object.
output_folder: str | Path
The output folder where the phy template-gui files are saved
rms_win_length_sec: float, default: 3
The window length in seconds for the RMS calculation.
welch_win_length_samples: int, default: 1024
The window length in samples for the Welch method.
total_secs: int, default: 100
The total number of seconds to use for the spectral density calculation.
only_ibl_specific_steps: bool, default: False
If True, only the IBL specific steps are run (i.e. skips calling `export_to_phy`)
compute_pc_features: bool, default: False
If True, pc features are computed
compute_amplitudes: bool, default: True
If True, waveforms amplitudes are computed
sparsity: ChannelSparsity or None, default: None
The sparsity object (currently only respected for phy part of the export)
copy_binary: bool, default: True
If True, the recording is copied and saved in the phy "output_folder"
remove_if_exists: bool, default: False
If True and "output_folder" exists, it is removed and overwritten
template_mode: str, default: "median"
Parameter "mode" to be given to WaveformExtractor.get_template()
dtype: dtype or None, default: None
Dtype to save binary data
verbose: bool, default: True
If True, output is verbose
use_relative_path : bool, default: False
If True and `copy_binary=True` saves the binary file `dat_path` in the `params.py` relative to `output_folder` (ie `dat_path=r"recording.dat"`). If `copy_binary=False`, then uses a path relative to the `output_folder`
If False, uses an absolute path in the `params.py` (ie `dat_path=r"path/to/the/recording.dat"`)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
----------
analyzer: SortingAnalyzer
The sorting analyzer object.
output_folder: str | Path
The output folder where the phy template-gui files are saved
rms_win_length_sec: float, default: 3
The window length in seconds for the RMS calculation.
welch_win_length_samples: int, default: 1024
The window length in samples for the Welch method.
total_secs: int, default: 100
The total number of seconds to use for the spectral density calculation.
only_ibl_specific_steps: bool, default: False
If True, only the IBL specific steps are run (i.e. skips calling `export_to_phy`)
compute_pc_features: bool, default: False
If True, pc features are computed
compute_amplitudes: bool, default: True
If True, waveforms amplitudes are computed
sparsity: ChannelSparsity or None, default: None
The sparsity object (currently only respected for phy part of the export)
copy_binary: bool, default: True
If True, the recording is copied and saved in the phy "output_folder"
remove_if_exists: bool, default: False
If True and "output_folder" exists, it is removed and overwritten
template_mode: str, default: "median"
Parameter "mode" to be given to WaveformExtractor.get_template()
dtype: dtype or None, default: None
Dtype to save binary data
verbose: bool, default: True
If True, output is verbose
use_relative_path : bool, default: False
If True and `copy_binary=True` saves the binary file `dat_path` in the `params.py` relative to `output_folder` (ie `dat_path=r"recording.dat"`). If `copy_binary=False`, then uses a path relative to the `output_folder`
If False, uses an absolute path in the `params.py` (ie `dat_path=r"path/to/the/recording.dat"`)
----------
analyzer : SortingAnalyzer
The sorting analyzer object.
output_folder : str | Path
The output folder where the phy template-gui files are saved
rms_win_length_sec : float, default: 3
The window length in seconds for the RMS calculation.
welch_win_length_samples : int, default: 1024
The window length in samples for the Welch method.
total_secs : int, default: 100
The total number of seconds to use for the spectral density calculation.
only_ibl_specific_steps : bool, default: False
If True, only the IBL specific steps are run (i.e. skips calling `export_to_phy`)
compute_pc_features : bool, default: False
If True, pc features are computed
compute_amplitudes : bool, default: True
If True, waveforms amplitudes are computed
sparsity : ChannelSparsity or None, default: None
The sparsity object (currently only respected for phy part of the export)
copy_binary : bool, default: True
If True, the recording is copied and saved in the phy "output_folder"
remove_if_exists : bool, default: False
If True and "output_folder" exists, it is removed and overwritten
template_mode : str, default: "median"
Parameter "mode" to be given to WaveformExtractor.get_template()
dtype : dtype or None, default: None
Dtype to save binary data
verbose : bool, default: True
If True, output is verbose
use_relative_path : bool, default: False
If True and `copy_binary=True` saves the binary file `dat_path` in the `params.py` relative to `output_folder` (ie `dat_path=r"recording.dat"`). If `copy_binary=False`, then uses a path relative to the `output_folder`
If False, uses an absolute path in the `params.py` (ie `dat_path=r"path/to/the/recording.dat"`)

We are working on implementing numpydoc linting in the future and it requires there to be a space after the parameter.

try:
from scipy.signal import welch
except ImportError as e:
raise ImportError("Please install scipy to use the export_to_ibl function.") from e
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never seen this before. How does this raise error from e work?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just import it in the function. The problem is that core tests will look in all files for tests, and script is not installed in core tests

"fscale": fscale(welch_win_length_samples, 1 / fs_ap, one_sided=True),
"tscale": wingen.tscale(fs=fs_ap),
}
win["spectral_density"] = np.zeros((len(win["fscale"]), analyzer.recording.get_num_channels()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have a proper function to compute the spectrum if you need it.
So we could have proper tests for this.

total_samples_ap = int(np.min([fs_ap * total_secs, analyzer.recording.get_num_samples()]))

# the window generator will generates window indices
wingen = WindowGenerator(ns=total_samples_ap, nswin=rms_win_length_samples_ap, overlap=0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have already some function to generate window in spikeinterface.

@samuelgarcia
Copy link
Member

Hi @jonahpearl
thanks for this contribution to spikeinetrface and making a bridge to ibl tools.

About to_ibl_utils.py I would identify function by function what the need and if there is an equivalent function in spikeinterface for it. Then I would implement then at the good place (core, preprocessing, postprocessing, ...) and also would make some tests function per function.

About the spectrum, I would implement it in a speparate file using the spikeinterface machinery on chunk parralelisation.

@jonahpearl
Copy link
Contributor Author

Fair enough -- I don't have time to refactor this right now but I can work on it in the coming months.

@alejoe91 alejoe91 added the exporters Related to exporters module label Nov 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
exporters Related to exporters module
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants