@@ -4,15 +4,13 @@ How SpikeInterface handles time
4
4
Extracellular electrophysiology commonly involves synchronisation of events
5
5
across many timestreams. For example, an experiment may involve
6
6
displaying 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
9
9
analysis.
10
10
11
- Below, we will explore the ways that SpikeInterface represents time
11
+ Below, we will explore the ways that SpikeInterface stores time
12
12
information 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.
16
14
17
15
A familiarity with the terms used in digital sampling (e.g. sampling
18
16
frequency) 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
56
54
57
55
When you load a recording into SpikeInterface, it will be automatically
58
56
associated 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.
60
60
61
- **[TODO: concrete example of this? ] **
61
+ **[TODO: concrete example of a datatype that loads time also on the neo side ] **
62
62
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.
68
65
69
66
.. code-block :: python
70
67
71
68
import spikeinterface.full as si
72
69
73
70
# 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 ])
75
72
76
73
print (f " number of samples: { recording.get_num_samples()} " )
77
74
print (f " sampling frequency: { recording.get_sampling_frequency()} "
@@ -80,22 +77,25 @@ with your recording.
80
77
recording.get_times()
81
78
)
82
79
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}}`.
86
85
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 ' ) .
90
89
91
90
^^^^^^^^^^^^^^^^^^^^^^^
92
91
Shifting the start time
93
92
^^^^^^^^^^^^^^^^^^^^^^^
94
93
95
94
Having 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
+
97
97
For 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
99
99
true (real world) start time of the recording, or relative to some
100
100
other event (e.g. behavioural trial start time).
101
101
@@ -117,19 +117,52 @@ the start time.
117
117
118
118
print (recording.get_times()) # time now start at 50 seconds
119
119
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
+
120
142
121
143
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
122
144
Manually setting a time vector
123
145
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
124
146
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 .
128
150
129
151
You 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> `
131
154
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()
133
166
134
167
.. warning::
135
168
@@ -142,8 +175,12 @@ as many samples as the recording itself) using `recording.set_times()`.
142
175
Retrieving timepoints from sample index
143
176
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
144
177
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:
147
184
148
185
.. code- block:: python
149
186
@@ -152,7 +189,9 @@ given an index of the time array:
152
189
print (sample_index)
153
190
154
191
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:
156
195
157
196
158
197
.. code- block:: python
@@ -166,9 +205,12 @@ Aligning events across timestreams
166
205
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
167
206
168
207
The 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