Skip to content

Conversation

@luiztauffer
Copy link
Collaborator

There are cases when we'd like to load only the .nev file containing the events data, and ignore the local .nsX files. One example for this is when an experimenter pauses and then resumes the recording. This creates a different number of segments between nev and nsX, which breaks neo reader.
Thankfully, neo already allows for it, all we need to do is to explicitly pass the nsx_to_load argument. So, in this PR I'm proposing adding it as an optional argument to BlackrockSortingExtractor.

Ps.: It's probably be a good idea tho eventually allow passing forward all kwargs available at the neo extractors directly from the SI wrappers __init__ methods, a neo_kwargs: dict or something

@luiztauffer
Copy link
Collaborator Author

@alejoe91 @h-mayorquin could you take a look at this whenever you get a chance? thanks!

@h-mayorquin
Copy link
Collaborator

Can you describe more about the files? I am not familiar with this format.
I am reading you correctly that at the moment we can load events as a sorting extractor?

@luiztauffer
Copy link
Collaborator Author

@h-mayorquin
The Blackrock systems store the ecephys recordings as multiple files, e.g.:

  • ns2 for LFP signals
  • ns6 for high-pass filtered signals
  • nev for events, including spiking units

neo can read from all of them separately, but the default configuration of parameters tells neo to read from all files together. All we’re doing here is adding to the SI wrapper a neo reader arg, that already exists but just wasn’t available to the wrapper. This new argument allows us to read only from nev, if that’s what the user wants. This is relevant for a particular lab we're working with right now.

important: this change depends on the current master branch of neo, so maybe we should push for a release over there before merging this?

@alejoe91
Copy link
Member

alejoe91 commented Apr 8, 2025

Thanks @luiztauffer

I wonder if the sorting extractor should automatically just load the nev file only. The other files shouldn't be required for loading spike trains only. What do you think?

@luiztauffer
Copy link
Collaborator Author

luiztauffer commented Apr 8, 2025

@alejoe91 in the current implementation, the Extractor seems to depend on the streams recordings to extract the sampling rate, otherwise the user need to provide the sampling rate at instantiation.

if sampling_frequency is None:
sampling_frequency = self._infer_sampling_frequency_from_analog_signal(stream_id=stream_id)

This shouldn't be the case, though. I can look into it, and see if we could make them independent.

Probably this mixing comes originally from the fact that:

  • if you have nev ou probably have ns6 as well
  • neo tries to read it all together at once

@luiztauffer
Copy link
Collaborator Author

@alejoe91 check it out now, let me know what you think

@h-mayorquin
Copy link
Collaborator

important: this change depends on the current master branch of neo, so maybe we should push for a release over there before merging this?

which feature that we merge recently? There will be release soon.

I wonder if the sorting extractor should automatically just load the nev file only. The other files shouldn't be required for loading spike trains only. What do you think?

Agree with this.

@alejoe91 the tests are failing because of pinnin numcodes. Oh, I see you merged already.

@luiztauffer
Copy link
Collaborator Author

@h-mayorquin I believe it's this one: NeuralEnsemble/python-neo@b87b8f2

@h-mayorquin
Copy link
Collaborator

@h-mayorquin I believe it's this one: NeuralEnsemble/python-neo@b87b8f2

Ah, the overflow of Thinh. Yes, there will be a release soon according to the neo team.

@h-mayorquin
Copy link
Collaborator

h-mayorquin commented Apr 8, 2025

@luiztauffer Ok, this is hardcoded here on the python neo side anyway.

https://github.com/NeuralEnsemble/python-neo/blob/be8e663e5912dcab70d5c87a9ade80f576681702/neo/rawio/blackrockrawio.py#L301

https://github.com/NeuralEnsemble/python-neo/blob/be8e663e5912dcab70d5c87a9ade80f576681702/neo/rawio/blackrockrawio.py#L253

So no need to override the function at all. If we are going to use the wf we might as well just hardcoded in the init of blackrock and avoid all the coding complications.

@luiztauffer
Copy link
Collaborator Author

@h-mayorquin right, nice catch! Yeah, from the SI wrapper side, I believe it would be better to wait for your PR to get merged and then we can retrieve the sampling rate from the neo reader, instead of hard coding it here again. This way we keep true to whatever neo says, including if there's any future changes to that parameter

@h-mayorquin
Copy link
Collaborator

@alejoe91

