Add read_nwb_sorting_analyzer: build a SortingAnalyzer from an NWB Units table - #4645
h-mayorquin wants to merge 46 commits into
Conversation
…load_analyzer_from_nwb
…erface into load_analyzer_from_nwb
# Conflicts: # src/spikeinterface/extractors/nwbextractors.py
for more information, see https://pre-commit.ci
…eberto' into load_analyzer_nwb_heberto
|
This is failing on |
…ikeinterface into load_analyzer_nwb_heberto
Fixed in last couple of commits |
# Conflicts: # src/spikeinterface/extractors/nwbextractors.py
|
Thanks for fixing the multi-group bug and the random-spikes handling. I built on that and fixed a few more things on the branch:
Two behavior changes to flag: with a |
|
NeurodataWithoutBorders/pynwb#2162 It was on the code (as a comment but not here) |
|
This is impressive. |
| print("Could not load recording, proceeding without it") | ||
| recording = None | ||
|
|
||
| t_start_tmp = 0 if t_start is None else t_start |
There was a problem hiding this comment.
| t_start_tmp = 0 if t_start is None else t_start | |
| if electrical_series_path is None: | |
| t_start_tmp = 0 if t_start is None else t_start | |
| else: | |
| t_start_tmp = None |
You can't pass an electical_series_path and a t_start. Something like this?
| if extension_map is not None: | ||
| resolved_extension_map.update(extension_map) | ||
| # try to read recording object to get the analyzer | ||
| try: |
There was a problem hiding this comment.
This try/except tricked me for a bit. I wasn't passing an electrical_series_path so this recording load was failing silently, then there were confusing downstream bugs. Not sure the solution.
|
I was trying this out on non-IBL datasets and I think this code makes quite a lot of assumptions about how the data is structured. E.g. all datasets I tried do not store an electrode index in their units table. I've not managed to load anything non-IBL - I failed to load any Allen datasets, but maybe @alejoe91 knows a good one to try. So I think the current function name is over promising, and we should change it to be more IBL focused. Or maybe shelve this for 0.105 and work to try and make it more general. |
|
Thanks for trying this, @alejoe91 I am gonna do another pass on Thursday maybe it can make it for release : ) |
|
Thanks @h-mayorquin will check this out today / tomorrow! I am curious on Chris' message and the NWB provenance. Does this load NWB files when they strictly adhere to the NWB format as described here? And the issue is that in many cases data is stored in NWB files but if not according to the exact schema? |
read_nwb_sorting_analyzerbuilds a curatableSortingAnalyzerdirectly from an NWB (Neurodata Without Borders) Units table, populating each extension from what the file already stores rather than recomputing it. When the Units table haswaveform_mean, the analyzer is built recordingless from those stored templates, plus the per-unit metrics and theelectrodesregion for sparsity; when the file has an accessible ElectricalSeries, it is used as the recording. It mirrorsread_kilosort_as_analyzerin structure, injecting the templates, quality and template metrics, sparsity, and random_spikes extensions from the file's contents. This supersedes the earlier draft #4270.The reads are deliberate: Units columns are classified from metadata and only the templates, the
electrodesregion, and the scalar metric/label columns are materialized, while the large per-spike ragged columns (spike times, amplitudes, depths) are never touched at build. The sorting is kept lazy so its spike times are read only on demand, which builds on #4662 (single bulk-read NWB spike vector) and thecopy_sortingoption (#4668); the recordingless case builds a lightweight placeholder recording from the electroderel_x/rel_ygeometry (viagenerate_ground_truth_recording, #4588) purely to carry probe geometry into the standard constructor, then drops it. Together these keep the build small and memory-light regardless of file size.Because the reads are deliberate and the sorting stays lazy, the function works the same whether the file is local or streamed (
stream_modeis passed through to the extractors). That makes the streamed case cheap: as a check, building from a real IBL (International Brain Laboratory) processed file on dandiset 000409, session6713a4a7-faed-4df2-acab-ee4e63326f8d(898 units, 20.7M spikes), produced a curatable analyzer in about 14 s while transferring only ~22 MB, with the ~130 MB spike read deferred until a spike-based view needs it. This PR is the reader itself; a separate how-to PR will cover the streaming-from-DANDI workflow in depth. It depends on #4662 and thecopy_sortingPR (#4668), which should merge first so this branch rebases down to just the reader.