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

How to use NEO format in SpikeInterface? #3448

Open
DerfelLee opened this issue Sep 27, 2024 · 15 comments
Open

How to use NEO format in SpikeInterface? #3448

DerfelLee opened this issue Sep 27, 2024 · 15 comments
Labels
question General question regarding SI

Comments

@DerfelLee
Copy link

Hi! I'm trying to build a pipeline combining different python packages including SpikeInterface. I want to read the data using 'neo.rawio' since it's supported by SpikeInterface. I wonder how I can put a 'neo.rawio' data into SpikeInterface? Also, after using the SpikeInterface, how can I export the data back to NEO format?

I saw someone discussing creating an 'export_to_neo' function in 2019, but it seems like this is not in the API or code.

Thanks!

@DerfelLee
Copy link
Author

I'm only using the recording right now, but I only saw NumpySorting has a 'from_neo_spiketrain' function

@zm711
Copy link
Collaborator

zm711 commented Sep 29, 2024

Basically all of neo.rawio is incorporated under the hood inside the extractors module. So you would do

import spikeinterface.extractors as se
recording = se.read_xx()

where the xx is the format you want. For example blackrock, intan, neuralynx, spikeglx, etc. See all formats here.

When you use the se.read_xx function (for example se.read_intan() you are actually using the neo.rawio api under the hood. If this doesn't answer your question could you provide more context about which format you want to use?

@zm711 zm711 added the question General question regarding SI label Sep 29, 2024
@DerfelLee
Copy link
Author

I've read these functions in the documents, but they seem to read the file into SpikeInterface formats. I want to use SpikeInterface with other Python packages like Elephant without converting and writing the file on disk, which may save me a lot of time and make things easier. Therefore, I just want to ask if I can just simply use functions to convert NEO-loaded files with SpikeInterface Extractors.

@zm711
Copy link
Collaborator

zm711 commented Sep 29, 2024

I think you'll need to explain the actual work flow you want. SpikeInterface Recording objects are also lazy when possible. If you want to sort data then it will need to be in a recording objects and some sorters require files to be written to disk to be used (for example KS1-3, MS5) so you'll have to write files anyway. If you want to do spike train analysis, we haven't gotten around to writing a SpikeInterface->Neo spiketrain function yet, but you could make it yourself by extracting the spike train information from a Sorting and then initializing your own Neo spiketrain. But without understanding what you specifically are hoping to do we can't really give you more specific tips.

@DerfelLee
Copy link
Author

We are trying to explore possible ways to use different packages in the pipeline. We have Blackrock NSX files and want to read them into Python using NEO to do some preprocessing and then do a small check using Elephant Visualization functions. After this, we want to put them into SpikeInterface for further analysis. It would be convenient for us to do this using one NEO format for the whole process because we saw both SpikeInterface and Elephant are based on NEO so we thought we could use the NEO format directly. But it seems like you don't have the function to convert between NEO and SpikeInterface data format, maybe I can use a Numpy array to transfer between them?

@zm711
Copy link
Collaborator

zm711 commented Sep 29, 2024

SpikeInterface has a variety of pre preprocessing so maybe you could do your preprocessing in spikeinterface?

Basically Neo has two formats/api's. There's the neo.io which works well with Elephant and then there is the neo.rawio which is basically numpy arrays (typically as memmaps so you can work with giant files) which is what SpikeInterface uses. So if you work with neo.rawio then it probably makes more sense to just use SpikeInterface directly. If you are using the neo.io format then things are trickier.

What preprocessing do you want to do? Maybe we could think of a way to still make this work. But my super simple way of thinking about this is:

Elephant <-> Neo.IO
SpikeInteface <- Neo.RawIO

@DerfelLee
Copy link
Author

DerfelLee commented Oct 5, 2024

Thanks! I think I'm going to use numpy arrays.

But I just ran into another issue when sorting the data. I'm testing the pipeline so I'm now just using 'simple' and 'tridesclous2' sorter.
Here's where things went wrong:

import spikeinterface as si
import spikeinterface.preprocessing as sp
import spikeinterface.widgets as sw
import spikeinterface.sorters as ss

# Create SI data
raw = si.NumpyRecording([all_segments], sampling_frequency=30000)

sw.plot_traces(raw, time_range=[0, 10])
filtered = sp.filter(raw, band=[250, 5000])
cmr = sp.common_reference(filtered, reference='global', operator='median')
sw.plot_traces(filtered, time_range=[0, 10])

sorter_name = 'simple'
sorting = ss.run_sorter(sorter_name=sorter_name, recording=cmr, folder=sorter_name, remove_existing_folder=True)

And the error message is:

---------------------------------------------------------------------------
SpikeSortingError                         Traceback (most recent call last)
Cell In[34], [line 2](vscode-notebook-cell:?execution_count=34&line=2)
      [1](vscode-notebook-cell:?execution_count=34&line=1) sorter_name = 'simple'
----> [2](vscode-notebook-cell:?execution_count=34&line=2) sorting = ss.run_sorter(sorter_name=sorter_name, recording=cmr, folder=sorter_name, remove_existing_folder=True)

File c:\Users\Lys-L\anaconda3\envs\NEOtest\Lib\site-packages\spikeinterface\sorters\runsorter.py:199, in run_sorter(sorter_name, recording, folder, remove_existing_folder, delete_output_folder, verbose, raise_error, docker_image, singularity_image, delete_container_files, with_output, output_folder, **sorter_params)
    [188](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:188)             raise RuntimeError(
    [189](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:189)                 "The python `spython` package must be installed to "
    [190](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:190)                 "run singularity. Install with `pip install spython`"
    [191](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:191)             )
    [193](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:193)     return run_sorter_container(
    [194](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:194)         container_image=container_image,
    [195](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:195)         mode=mode,
    [196](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:196)         **common_kwargs,
    [197](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:197)     )
--> [199](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:199) return run_sorter_local(**common_kwargs)

File c:\Users\Lys-L\anaconda3\envs\NEOtest\Lib\site-packages\spikeinterface\sorters\runsorter.py:261, in run_sorter_local(sorter_name, recording, folder, remove_existing_folder, delete_output_folder, verbose, raise_error, with_output, output_folder, **sorter_params)
    [259](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:259) SorterClass.setup_recording(recording, folder, verbose=verbose)
    [260](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:260) # This NEEDS to happen in the docker because of dependencies
--> [261](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:261) SorterClass.run_from_folder(folder, raise_error, verbose)
    [262](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:262) if with_output:
    [263](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/runsorter.py:263)     sorting = SorterClass.get_result_from_folder(folder, register_recording=True, sorting_info=True)

File c:\Users\Lys-L\anaconda3\envs\NEOtest\Lib\site-packages\spikeinterface\sorters\basesorter.py:301, in BaseSorter.run_from_folder(cls, output_folder, raise_error, verbose)
    [298](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/basesorter.py:298)         print(f"{sorter_name} run time {run_time:0.2f}s")
    [300](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/basesorter.py:300) if has_error and raise_error:
--> [301](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/basesorter.py:301)     raise SpikeSortingError(
    [302](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/basesorter.py:302)         f"Spike sorting error trace:\n{error_log_to_display}\n"
    [303](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/basesorter.py:303)         f"Spike sorting failed. You can inspect the runtime trace in {output_folder}/spikeinterface_log.json."
    [304](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/basesorter.py:304)     )
    [306](file:///C:/Users/Lys-L/anaconda3/envs/NEOtest/Lib/site-packages/spikeinterface/sorters/basesorter.py:306) return run_time

SpikeSortingError: Spike sorting error trace:
Traceback (most recent call last):
  File "c:\Users\Lys-L\anaconda3\envs\NEOtest\Lib\site-packages\spikeinterface\sorters\basesorter.py", line 261, in run_from_folder
    SorterClass._run_from_folder(sorter_output_folder, sorter_params, verbose)
  File "c:\Users\Lys-L\anaconda3\envs\NEOtest\Lib\site-packages\spikeinterface\sorters\internal\simplesorter.py", line 91, in _run_from_folder
    num_chans = recording_raw.get_num_channels()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'get_num_channels'

Spike sorting failed. You can inspect the runtime trace in c:\Users\Lys-L\OneDrive\桌面\NEOtest\simple/spikeinterface_log.json.

I tried to use the 'get_num_channels()' and it works. I don't know where things went wrong.
image

Thanks!

@zm711
Copy link
Collaborator

zm711 commented Oct 5, 2024

The NumpyRecording is not serializable to json so that breaks the code. The way the run_sorter works is it takes the recording you pass it and then writes the information into the folder you specify to maintain provenance. Then in the next step since it has written the "recipe" it reads that info back in. But since you used NumpyRecording this process of writing the provenance fails so this causes the read back in process to return None which causes everything to break. You should look inside the spikeinterface_recording.json file inside your folder to see the error. If you really want to keep the numpy arrays we can ping one of the other members of the team who might be able to suggest a work around, although I would still recommend just using the SpikeInterface object and just pulling out the traces with get_traces if you want to do Neo/Elephant stuff.

@DerfelLee
Copy link
Author

Thanks for replying! I do want to use Numpy so any suggestion would be really appreciated! Now, we are using something else to synchronize the event and neural signal, and map the electrodes, so we can get the right order and format for further analysis. Though it should also work if we modify the pipeline, I have to discuss it with team members. Therefore, it's good to know if there's any other way to use just Numpy arrays.

Thanks again!

@zm711
Copy link
Collaborator

zm711 commented Oct 7, 2024

@alejoe91 / @samuelgarcia

do you guys have a better way to do this then?

@h-mayorquin
Copy link
Collaborator

We need a better error for the case above, that should have surfaced way earlier.

You should save to binary before running the sorters and most of the analysis. That's basically numpy but on disk.

@zm711
Copy link
Collaborator

zm711 commented Oct 8, 2024

The idea was to try to do this without writing to disk, but I can't think of any way to do that. So I thought maybe someone else would have a trick for that. If we are able to write to disk then we go back to the original problem of it being better just to use the spikeinterface extractor to generate the correct recording format. I agree on the error though. I had to go through a simulated dataset to find it.

@h-mayorquin
Copy link
Collaborator

Back in the day, Sam wanted to explicitly prohibit that workflow in some places so I don't think there is a way around it.

@zm711
Copy link
Collaborator

zm711 commented Oct 15, 2024

I'll ping @samuelgarcia to respond and if he agrees it is still prohibited we can close this then.

@samuelgarcia
Copy link
Member

I think we should find a way to use sorter for in memory only object.
This will be impossible for almost all external sorters by design but for internal sorters this would be super uselfull.
Lets keep this mind.
Not sure to handle this very soon but this could be manageable.

Also lets have in mind that pure numpy recording can not have parralel access so when n_jobs>1 the numpy is copied which is worts than writing to disk and read it back.
The main target would be the use of SharedMemoryRecording.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question General question regarding SI
Projects
None yet
Development

No branches or pull requests

4 participants