If we consider wf_sampling_rate from the Blackrock headers a reliable heuristic for determining the sorter's sampling frequency (@samuelgarcia maybe knows better), I suggest the following path forward:

  1. Expose this value as a class attribute on the Neo side, so users don’t need to parse the headers to access the main sampling frequency. See: Blackrock make sampling rate a class attribute. NeuralEnsemble/python-neo#1685

  2. Add a keyword argument to BlackrockSortingExtractor like use_main_blackrock_frequency: bool = True, and pass the main sampling frequency from the header here:

    NeoBaseSortingExtractor.__init__(
    self,
    sampling_frequency=sampling_frequency,
    stream_id=stream_id,
    stream_name=stream_name,
    **neo_kwargs,

This approach has a few advantages:

  • No need to override any functionality.
  • Clearly communicates the heuristic being used.
  • Couples the logic to Neo, so improvements in Neo automatically propagate here.
  • Provides users with an escape hatch in case the heuristic doesn’t apply in their case.

@h-mayorquin h-mayorquin added extractors Related to extractors module NEO Problem related to NEO IO labels Apr 8, 2025
@luiztauffer
Copy link
Collaborator Author

maybe instead of use_main_blackrock_frequency: bool we could use sampling_frequency: Optional[float] = None

@h-mayorquin
Copy link
Collaborator

maybe instead of use_main_blackrock_frequency: bool we could use sampling_frequency: Optional[float] = None

You mean that if the sampling_frequency is None then the main sampling frequency is used?
The problem with that is it blocks the path from the other heuristic that we have that you already touched upon:

_infer_sampling_frequency_from_analog_signal

I suggest that for your conversion you should just set the sampling frequency to this value there and ask them if that the is the sampling frequency of the signal that they sorted.

@luiztauffer
Copy link
Collaborator Author

It looks like Blackrock won’t need _infer_sampling_frequency_from_analog_signal at all, or am I missing something?

If we want to keep having sampling_frequency as an optional argument for SI extractor to override whatever neo has in place, ok.

But I don’t think it’s reasonable for a Neuroconv DataInterface to have a required argument for something which is a fixed attribute down the line, in the neo reader (and it seems to be correct for that system). And I’m not even sure if leaving this as an optional override for NWB conversions would be recommended.

@h-mayorquin
Copy link
Collaborator

h-mayorquin commented Apr 9, 2025

But I don’t think it’s reasonable for a Neuroconv DataInterface to have a required argument for something which is a fixed attribute down the line, in the neo reader (and it seems to be correct for that system). And I’m not even sure if leaving this as an optional override for NWB conversions would be recommended.

[bold mine]

Yes, I think that’s the core of the issue. The Neo sampling frequency is undocumented, and the last time I discussed the extractor with Sam, he mentioned he hasn’t touched it in a while. I also asked him about using the frequency from the spike header, and he said it might not be accurate. In other words, I don’t fully trust that value, and I’d prefer to leave an escape valve in case we're wrong so we don’t have to tell users to wait for another PR to correct the mistake.

It looks like Blackrock won’t need _infer_sampling_frequency_from_analog_signal at all, or am I missing something?

If there’s only one signal channel, the heuristic gets the sampling frequency from that channel, which should be the signal that was sorted. Otherwise, if there’s more than one signal, the user gets an error telling them to set the sampling frequency manually.

Regardless of all that, it's important to point out that this is a problem for us (people who work with other people's data) because we often don’t know the correct sampling frequency. Hopefully, most users will be the ones who generated the data (fingers crossed), in which case it's better if they always set the frequency themselves. Maybe in this case we should have an especial error for when the segments in the sorting are not the same as the signal which was the initial problem, right?

I’ve come around to agree with Sam that users should be responsible for setting the sampling frequency as neo is not the right tool to fetch this from and he, reasonably, does not want to maintain that. All that said, I’m okay with adding the heuristic you’re suggesting here for the sake of expediency and give the user more options. Let’s see what others think.

@luiztauffer luiztauffer deleted the blackrock-nev branch April 14, 2025 08:07
@luiztauffer luiztauffer restored the blackrock-nev branch April 14, 2025 08:18
@luiztauffer luiztauffer reopened this Apr 14, 2025
@luiztauffer
Copy link
Collaborator Author

luiztauffer commented Apr 14, 2025

just adding now the option to to pass two relevant kwargs to the neo reader: nsx_to_load and sampling_frequency
which will be used by Neuroconv's Blackrock data interface: catalystneuro/neuroconv#1290

Copy link
Collaborator

@h-mayorquin h-mayorquin left a comment

Choose a reason for hiding this comment

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

+1 on this.
Will allow users to specify the sampling frequency and to load data when there are more than one segment on the recording but not in the sorting.

stream_id: Optional[str] = None,
stream_name: Optional[str] = None,
sampling_frequency: Optional[float] = None,
nsx_to_load: Optional[int | list | str] = None,
Copy link
Collaborator

Choose a reason for hiding this comment

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

the typing of this seems strange, can you double check @luiztauffer?

Sam is going to vacations so if he does not have time to check it before that I will merge this next Monday but just double check this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok, makes sense.

@h-mayorquin h-mayorquin merged commit e068774 into SpikeInterface:main Apr 22, 2025
15 checks passed
@luiztauffer luiztauffer deleted the blackrock-nev branch April 22, 2025 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extractors Related to extractors module NEO Problem related to NEO IO

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants