From f55a9405040310064f716015f8d9b0c976b97923 Mon Sep 17 00:00:00 2001 From: JoeZiminski Date: Mon, 28 Oct 2024 14:28:05 +0000 Subject: [PATCH 01/15] Add 'shift start time' function. --- src/spikeinterface/core/baserecording.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/spikeinterface/core/baserecording.py b/src/spikeinterface/core/baserecording.py index 5e2e9e4014..b8a0420794 100644 --- a/src/spikeinterface/core/baserecording.py +++ b/src/spikeinterface/core/baserecording.py @@ -509,6 +509,26 @@ def reset_times(self): rs.t_start = None rs.sampling_frequency = self.sampling_frequency + def shift_start_time(self, shift, segment_index=None): + """ + Shift the starting time of the times. + + shift : int | float + The shift to apply to the first time point. If positive, + the current start time will be increased by `shift`. If + negative, the start time will be decreased. + + segment_index : int | None + The segment on which to shift the times. + """ + segment_index = self._check_segment_index(segment_index) + rs = self._recording_segments[segment_index] + + if self.has_time_vector(): + rs.time_vector += shift + else: + rs.t_start += shift + def sample_index_to_time(self, sample_ind, segment_index=None): """ Transform sample index into time in seconds From f34da1aff682828dfba78cd17034c0fc2cb40fda Mon Sep 17 00:00:00 2001 From: JoeZiminski Date: Fri, 1 Nov 2024 14:03:13 +0000 Subject: [PATCH 02/15] Make new index page with hover CSS. --- doc/_static/css/custom.css | 20 +++ doc/conf.py | 6 +- doc/index.rst | 1 + doc/tutorials_custom_index.rst | 254 +++++++++++++++++++++++++++++++++ 4 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 doc/_static/css/custom.css create mode 100644 doc/tutorials_custom_index.rst diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css new file mode 100644 index 0000000000..0c51da539e --- /dev/null +++ b/doc/_static/css/custom.css @@ -0,0 +1,20 @@ +/* Center and make the title bold */ +.gallery-card .grid-item-card-title { + text-align: center; + font-weight: bold; +} + +/* Default style for hover content (hidden) */ +.gallery-card .hover-content { + display: none; + text-align: center; +} + +/* Show the hover content when hovering over the card */ +.gallery-card:hover .default-title { + display: none; +} + +.gallery-card:hover .hover-content { + display: block; +} diff --git a/doc/conf.py b/doc/conf.py index e3d58ca8f2..db16269991 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -109,8 +109,10 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - +html_static_path = ['_static'] +html_css_files = [ + 'css/custom.css', +] html_favicon = "images/favicon-32x32.png" diff --git a/doc/index.rst b/doc/index.rst index ed443e4200..57a0c95443 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -51,6 +51,7 @@ SpikeInterface is made of several modules to deal with different aspects of the overview get_started/index + tutorials_custom_index tutorials/index how_to/index modules/index diff --git a/doc/tutorials_custom_index.rst b/doc/tutorials_custom_index.rst new file mode 100644 index 0000000000..46a7bea630 --- /dev/null +++ b/doc/tutorials_custom_index.rst @@ -0,0 +1,254 @@ +.. This page provides a custom index to the 'Tutorials' page, rather than the default sphinx-gallery +.. generated page. The benefits of this are flexibility in design and inclusion of non-sphinx files in the index. +.. +.. To update this index with a new documentation page +.. 1) Copy the grid-item-card and associated ".. raw:: html" section. +.. 2) change :link: to a link to your page. If this is an `.rst` file, point to the rst file directly. +.. If it is a sphinx-gallery generated file, format the path as separated by underscore and prefix `sphx_glr`, +.. pointing to the .py file. e.g. `tutorials/my/page.py` -> `sphx_glr_tutorials_my_page.py +.. 3) Change :img-top: to point to the thumbnail image of your choosing. You can point to images generated +.. in the sphinx gallery page if you wish. +.. 4) In the `html` section, change the `default-title` to your pages title and `hover-content` to the subtitle. + +:orphan: + +TutorialsNew +============ + +Longer form tutorials about using SpikeInterface. Many of these are downloadable as notebooks or Python scripts so that you can "code along" with the tutorials. + +If you're new to SpikeInterface, we recommend trying out the :ref:`get_started/quickstart:Quickstart tutorial` first. + +Updating from legacy +-------------------- + +.. toctree:: + :maxdepth: 1 + + tutorials/waveform_extractor_to_sorting_analyzer + +Core tutorials +-------------- + +These tutorials focus on the :py:mod:`spikeinterface.core` module. + +.. grid:: 1 2 2 3 + :gutter: 2 + + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_core_plot_1_recording_extractor.py + :img-top: /tutorials/core/images/thumb/sphx_glr_plot_1_recording_extractor_thumb.png + :img-alt: Recording objects + :class-card: gallery-card + + .. raw:: html + +
Recording objects
+
Manage loaded recordings in SpikeInterface
+ + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_core_plot_2_sorting_extractor.py + :img-top: /tutorials/core/images/thumb/sphx_glr_plot_2_sorting_extractor_thumb.png + :img-alt: Sorting objects + :class-card: gallery-card + + .. raw:: html + +
Sorting objects
+
Explore sorting extractor features
+ + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_core_plot_3_handle_probe_info.py + :img-top: /tutorials/core/images/thumb/sphx_glr_plot_3_handle_probe_info_thumb.png + :img-alt: Handling probe information + :class-card: gallery-card + + .. raw:: html + +
Handling probe information
+
Handle and visualize probe information
+ + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_core_plot_4_sorting_analyzer.py + :img-top: /tutorials/core/images/thumb/sphx_glr_plot_4_sorting_analyzer_thumb.png + :img-alt: SortingAnalyzer + :class-card: gallery-card + + .. raw:: html + +
SortingAnalyzer
+
Analyze sorting results with ease
+ + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_core_plot_5_append_concatenate_segments.py + :img-top: /tutorials/core/images/thumb/sphx_glr_plot_5_append_concatenate_segments_thumb.png + :img-alt: Append/Concatenate segments + :class-card: gallery-card + + .. raw:: html + +
Append and/or concatenate segments
+
Combine segments efficiently
+ + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_core_plot_6_handle_times.py + :img-top: /tutorials/core/images/thumb/sphx_glr_plot_6_handle_times_thumb.png + :img-alt: Handle time information + :class-card: gallery-card + + .. raw:: html + +
Handle time information
+
Manage and analyze time information
+ +Extractors tutorials +-------------------- + +The :py:mod:`spikeinterface.extractors` module is designed to load and save recorded and sorted data, and to handle probe information. + +.. grid:: 1 2 2 3 + :gutter: 2 + + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_extractors_plot_1_read_various_formats.py + :img-top: /tutorials/extractors/images/thumb/sphx_glr_plot_1_read_various_formats_thumb.png + :img-alt: Read various formats + :class-card: gallery-card + + .. raw:: html + +
Read various formats
+
Read different recording formats efficiently
+ + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_extractors_plot_2_working_with_unscaled_traces.py + :img-top: /tutorials/extractors/images/thumb/sphx_glr_plot_2_working_with_unscaled_traces_thumb.png + :img-alt: Unscaled traces + :class-card: gallery-card + + .. raw:: html + +
Working with unscaled traces
+
Learn about managing unscaled traces
+ +Quality metrics tutorial +------------------------ + +The :code:`spikeinterface.qualitymetrics` module allows users to compute various quality metrics to assess the goodness of a spike sorting output. + +.. grid:: 1 2 2 3 + :gutter: 2 + + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_qualitymetrics_plot_3_quality_mertics.py + :img-top: /tutorials/qualitymetrics/images/thumb/sphx_glr_plot_3_quality_mertics_thumb.png + :img-alt: Quality Metrics + :class-card: gallery-card + + .. raw:: html + +
Quality Metrics
+
Evaluate sorting quality using metrics
+ + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_qualitymetrics_plot_4_curation.py + :img-top: /tutorials/qualitymetrics/images/thumb/sphx_glr_plot_4_curation_thumb.png + :img-alt: Curation Tutorial + :class-card: gallery-card + + .. raw:: html + +
Curation Tutorial
+
Learn how to curate spike sorting data
+ +Comparison tutorial +------------------- + +The :code:`spikeinterface.comparison` module allows you to compare sorter outputs or benchmark against ground truth. + +.. grid:: 1 2 2 3 + :gutter: 2 + + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_comparison_plot_5_comparison_sorter_weaknesses.py + :img-top: /tutorials/comparison/images/thumb/sphx_glr_plot_5_comparison_sorter_weaknesses_thumb.png + :img-alt: Sorter Comparison + :class-card: gallery-card + + .. raw:: html + +
Sorter Comparison
+
Compare sorter outputs and assess weaknesses
+ +Widgets tutorials +----------------- + +The :code:`widgets` module contains several plotting routines (widgets) for visualizing recordings, sorting data, probe layout, and more. + +.. grid:: 1 2 2 3 + :gutter: 2 + + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_widgets_plot_1_rec_gallery.py + :img-top: /tutorials/widgets/images/thumb/sphx_glr_plot_1_rec_gallery_thumb.png + :img-alt: Recording Widgets + :class-card: gallery-card + + .. raw:: html + +
RecordingExtractor Widgets
+
Visualize recordings with widgets
+ + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_widgets_plot_2_sort_gallery.py + :img-top: /tutorials/widgets/images/thumb/sphx_glr_plot_2_sort_gallery_thumb.png + :img-alt: Sorting Widgets + :class-card: gallery-card + + .. raw:: html + +
SortingExtractor Widgets
+
Explore sorting data using widgets
+ + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_widgets_plot_3_waveforms_gallery.py + :img-top: /tutorials/widgets/images/thumb/sphx_glr_plot_3_waveforms_gallery_thumb.png + :img-alt: Waveforms Widgets + :class-card: gallery-card + + .. raw:: html + +
Waveforms Widgets
+
Display waveforms using SpikeInterface
+ + .. grid-item-card:: + :link-type: ref + :link: sphx_glr_tutorials_widgets_plot_4_peaks_gallery.py + :img-top: /tutorials/widgets/images/thumb/sphx_glr_plot_4_peaks_gallery_thumb.png + :img-alt: Peaks Widgets + :class-card: gallery-card + + .. raw:: html + +
Peaks Widgets
+
Visualize detected peaks
+ +Download All Examples +--------------------- + +- :download:`Download all examples in Python source code ` +- :download:`Download all examples in Jupyter notebooks ` From 7aa93490cca20916338629518800a1cbf976b8ff Mon Sep 17 00:00:00 2001 From: JoeZiminski Date: Fri, 1 Nov 2024 14:53:22 +0000 Subject: [PATCH 03/15] Remove CSS and update development docs. --- doc/_static/css/custom.css | 20 ----- doc/conf.py | 6 +- doc/development/development.rst | 19 +++++ doc/index.rst | 1 - doc/tutorials_custom_index.rst | 128 +++++++++----------------------- 5 files changed, 56 insertions(+), 118 deletions(-) delete mode 100644 doc/_static/css/custom.css diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css deleted file mode 100644 index 0c51da539e..0000000000 --- a/doc/_static/css/custom.css +++ /dev/null @@ -1,20 +0,0 @@ -/* Center and make the title bold */ -.gallery-card .grid-item-card-title { - text-align: center; - font-weight: bold; -} - -/* Default style for hover content (hidden) */ -.gallery-card .hover-content { - display: none; - text-align: center; -} - -/* Show the hover content when hovering over the card */ -.gallery-card:hover .default-title { - display: none; -} - -.gallery-card:hover .hover-content { - display: block; -} diff --git a/doc/conf.py b/doc/conf.py index db16269991..e3d58ca8f2 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -109,10 +109,8 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] -html_css_files = [ - 'css/custom.css', -] +# html_static_path = ['_static'] + html_favicon = "images/favicon-32x32.png" diff --git a/doc/development/development.rst b/doc/development/development.rst index a91818a271..1638c41243 100644 --- a/doc/development/development.rst +++ b/doc/development/development.rst @@ -213,6 +213,25 @@ We use Sphinx to build the documentation. To build the documentation locally, yo This will build the documentation in the :code:`doc/_build/html` folder. You can open the :code:`index.html` file in your browser to see the documentation. +Adding new documentation +------------------------ + +Documentation can be added as a +`sphinx-gallery `_ +python file ('tutorials') +or a +`sphinx rst `_ +file (all other sections). + +To add a new tutorial, add your ``.py`` file to ``spikeinterface/examples``. +Then, update the ``spikeinterface/doc/tutorials_custom_index.rst`` file +to make a new card linking to the page and an optional image. See +``tutorials_custom_index.rst`` header for more information. + +For other sections, write your documentation in ``.rst`` format and add +the page to the appropriate ``index.rst`` file found in the relevant +folder (e.g. ``how_to/index.rst``). + How to run code coverage locally -------------------------------- To run code coverage locally, you can use the following command: diff --git a/doc/index.rst b/doc/index.rst index 57a0c95443..e6d8aa3fea 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -52,7 +52,6 @@ SpikeInterface is made of several modules to deal with different aspects of the overview get_started/index tutorials_custom_index - tutorials/index how_to/index modules/index api diff --git a/doc/tutorials_custom_index.rst b/doc/tutorials_custom_index.rst index 46a7bea630..4c7625d811 100644 --- a/doc/tutorials_custom_index.rst +++ b/doc/tutorials_custom_index.rst @@ -12,12 +12,14 @@ :orphan: -TutorialsNew +Tutorials ============ -Longer form tutorials about using SpikeInterface. Many of these are downloadable as notebooks or Python scripts so that you can "code along" with the tutorials. +Longer form tutorials about using SpikeInterface. Many of these are downloadable +as notebooks or Python scripts so that you can "code along" with the tutorials. -If you're new to SpikeInterface, we recommend trying out the :ref:`get_started/quickstart:Quickstart tutorial` first. +If you're new to SpikeInterface, we recommend trying out the +:ref:`get_started/quickstart:Quickstart tutorial` first. Updating from legacy -------------------- @@ -35,77 +37,53 @@ These tutorials focus on the :py:mod:`spikeinterface.core` module. .. grid:: 1 2 2 3 :gutter: 2 - .. grid-item-card:: + .. grid-item-card:: Recording objects :link-type: ref :link: sphx_glr_tutorials_core_plot_1_recording_extractor.py :img-top: /tutorials/core/images/thumb/sphx_glr_plot_1_recording_extractor_thumb.png :img-alt: Recording objects :class-card: gallery-card + :text-align: center - .. raw:: html - -
Recording objects
-
Manage loaded recordings in SpikeInterface
- - .. grid-item-card:: + .. grid-item-card:: Sorting objects :link-type: ref :link: sphx_glr_tutorials_core_plot_2_sorting_extractor.py :img-top: /tutorials/core/images/thumb/sphx_glr_plot_2_sorting_extractor_thumb.png :img-alt: Sorting objects :class-card: gallery-card + :text-align: center - .. raw:: html - -
Sorting objects
-
Explore sorting extractor features
- - .. grid-item-card:: + .. grid-item-card:: Handling probe information :link-type: ref :link: sphx_glr_tutorials_core_plot_3_handle_probe_info.py :img-top: /tutorials/core/images/thumb/sphx_glr_plot_3_handle_probe_info_thumb.png :img-alt: Handling probe information :class-card: gallery-card + :text-align: center - .. raw:: html - -
Handling probe information
-
Handle and visualize probe information
- - .. grid-item-card:: + .. grid-item-card:: SortingAnalyzer :link-type: ref :link: sphx_glr_tutorials_core_plot_4_sorting_analyzer.py :img-top: /tutorials/core/images/thumb/sphx_glr_plot_4_sorting_analyzer_thumb.png :img-alt: SortingAnalyzer :class-card: gallery-card + :text-align: center - .. raw:: html - -
SortingAnalyzer
-
Analyze sorting results with ease
- - .. grid-item-card:: + .. grid-item-card:: Append and/or concatenate segments :link-type: ref :link: sphx_glr_tutorials_core_plot_5_append_concatenate_segments.py :img-top: /tutorials/core/images/thumb/sphx_glr_plot_5_append_concatenate_segments_thumb.png :img-alt: Append/Concatenate segments :class-card: gallery-card + :text-align: center - .. raw:: html - -
Append and/or concatenate segments
-
Combine segments efficiently
- - .. grid-item-card:: + .. grid-item-card:: Handle time information :link-type: ref :link: sphx_glr_tutorials_core_plot_6_handle_times.py :img-top: /tutorials/core/images/thumb/sphx_glr_plot_6_handle_times_thumb.png :img-alt: Handle time information :class-card: gallery-card - - .. raw:: html - -
Handle time information
-
Manage and analyze time information
+ :text-align: center Extractors tutorials -------------------- @@ -115,29 +93,21 @@ The :py:mod:`spikeinterface.extractors` module is designed to load and save reco .. grid:: 1 2 2 3 :gutter: 2 - .. grid-item-card:: + .. grid-item-card:: Read various formats :link-type: ref :link: sphx_glr_tutorials_extractors_plot_1_read_various_formats.py :img-top: /tutorials/extractors/images/thumb/sphx_glr_plot_1_read_various_formats_thumb.png :img-alt: Read various formats :class-card: gallery-card + :text-align: center - .. raw:: html - -
Read various formats
-
Read different recording formats efficiently
- - .. grid-item-card:: + .. grid-item-card:: Working with unscaled traces :link-type: ref :link: sphx_glr_tutorials_extractors_plot_2_working_with_unscaled_traces.py :img-top: /tutorials/extractors/images/thumb/sphx_glr_plot_2_working_with_unscaled_traces_thumb.png :img-alt: Unscaled traces :class-card: gallery-card - - .. raw:: html - -
Working with unscaled traces
-
Learn about managing unscaled traces
+ :text-align: center Quality metrics tutorial ------------------------ @@ -147,29 +117,21 @@ The :code:`spikeinterface.qualitymetrics` module allows users to compute various .. grid:: 1 2 2 3 :gutter: 2 - .. grid-item-card:: + .. grid-item-card:: Quality Metrics :link-type: ref :link: sphx_glr_tutorials_qualitymetrics_plot_3_quality_mertics.py :img-top: /tutorials/qualitymetrics/images/thumb/sphx_glr_plot_3_quality_mertics_thumb.png :img-alt: Quality Metrics :class-card: gallery-card + :text-align: center - .. raw:: html - -
Quality Metrics
-
Evaluate sorting quality using metrics
- - .. grid-item-card:: + .. grid-item-card:: Curation Tutorial :link-type: ref :link: sphx_glr_tutorials_qualitymetrics_plot_4_curation.py :img-top: /tutorials/qualitymetrics/images/thumb/sphx_glr_plot_4_curation_thumb.png :img-alt: Curation Tutorial :class-card: gallery-card - - .. raw:: html - -
Curation Tutorial
-
Learn how to curate spike sorting data
+ :text-align: center Comparison tutorial ------------------- @@ -179,17 +141,13 @@ The :code:`spikeinterface.comparison` module allows you to compare sorter output .. grid:: 1 2 2 3 :gutter: 2 - .. grid-item-card:: + .. grid-item-card:: Sorter Comparison :link-type: ref :link: sphx_glr_tutorials_comparison_plot_5_comparison_sorter_weaknesses.py :img-top: /tutorials/comparison/images/thumb/sphx_glr_plot_5_comparison_sorter_weaknesses_thumb.png :img-alt: Sorter Comparison :class-card: gallery-card - - .. raw:: html - -
Sorter Comparison
-
Compare sorter outputs and assess weaknesses
+ :text-align: center Widgets tutorials ----------------- @@ -199,53 +157,37 @@ The :code:`widgets` module contains several plotting routines (widgets) for visu .. grid:: 1 2 2 3 :gutter: 2 - .. grid-item-card:: + .. grid-item-card:: RecordingExtractor Widgets :link-type: ref :link: sphx_glr_tutorials_widgets_plot_1_rec_gallery.py :img-top: /tutorials/widgets/images/thumb/sphx_glr_plot_1_rec_gallery_thumb.png :img-alt: Recording Widgets :class-card: gallery-card + :text-align: center - .. raw:: html - -
RecordingExtractor Widgets
-
Visualize recordings with widgets
- - .. grid-item-card:: + .. grid-item-card:: SortingExtractor Widgets :link-type: ref :link: sphx_glr_tutorials_widgets_plot_2_sort_gallery.py :img-top: /tutorials/widgets/images/thumb/sphx_glr_plot_2_sort_gallery_thumb.png :img-alt: Sorting Widgets :class-card: gallery-card + :text-align: center - .. raw:: html - -
SortingExtractor Widgets
-
Explore sorting data using widgets
- - .. grid-item-card:: + .. grid-item-card:: Waveforms Widgets :link-type: ref :link: sphx_glr_tutorials_widgets_plot_3_waveforms_gallery.py :img-top: /tutorials/widgets/images/thumb/sphx_glr_plot_3_waveforms_gallery_thumb.png :img-alt: Waveforms Widgets :class-card: gallery-card + :text-align: center - .. raw:: html - -
Waveforms Widgets
-
Display waveforms using SpikeInterface
- - .. grid-item-card:: + .. grid-item-card:: Peaks Widgets :link-type: ref :link: sphx_glr_tutorials_widgets_plot_4_peaks_gallery.py :img-top: /tutorials/widgets/images/thumb/sphx_glr_plot_4_peaks_gallery_thumb.png :img-alt: Peaks Widgets :class-card: gallery-card - - .. raw:: html - -
Peaks Widgets
-
Visualize detected peaks
+ :text-align: center Download All Examples --------------------- From d6b4c1e7474c372c6d9f71787ddbe707854bd11f Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 7 Nov 2024 11:44:13 +0100 Subject: [PATCH 04/15] Fix cbin_file_path --- src/spikeinterface/extractors/cbin_ibl.py | 30 +++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/spikeinterface/extractors/cbin_ibl.py b/src/spikeinterface/extractors/cbin_ibl.py index d7e5b58e11..88e1029ab0 100644 --- a/src/spikeinterface/extractors/cbin_ibl.py +++ b/src/spikeinterface/extractors/cbin_ibl.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +import warnings import numpy as np import probeinterface @@ -30,8 +31,10 @@ class CompressedBinaryIblExtractor(BaseRecording): stream_name : {"ap", "lp"}, default: "ap". Whether to load AP or LFP band, one of "ap" or "lp". - cbin_file : str or None, default None + cbin_file_path : str or None, default None The cbin file of the recording. If None, searches in `folder_path` for file. + cbin_file : str or None, default None + (deprecated) The cbin file of the recording. If None, searches in `folder_path` for file. Returns ------- @@ -41,14 +44,21 @@ class CompressedBinaryIblExtractor(BaseRecording): installation_mesg = "To use the CompressedBinaryIblExtractor, install mtscomp: \n\n pip install mtscomp\n\n" - def __init__(self, folder_path=None, load_sync_channel=False, stream_name="ap", cbin_file=None): + def __init__( + self, folder_path=None, load_sync_channel=False, stream_name="ap", cbin_file_path=None, cbin_file=None + ): from neo.rawio.spikeglxrawio import read_meta_file try: import mtscomp except ImportError: raise ImportError(self.installation_mesg) - if cbin_file is None: + if cbin_file is not None: + warnings.warn( + "The `cbin_file` argument is deprecated, please use `cbin_file_path` instead", DeprecationWarning + ) + cbin_file_path = cbin_file + if cbin_file_path is None: folder_path = Path(folder_path) # check bands assert stream_name in ["ap", "lp"], "stream_name must be one of: 'ap', 'lp'" @@ -60,17 +70,17 @@ def __init__(self, folder_path=None, load_sync_channel=False, stream_name="ap", assert ( len(curr_cbin_files) == 1 ), f"There should only be one `*.cbin` file in the folder, but {print(curr_cbin_files)} have been found" - cbin_file = curr_cbin_files[0] + cbin_file_path = curr_cbin_files[0] else: - cbin_file = Path(cbin_file) - folder_path = cbin_file.parent + cbin_file_path = Path(cbin_file_path) + folder_path = cbin_file_path.parent - ch_file = cbin_file.with_suffix(".ch") - meta_file = cbin_file.with_suffix(".meta") + ch_file = cbin_file_path.with_suffix(".ch") + meta_file = cbin_file_path.with_suffix(".meta") # reader cbuffer = mtscomp.Reader() - cbuffer.open(cbin_file, ch_file) + cbuffer.open(cbin_file_path, ch_file) # meta data meta = read_meta_file(meta_file) @@ -119,7 +129,7 @@ def __init__(self, folder_path=None, load_sync_channel=False, stream_name="ap", self._kwargs = { "folder_path": str(Path(folder_path).resolve()), "load_sync_channel": load_sync_channel, - "cbin_file": str(Path(cbin_file).resolve()), + "cbin_file_path": str(Path(cbin_file_path).resolve()), } From e6f45056852e181fb8d6909c8a3365a08cb2c8f5 Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Thu, 7 Nov 2024 15:56:48 +0100 Subject: [PATCH 05/15] Update src/spikeinterface/extractors/cbin_ibl.py --- src/spikeinterface/extractors/cbin_ibl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spikeinterface/extractors/cbin_ibl.py b/src/spikeinterface/extractors/cbin_ibl.py index 88e1029ab0..357afde04e 100644 --- a/src/spikeinterface/extractors/cbin_ibl.py +++ b/src/spikeinterface/extractors/cbin_ibl.py @@ -55,7 +55,7 @@ def __init__( raise ImportError(self.installation_mesg) if cbin_file is not None: warnings.warn( - "The `cbin_file` argument is deprecated, please use `cbin_file_path` instead", DeprecationWarning + "The `cbin_file` argument is deprecated, please use `cbin_file_path` instead", DeprecationWarning, stacklevel=2 ) cbin_file_path = cbin_file if cbin_file_path is None: From 471ce724faac7245766538880b7fcd196f49fa30 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 14:57:14 +0000 Subject: [PATCH 06/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/spikeinterface/extractors/cbin_ibl.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/spikeinterface/extractors/cbin_ibl.py b/src/spikeinterface/extractors/cbin_ibl.py index 357afde04e..8fe19f3d7e 100644 --- a/src/spikeinterface/extractors/cbin_ibl.py +++ b/src/spikeinterface/extractors/cbin_ibl.py @@ -55,7 +55,9 @@ def __init__( raise ImportError(self.installation_mesg) if cbin_file is not None: warnings.warn( - "The `cbin_file` argument is deprecated, please use `cbin_file_path` instead", DeprecationWarning, stacklevel=2 + "The `cbin_file` argument is deprecated, please use `cbin_file_path` instead", + DeprecationWarning, + stacklevel=2, ) cbin_file_path = cbin_file if cbin_file_path is None: From 620f8013b8bf4f1332a7802dd3f6804ce068493c Mon Sep 17 00:00:00 2001 From: JoeZiminski Date: Wed, 13 Nov 2024 13:37:50 +0000 Subject: [PATCH 07/15] Apply to all segments if 'segment_index' is 'None'. --- src/spikeinterface/core/baserecording.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/spikeinterface/core/baserecording.py b/src/spikeinterface/core/baserecording.py index b8a0420794..7392caa69b 100644 --- a/src/spikeinterface/core/baserecording.py +++ b/src/spikeinterface/core/baserecording.py @@ -521,13 +521,20 @@ def shift_start_time(self, shift, segment_index=None): segment_index : int | None The segment on which to shift the times. """ - segment_index = self._check_segment_index(segment_index) - rs = self._recording_segments[segment_index] + self._check_segment_index(segment_index) - if self.has_time_vector(): - rs.time_vector += shift + if segment_index is None: + segments_to_shift = range(self.get_num_segments()) else: - rs.t_start += shift + segments_to_shift = (segment_index,) + + for idx in segments_to_shift: + rs = self._recording_segments[idx] + + if self.has_time_vector(): + rs.time_vector += shift + else: + rs.t_start += shift def sample_index_to_time(self, sample_ind, segment_index=None): """ From 22d5dfc2a552e00d7b55d7c28681e25a1f51a711 Mon Sep 17 00:00:00 2001 From: JoeZiminski Date: Wed, 13 Nov 2024 13:39:34 +0000 Subject: [PATCH 08/15] Add type hints. --- src/spikeinterface/core/baserecording.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/spikeinterface/core/baserecording.py b/src/spikeinterface/core/baserecording.py index 7392caa69b..0af9c4bb6a 100644 --- a/src/spikeinterface/core/baserecording.py +++ b/src/spikeinterface/core/baserecording.py @@ -509,7 +509,7 @@ def reset_times(self): rs.t_start = None rs.sampling_frequency = self.sampling_frequency - def shift_start_time(self, shift, segment_index=None): + def shift_start_time(self, shift: int | float, segment_index: int | None = None) -> None: """ Shift the starting time of the times. @@ -536,15 +536,14 @@ def shift_start_time(self, shift, segment_index=None): else: rs.t_start += shift - def sample_index_to_time(self, sample_ind, segment_index=None): - """ - Transform sample index into time in seconds - """ + def sample_index_to_time(self, sample_ind: int, segment_index: int | None = None): + """ """ segment_index = self._check_segment_index(segment_index) rs = self._recording_segments[segment_index] return rs.sample_index_to_time(sample_ind) - def time_to_sample_index(self, time_s, segment_index=None): + def time_to_sample_index(self, time_s: float, segment_index: int | None = None): + """ """ segment_index = self._check_segment_index(segment_index) rs = self._recording_segments[segment_index] return rs.time_to_sample_index(time_s) From 458a3dcc201380740583ef1f075951e83ee77ed8 Mon Sep 17 00:00:00 2001 From: JoeZiminski Date: Wed, 13 Nov 2024 13:43:45 +0000 Subject: [PATCH 09/15] Update name and docstring. --- src/spikeinterface/core/baserecording.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/spikeinterface/core/baserecording.py b/src/spikeinterface/core/baserecording.py index 0af9c4bb6a..91f99f17b0 100644 --- a/src/spikeinterface/core/baserecording.py +++ b/src/spikeinterface/core/baserecording.py @@ -509,19 +509,24 @@ def reset_times(self): rs.t_start = None rs.sampling_frequency = self.sampling_frequency - def shift_start_time(self, shift: int | float, segment_index: int | None = None) -> None: + def shift_times(self, shift: int | float, segment_index: int | None = None) -> None: """ - Shift the starting time of the times. + Shift all times by a scalar value. The default behaviour is to + shift all segments uniformly. + Parameters + ---------- shift : int | float - The shift to apply to the first time point. If positive, - the current start time will be increased by `shift`. If - negative, the start time will be decreased. + The shift to apply. If positive, times will be increased by `shift`. + e.g. shifting by 1 will be like the recording started 1 second later. + If negative, the start time will be decreased i.e. as if the recording + started earlier. segment_index : int | None - The segment on which to shift the times. + The segment on which to shift the times. if `None`, all + segments will be shifted. """ - self._check_segment_index(segment_index) + self._check_segment_index(segment_index) # Check the segment index is valid only if segment_index is None: segments_to_shift = range(self.get_num_segments()) From 8845d3d7eb6caad8c6a5f0c12842f480766d3a26 Mon Sep 17 00:00:00 2001 From: chrishalcrow <57948917+chrishalcrow@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:40:16 +0000 Subject: [PATCH 10/15] Add verbose kwarg to mda write_recording --- src/spikeinterface/extractors/mdaextractors.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/spikeinterface/extractors/mdaextractors.py b/src/spikeinterface/extractors/mdaextractors.py index f055e1d7c9..d2886d9e79 100644 --- a/src/spikeinterface/extractors/mdaextractors.py +++ b/src/spikeinterface/extractors/mdaextractors.py @@ -72,6 +72,7 @@ def write_recording( params_fname="params.json", geom_fname="geom.csv", dtype=None, + verbose=False, **job_kwargs, ): """Write a recording to file in MDA format. @@ -93,6 +94,8 @@ def write_recording( File name of geom file dtype : dtype or None, default: None Data type to be used. If None dtype is same as recording traces. + verbose : bool + If True, shows progress bar when saving recording. **job_kwargs: Use by job_tools modules to set: @@ -130,6 +133,7 @@ def write_recording( dtype=dtype, byte_offset=header_size, add_file_extension=False, + verbose=verbose, **job_kwargs, ) From 3e98c670a27671590613b7c1c4118780a8c47ce8 Mon Sep 17 00:00:00 2001 From: JoeZiminski Date: Wed, 13 Nov 2024 18:32:48 +0000 Subject: [PATCH 11/15] Add tests. --- .../core/tests/test_time_handling.py | 92 ++++++++++++++++++- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/src/spikeinterface/core/tests/test_time_handling.py b/src/spikeinterface/core/tests/test_time_handling.py index a129316ee7..9b7ed11bbb 100644 --- a/src/spikeinterface/core/tests/test_time_handling.py +++ b/src/spikeinterface/core/tests/test_time_handling.py @@ -15,7 +15,10 @@ class TestTimeHandling: is generated on the fly. Both time representations are tested here. """ - # Fixtures ##### + # ######################################################################### + # Fixtures + # ######################################################################### + @pytest.fixture(scope="session") def time_vector_recording(self): """ @@ -95,7 +98,10 @@ def _get_fixture_data(self, request, fixture_name): raw_recording, times_recording, all_times = time_recording_fixture return (raw_recording, times_recording, all_times) - # Tests ##### + # ######################################################################### + # Tests + # ######################################################################### + def test_has_time_vector(self, time_vector_recording): """ Test the `has_time_vector` function returns `False` before @@ -305,7 +311,87 @@ def test_sorting_analyzer_get_durations_no_recording(self, time_vector_recording assert np.array_equal(sorting_analyzer.get_total_duration(), raw_recording.get_total_duration()) - # Helpers #### + @pytest.mark.parametrize("fixture_name", ["time_vector_recording", "t_start_recording"]) + @pytest.mark.parametrize("shift", [-123.456, 123.456]) + def test_shift_time_all_segments(self, request, fixture_name, shift): + """ + Shift the times in every segment using the `None` default, then + check that every segment of the recording is shifted as expected. + """ + _, times_recording, all_times = self._get_fixture_data(request, fixture_name) + + num_segments, orig_seg_data = self._store_all_times(times_recording) + + times_recording.shift_times(shift) # use default `segment_index=None` + + for idx in range(num_segments): + assert np.allclose( + orig_seg_data[idx], times_recording.get_times(segment_index=idx) - shift, rtol=0, atol=1e-8 + ) + + @pytest.mark.parametrize("fixture_name", ["time_vector_recording", "t_start_recording"]) + @pytest.mark.parametrize("shift", [-123.456, 123.456]) + def test_shift_times_different_segments(self, request, fixture_name, shift): + """ + Shift each segment separately, and check the shifted segment only + is shifted as expected. + """ + _, times_recording, all_times = self._get_fixture_data(request, fixture_name) + + num_segments, orig_seg_data = self._store_all_times(times_recording) + + # For each segment, shift the segment only and check the + # times are updated as expected. + for idx in range(num_segments): + + scaler = idx + 2 + times_recording.shift_times(shift * scaler, segment_index=idx) + + assert np.allclose( + orig_seg_data[idx], times_recording.get_times(segment_index=idx) - shift * scaler, rtol=0, atol=1e-8 + ) + + # Just do a little check that we are not + # accidentally changing some other segments, + # which should remain unchanged at this point in the loop. + if idx != num_segments - 1: + assert np.array_equal(orig_seg_data[idx + 1], times_recording.get_times(segment_index=idx + 1)) + + @pytest.mark.parametrize("fixture_name", ["time_vector_recording", "t_start_recording"]) + def test_save_and_load_time_shift(self, request, fixture_name, tmp_path): + """ + Save the shifted data and check the shift is propagated correctly. + """ + _, times_recording, all_times = self._get_fixture_data(request, fixture_name) + + shift = 100 + times_recording.shift_times(shift=shift) + + times_recording.save(folder=tmp_path / "my_file") + + loaded_recording = si.load_extractor(tmp_path / "my_file") + + for idx in range(times_recording.get_num_segments()): + assert np.array_equal( + times_recording.get_times(segment_index=idx), loaded_recording.get_times(segment_index=idx) + ) + + def _store_all_times(self, recording): + """ + Convenience function to store original times of all segments to a dict. + """ + num_segments = recording.get_num_segments() + seg_data = {} + + for idx in range(num_segments): + seg_data[idx] = copy.deepcopy(recording.get_times(segment_index=idx)) + + return num_segments, seg_data + + # ######################################################################### + # Helpers + # ######################################################################### + def _check_times_match(self, recording, all_times): """ For every segment in a recording, check the `get_times()` From 4d7246a529e3d17747cf5a496a0a04bd97f4eb09 Mon Sep 17 00:00:00 2001 From: JoeZiminski Date: Wed, 13 Nov 2024 18:33:17 +0000 Subject: [PATCH 12/15] Fixes on shift function. --- src/spikeinterface/core/baserecording.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/spikeinterface/core/baserecording.py b/src/spikeinterface/core/baserecording.py index 91f99f17b0..4b545dc7c7 100644 --- a/src/spikeinterface/core/baserecording.py +++ b/src/spikeinterface/core/baserecording.py @@ -526,8 +526,6 @@ def shift_times(self, shift: int | float, segment_index: int | None = None) -> N The segment on which to shift the times. if `None`, all segments will be shifted. """ - self._check_segment_index(segment_index) # Check the segment index is valid only - if segment_index is None: segments_to_shift = range(self.get_num_segments()) else: @@ -536,7 +534,7 @@ def shift_times(self, shift: int | float, segment_index: int | None = None) -> N for idx in segments_to_shift: rs = self._recording_segments[idx] - if self.has_time_vector(): + if self.has_time_vector(segment_index=idx): rs.time_vector += shift else: rs.t_start += shift From a1cf3367d18a549281208b25c622f2a1ee773226 Mon Sep 17 00:00:00 2001 From: JoeZiminski Date: Wed, 13 Nov 2024 18:35:32 +0000 Subject: [PATCH 13/15] Undo out of scope changes. --- src/spikeinterface/core/baserecording.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/spikeinterface/core/baserecording.py b/src/spikeinterface/core/baserecording.py index 4b545dc7c7..886f7db79f 100644 --- a/src/spikeinterface/core/baserecording.py +++ b/src/spikeinterface/core/baserecording.py @@ -539,14 +539,15 @@ def shift_times(self, shift: int | float, segment_index: int | None = None) -> N else: rs.t_start += shift - def sample_index_to_time(self, sample_ind: int, segment_index: int | None = None): - """ """ + def sample_index_to_time(self, sample_ind, segment_index=None): + """ + Transform sample index into time in seconds + """ segment_index = self._check_segment_index(segment_index) rs = self._recording_segments[segment_index] return rs.sample_index_to_time(sample_ind) - def time_to_sample_index(self, time_s: float, segment_index: int | None = None): - """ """ + def time_to_sample_index(self, time_s, segment_index=None): segment_index = self._check_segment_index(segment_index) rs = self._recording_segments[segment_index] return rs.time_to_sample_index(time_s) From 469b3b0e36fdbc0571d37e100d99d6c741af1377 Mon Sep 17 00:00:00 2001 From: JoeZiminski Date: Wed, 13 Nov 2024 18:37:20 +0000 Subject: [PATCH 14/15] Fix docstring. --- src/spikeinterface/core/baserecording.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/spikeinterface/core/baserecording.py b/src/spikeinterface/core/baserecording.py index 886f7db79f..6d9d2a827f 100644 --- a/src/spikeinterface/core/baserecording.py +++ b/src/spikeinterface/core/baserecording.py @@ -511,8 +511,7 @@ def reset_times(self): def shift_times(self, shift: int | float, segment_index: int | None = None) -> None: """ - Shift all times by a scalar value. The default behaviour is to - shift all segments uniformly. + Shift all times by a scalar value. Parameters ---------- @@ -523,8 +522,8 @@ def shift_times(self, shift: int | float, segment_index: int | None = None) -> N started earlier. segment_index : int | None - The segment on which to shift the times. if `None`, all - segments will be shifted. + The segment on which to shift the times. + If `None`, all segments will be shifted. """ if segment_index is None: segments_to_shift = range(self.get_num_segments()) From 1e53a5e06b2a90956d72150826a8f590d673b5ce Mon Sep 17 00:00:00 2001 From: Alessio Buccino Date: Tue, 19 Nov 2024 17:45:40 +0100 Subject: [PATCH 15/15] Update src/spikeinterface/extractors/cbin_ibl.py Co-authored-by: Heberto Mayorquin --- src/spikeinterface/extractors/cbin_ibl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spikeinterface/extractors/cbin_ibl.py b/src/spikeinterface/extractors/cbin_ibl.py index 8fe19f3d7e..728d352973 100644 --- a/src/spikeinterface/extractors/cbin_ibl.py +++ b/src/spikeinterface/extractors/cbin_ibl.py @@ -31,7 +31,7 @@ class CompressedBinaryIblExtractor(BaseRecording): stream_name : {"ap", "lp"}, default: "ap". Whether to load AP or LFP band, one of "ap" or "lp". - cbin_file_path : str or None, default None + cbin_file_path : str, Path or None, default None The cbin file of the recording. If None, searches in `folder_path` for file. cbin_file : str or None, default None (deprecated) The cbin file of the recording. If None, searches in `folder_path` for file.