Skip to content

Commit

Permalink
COnvert to sphinx gallery.
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeZiminski committed Nov 1, 2024
1 parent b3ccc31 commit 47b2c37
Show file tree
Hide file tree
Showing 183 changed files with 5,402 additions and 53 deletions.
114 changes: 78 additions & 36 deletions doc/how_to/handle_times.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ How SpikeInterface handles time
Extracellular electrophysiology commonly involves synchronisation of events
across many timestreams. For example, an experiment may involve
displaying a stimuli to an animal and recording the stimuli-evoked
neuronal responses. It is critical that timings is represented in
a clear way across data streams so they may be properly synchronised during
neuronal responses. It is critical that timings are represented
accurately so they may be properly synchronised during
analysis.

Below, we will explore the ways that SpikeInterface represents time
Below, we will explore the ways that SpikeInterface stores time
information and how you can use it in your analysis to ensure the timing
of your spike events is represented faithfully. The ways that will
be explored are **providing no times**, **providing start times**
and **providing the full time array**.
of your spike events are accurate.

A familiarity with the terms used in digital sampling (e.g. sampling
frequency) will be assumed below. If you are not familiar with these concepts,
Expand Down Expand Up @@ -56,22 +54,21 @@ An Overview of the possible Time representations in SpikeInterface

When you load a recording into SpikeInterface, it will be automatically
associated with a time array. Depending on your data format, this might
be loaded from metadata on your raw recording.
be loaded from metadata on your raw recording. If there is no time metadata
on your raw recording, the times will be generated based on your sampling
rate and number of samples.

**[TODO: concrete example of this?]**
**[TODO: concrete example of a datatype that loads time also on the neo side]**

If there is no time metadata on your raw recording, the times will be
generated based on your sampling rate and number of samples.

You can use the `get_times()` method to inspect the time array associated
with your recording.
You can use the :meth:`get_times() <spikeinterface.core.BaseRecording.get_times>`
method to inspect the time array associated with your recording.

.. code-block:: python
import spikeinterface.full as si
# Generate a recording for this example
recording, _ = si.generate_ground_truth_recording(durations=[10])
recording, sorting = si.generate_ground_truth_recording(durations=[10])
print(f"number of samples: {recording.get_num_samples()}")
print(f"sampling frequency: {recording.get_sampling_frequency()}"
Expand All @@ -80,22 +77,25 @@ with your recording.
recording.get_times()
)
Here, we see that as no time metadata was associated with the loaded recording,
the time array starts at 0 seconds and continues until 10 seconds
(`10 * sampling_frequency`) in steps of sampling step (`1 / sampling_frequency`).
Here, we see that as no time metadata is associated with the loaded recording,
a default time array is generated from the number of samples and sampling frequency.
The times starts at :math:`0` seconds and continues until :math:`10` seconds
(:math:`10 \cdot \text{sampling frequency}` samples) in steps of sampling step size,
:math:`\frac{1}{\text{sampling frequency}}`.
If timings were loaded from metadata, you may find that the first timepoint is
not zero, or the times may not be separated by exactly `1/sampling_frequency` but
may be irregular due to small drifts in sampling rate during acquisition.
If timings are obtained from metadata during file loading, you may find that the first timepoint is
not zero, or the times may not be separated by exactly :math:`\frac{1}{\text{sampling frequency}}`
but may be irregular due to small drifts in sampling rate during acquisition (so called 'clock drift').
^^^^^^^^^^^^^^^^^^^^^^^
Shifting the start time
^^^^^^^^^^^^^^^^^^^^^^^
Having loaded your recording object and inspected the associated
time vector, you may want to change the start time of your recording.
times, you may want to change the start time of your recording.
For example, your recording may not have metadata attached and you
want to shift the default time vector (with zero start time) to the
want to shift the default times to start at the
true (real world) start time of the recording, or relative to some
other event (e.g. behavioural trial start time).
Expand All @@ -117,19 +117,52 @@ the start time.
print(recording.get_times()) # time now start at 50 seconds
**TODO: link to new function and test when other PR is merged**
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Setting time vector changes spike times
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If we sort out recording, the spike times will reflect the times
set on the recording. In our case, because we already have the
sorting object based on the default times, we will set the new
recording object on the sorting.
.. code-block:: python
unit_id_to_show = sorting.unid_ids[0]
spike_times_orig = sorting.get_unit_spike_train(unit_id_to_show, return_times=True)
sorting.register_recording(recording)
spike_times_new = sorting.get_unit_spike_train(unit_id_to_show, return_times=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Manually setting a time vector
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Less commonly, you may want to manually set the time vector on a recording.
For example, maybe you have a known time vector with non-regularly spaced
samples due to sampling drift, and you want to associate it with your recording.
It is also possible to manualyl set an entire time vector on your recording.
This might be useful in case you have the true sample timestamps of your
recording but these were not automatically loaded from metadata.
You can associate any time vector with your recording (as long as it contains
as many samples as the recording itself) using `recording.set_times()`.
as many samples as the recording itself) using
:meth:`set_times() <spikeinterface.core.BaseRecording.set_times>`
[TODO - an example?]
.. code-block:: python
times = np.linspace(0, 10, recording.get_num_samples()))
offset = np.cumsum(
np.linspace(0, 0.1, recording.get_num_samples())
)
true_times = times + offset
recording.set_times(true_times)
recording.get_times()
.. warning::
Expand All @@ -142,8 +175,12 @@ as many samples as the recording itself) using `recording.set_times()`.
Retrieving timepoints from sample index
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SpikeInterface provides two convenience methods for obtaining the timepoint in seconds
given an index of the time array:
SpikeInterface provides two convenience methods for obtaining the
timepoint in seconds given an index of the time array.
Use
:meth:`time_to_sample_index() <spikeinterface.core.BaseRecording.time_to_sample_index>`
to go from time to the sample index:
.. code-block:: python
Expand All @@ -152,7 +189,9 @@ given an index of the time array:
print(sample_index)
Similarly, you can retrieve the time array index given a timepoint:
and
:meth:`sample_index_to_to_time() <spikeinterface.core.BaseRecording.sample_index_to_to_time>`
to can retrieve the index given a timepoint:
.. code-block:: python
Expand All @@ -166,9 +205,12 @@ Aligning events across timestreams
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The alignment of electrophysiology recording time to other data streams (e.g. behaviour)
is an important step in ephys analysis. To acheive this,it is common to collect
a synconrisation ('sync') pulse on an additional channel. At present SpikeInterface does not include
features for time-alignment, but some useful articles can be found on the following pages,
`SpikeGLX <https://github.com/billkarsh/SpikeGLX/blob/master/Markdown/UserManual.md#procedure-to-calibrate-sample-rates>`_,
`OpenEphys <https://open-ephys.github.io/gui-docs/Tutorials/Data-Synchronization.html>`_,
`NWB <https://neuroconv.readthedocs.io/en/main/user_guide/temporal_alignment.html>`_
is an important step in electrophysiology analysis. To achieve this,it is common to acquire
a synchronisation ('sync') pulse on an additional channel.
At present SpikeInterface does not include features for time-alignment,
but some useful articles on how to approach this can be found on the following pages:
* `SpikeGLX <https://github.com/billkarsh/SpikeGLX/blob/master/Markdown/UserManual.md#procedure-to-calibrate-sample-rates>`_,
* `OpenEphys <https://open-ephys.github.io/gui-docs/Tutorials/Data-Synchronization.html>`_,
* `NWB <https://neuroconv.readthedocs.io/en/main/user_guide/temporal_alignment.html>`_
40 changes: 23 additions & 17 deletions doc/sg_execution_times.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

Computation times
=================
**00:02.107** total execution time for 15 files **from all galleries**:
**00:01.439** total execution time for 17 files **from all galleries**:

.. container::

Expand All @@ -32,48 +32,54 @@ Computation times
* - Example
- Time
- Mem (MB)
* - :ref:`sphx_glr_tutorials_core_plot_4_sorting_analyzer.py` (``../examples/tutorials/core/plot_4_sorting_analyzer.py``)
- 00:02.107
* - :ref:`sphx_glr_tutorials_core_plot_4_sorting_analyzer.py` (``..\examples\tutorials\core\plot_4_sorting_analyzer.py``)
- 00:01.425
- 0.0
* - :ref:`sphx_glr_tutorials_comparison_plot_5_comparison_sorter_weaknesses.py` (``../examples/tutorials/comparison/plot_5_comparison_sorter_weaknesses.py``)
* - :ref:`sphx_glr_tutorials_core_plot_7_handle_times_new.py` (``..\examples\tutorials\core\plot_7_handle_times_new.py``)
- 00:00.013
- 0.0
* - :ref:`sphx_glr_tutorials_comparison_generate_erroneous_sorting.py` (``..\examples\tutorials\comparison\generate_erroneous_sorting.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_comparison_plot_5_comparison_sorter_weaknesses.py` (``..\examples\tutorials\comparison\plot_5_comparison_sorter_weaknesses.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_core_plot_1_recording_extractor.py` (``../examples/tutorials/core/plot_1_recording_extractor.py``)
* - :ref:`sphx_glr_tutorials_core_plot_1_recording_extractor.py` (``..\examples\tutorials\core\plot_1_recording_extractor.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_core_plot_2_sorting_extractor.py` (``../examples/tutorials/core/plot_2_sorting_extractor.py``)
* - :ref:`sphx_glr_tutorials_core_plot_2_sorting_extractor.py` (``..\examples\tutorials\core\plot_2_sorting_extractor.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_core_plot_3_handle_probe_info.py` (``../examples/tutorials/core/plot_3_handle_probe_info.py``)
* - :ref:`sphx_glr_tutorials_core_plot_3_handle_probe_info.py` (``..\examples\tutorials\core\plot_3_handle_probe_info.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_core_plot_5_append_concatenate_segments.py` (``../examples/tutorials/core/plot_5_append_concatenate_segments.py``)
* - :ref:`sphx_glr_tutorials_core_plot_5_append_concatenate_segments.py` (``..\examples\tutorials\core\plot_5_append_concatenate_segments.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_core_plot_6_handle_times.py` (``../examples/tutorials/core/plot_6_handle_times.py``)
* - :ref:`sphx_glr_tutorials_core_plot_6_handle_times.py` (``..\examples\tutorials\core\plot_6_handle_times.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_extractors_plot_1_read_various_formats.py` (``../examples/tutorials/extractors/plot_1_read_various_formats.py``)
* - :ref:`sphx_glr_tutorials_extractors_plot_1_read_various_formats.py` (``..\examples\tutorials\extractors\plot_1_read_various_formats.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_extractors_plot_2_working_with_unscaled_traces.py` (``../examples/tutorials/extractors/plot_2_working_with_unscaled_traces.py``)
* - :ref:`sphx_glr_tutorials_extractors_plot_2_working_with_unscaled_traces.py` (``..\examples\tutorials\extractors\plot_2_working_with_unscaled_traces.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_qualitymetrics_plot_3_quality_mertics.py` (``../examples/tutorials/qualitymetrics/plot_3_quality_mertics.py``)
* - :ref:`sphx_glr_tutorials_qualitymetrics_plot_3_quality_mertics.py` (``..\examples\tutorials\qualitymetrics\plot_3_quality_mertics.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_qualitymetrics_plot_4_curation.py` (``../examples/tutorials/qualitymetrics/plot_4_curation.py``)
* - :ref:`sphx_glr_tutorials_qualitymetrics_plot_4_curation.py` (``..\examples\tutorials\qualitymetrics\plot_4_curation.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_widgets_plot_1_rec_gallery.py` (``../examples/tutorials/widgets/plot_1_rec_gallery.py``)
* - :ref:`sphx_glr_tutorials_widgets_plot_1_rec_gallery.py` (``..\examples\tutorials\widgets\plot_1_rec_gallery.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_widgets_plot_2_sort_gallery.py` (``../examples/tutorials/widgets/plot_2_sort_gallery.py``)
* - :ref:`sphx_glr_tutorials_widgets_plot_2_sort_gallery.py` (``..\examples\tutorials\widgets\plot_2_sort_gallery.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_widgets_plot_3_waveforms_gallery.py` (``../examples/tutorials/widgets/plot_3_waveforms_gallery.py``)
* - :ref:`sphx_glr_tutorials_widgets_plot_3_waveforms_gallery.py` (``..\examples\tutorials\widgets\plot_3_waveforms_gallery.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_tutorials_widgets_plot_4_peaks_gallery.py` (``../examples/tutorials/widgets/plot_4_peaks_gallery.py``)
* - :ref:`sphx_glr_tutorials_widgets_plot_4_peaks_gallery.py` (``..\examples\tutorials\widgets\plot_4_peaks_gallery.py``)
- 00:00.000
- 0.0
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 47b2c37

Please sign in to comment.