@@ -4,15 +4,13 @@ How SpikeInterface handles time
44Extracellular electrophysiology commonly involves synchronisation of events
55across many timestreams. For example, an experiment may involve
66displaying a stimuli to an animal and recording the stimuli-evoked
7- neuronal responses. It is critical that timings is represented in
8- a clear way across data streams so they may be properly synchronised during
7+ neuronal responses. It is critical that timings are represented
8+ accurately so they may be properly synchronised during
99analysis.
1010
11- Below, we will explore the ways that SpikeInterface represents time
11+ Below, we will explore the ways that SpikeInterface stores time
1212information and how you can use it in your analysis to ensure the timing
13- of your spike events is represented faithfully. The ways that will
14- be explored are **providing no times **, **providing start times **
15- and **providing the full time array **.
13+ of your spike events are accurate.
1614
1715A familiarity with the terms used in digital sampling (e.g. sampling
1816frequency) will be assumed below. If you are not familiar with these concepts,
@@ -56,22 +54,21 @@ An Overview of the possible Time representations in SpikeInterface
5654
5755When you load a recording into SpikeInterface, it will be automatically
5856associated with a time array. Depending on your data format, this might
59- be loaded from metadata on your raw recording.
57+ be loaded from metadata on your raw recording. If there is no time metadata
58+ on your raw recording, the times will be generated based on your sampling
59+ rate and number of samples.
6060
61- **[TODO: concrete example of this? ] **
61+ **[TODO: concrete example of a datatype that loads time also on the neo side ] **
6262
63- If there is no time metadata on your raw recording, the times will be
64- generated based on your sampling rate and number of samples.
65-
66- You can use the `get_times() ` method to inspect the time array associated
67- with your recording.
63+ You can use the :meth: `get_times() <spikeinterface.core.BaseRecording.get_times> `
64+ method to inspect the time array associated with your recording.
6865
6966.. code-block :: python
7067
7168 import spikeinterface.full as si
7269
7370 # Generate a recording for this example
74- recording, _ = si.generate_ground_truth_recording(durations = [10 ])
71+ recording, sorting = si.generate_ground_truth_recording(durations = [10 ])
7572
7673 print (f " number of samples: { recording.get_num_samples()} " )
7774 print (f " sampling frequency: { recording.get_sampling_frequency()} "
@@ -80,22 +77,25 @@ with your recording.
8077 recording.get_times()
8178 )
8279
83- Here, we see that as no time metadata was associated with the loaded recording,
84- the time array starts at 0 seconds and continues until 10 seconds
85- (`10 * sampling_frequency` ) in steps of sampling step (`1 / sampling_frequency` ).
80+ Here, we see that as no time metadata is associated with the loaded recording,
81+ a default time array is generated from the number of samples and sampling frequency.
82+ The times starts at :math:`0 ` seconds and continues until :math:`10 ` seconds
83+ (:math:`10 \cdot \text{sampling frequency}` samples) in steps of sampling step size,
84+ :math:` \frac{1}{\text{sampling frequency}}`.
8685
87- If timings were loaded from metadata, you may find that the first timepoint is
88- not zero, or the times may not be separated by exactly ` 1 / sampling_frequency` but
89- may be irregular due to small drifts in sampling rate during acquisition.
86+ If timings are obtained from metadata during file loading , you may find that the first timepoint is
87+ not zero, or the times may not be separated by exactly :math: `\ frac{1}{\text{sampling frequency}}`
88+ but may be irregular due to small drifts in sampling rate during acquisition (so called ' clock drift ' ) .
9089
9190^^^^^^^^^^^^^^^^^^^^^^^
9291Shifting the start time
9392^^^^^^^^^^^^^^^^^^^^^^^
9493
9594Having loaded your recording object and inspected the associated
96- time vector, you may want to change the start time of your recording.
95+ times, you may want to change the start time of your recording.
96+
9797For example, your recording may not have metadata attached and you
98- want to shift the default time vector ( with zero start time) to the
98+ want to shift the default times to start at the
9999true (real world) start time of the recording, or relative to some
100100other event (e.g. behavioural trial start time).
101101
@@ -117,19 +117,52 @@ the start time.
117117
118118 print (recording.get_times()) # time now start at 50 seconds
119119
120+ ** TODO : link to new function and test when other PR is merged**
121+
122+
123+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
124+ Setting time vector changes spike times
125+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
126+
127+ If we sort out recording, the spike times will reflect the times
128+ set on the recording. In our case, because we already have the
129+ sorting object based on the default times, we will set the new
130+ recording object on the sorting.
131+
132+ .. code- block:: python
133+
134+ unit_id_to_show = sorting.unid_ids[0 ]
135+
136+ spike_times_orig = sorting.get_unit_spike_train(unit_id_to_show, return_times = True )
137+
138+ sorting.register_recording(recording)
139+
140+ spike_times_new = sorting.get_unit_spike_train(unit_id_to_show, return_times = True )
141+
120142
121143^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
122144Manually setting a time vector
123145^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
124146
125- Less commonly, you may want to manually set the time vector on a recording.
126- For example, maybe you have a known time vector with non - regularly spaced
127- samples due to sampling drift, and you want to associate it with your recording .
147+ It is also possible to manualyl set an entire time vector on your recording.
148+ This might be useful in case you have the true sample timestamps of your
149+ recording but these were not automatically loaded from metadata .
128150
129151You can associate any time vector with your recording (as long as it contains
130- as many samples as the recording itself) using `recording.set_times()` .
152+ as many samples as the recording itself) using
153+ :meth:`set_times() < spikeinterface.core.BaseRecording.set_times> `
131154
132- [TODO - an example? ]
155+ .. code- block:: python
156+
157+ times = np.linspace(0 , 10 , recording.get_num_samples()))
158+ offset = np.cumsum(
159+ np.linspace(0 , 0.1 , recording.get_num_samples())
160+ )
161+ true_times = times + offset
162+
163+ recording.set_times(true_times)
164+
165+ recording.get_times()
133166
134167.. warning::
135168
@@ -142,8 +175,12 @@ as many samples as the recording itself) using `recording.set_times()`.
142175Retrieving timepoints from sample index
143176^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
144177
145- SpikeInterface provides two convenience methods for obtaining the timepoint in seconds
146- given an index of the time array:
178+ SpikeInterface provides two convenience methods for obtaining the
179+ timepoint in seconds given an index of the time array.
180+
181+ Use
182+ :meth:`time_to_sample_index() < spikeinterface.core.BaseRecording.time_to_sample_index> `
183+ to go from time to the sample index:
147184
148185.. code- block:: python
149186
@@ -152,7 +189,9 @@ given an index of the time array:
152189 print (sample_index)
153190
154191
155- Similarly, you can retrieve the time array index given a timepoint:
192+ and
193+ :meth:`sample_index_to_to_time() < spikeinterface.core.BaseRecording.sample_index_to_to_time> `
194+ to can retrieve the index given a timepoint:
156195
157196
158197.. code- block:: python
@@ -166,9 +205,12 @@ Aligning events across timestreams
166205^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
167206
168207The alignment of electrophysiology recording time to other data streams (e.g. behaviour)
169- is an important step in ephys analysis. To acheive this,it is common to collect
170- a synconrisation (' sync' ) pulse on an additional channel. At present SpikeInterface does not include
171- features for time- alignment, but some useful articles can be found on the following pages,
172- `SpikeGLX < https:// github.com/ billkarsh/ SpikeGLX/ blob/ master/ Markdown/ UserManual.md# procedure-to-calibrate-sample-rates>`_,
173- `OpenEphys < https:// open - ephys.github.io/ gui- docs/ Tutorials/ Data- Synchronization.html> ` _,
174- `NWB < https:// neuroconv.readthedocs.io/ en/ main/ user_guide/ temporal_alignment.html> ` _
208+ is an important step in electrophysiology analysis. To achieve this,it is common to acquire
209+ a synchronisation (' sync' ) pulse on an additional channel.
210+
211+ At present SpikeInterface does not include features for time- alignment,
212+ but some useful articles on how to approach this can be found on the following pages:
213+
214+ * `SpikeGLX < https:// github.com/ billkarsh/ SpikeGLX/ blob/ master/ Markdown/ UserManual.md# procedure-to-calibrate-sample-rates>`_,
215+ * `OpenEphys < https:// open - ephys.github.io/ gui- docs/ Tutorials/ Data- Synchronization.html> ` _,
216+ * `NWB < https:// neuroconv.readthedocs.io/ en/ main/ user_guide/ temporal_alignment.html> ` _
0 commit comments