+
+ +
+

Loading Spike Waveforms

+
+

Loading the spike sorting and average waveforms

+
+
[2]:
+
+
+
%%capture
+from one.api import ONE
+from brainbox.io.one import SpikeSortingLoader
+
+one = ONE(base_url='https://openalyx.internationalbrainlab.org')
+
+pid = 'da8dfec1-d265-44e8-84ce-6ae9c109b8bd'
+
+# Load in the spikesorting
+ssl = SpikeSortingLoader(pid=pid, one=one)
+spikes, clusters, channels = ssl.load_spike_sorting(revision='2024-05-06')
+clusters = ssl.merge_clusters(spikes, clusters, channels)
+waveforms = ssl.load_spike_sorting_object('waveforms')
+


+
+
+
+
+

Displaying a few average waveforms

+
+
[3]:
+
+
+
import matplotlib.pyplot as plt
+import numpy as np
+import ibldsp.waveforms
+from ibl_style.style import figure_style
+figure_style()
+
+cids = np.random.choice(np.where(clusters['label'] == 1)[0], 3)
+fig, axs = plt.subplots(1, 3, figsize=(12, 4))
+for i, cid in enumerate(cids):
+    wf = waveforms['templates'][cid, :, :]
+    ax = ibldsp.waveforms.double_wiggle(wf * 1e6 / 80, fs=30_000, ax=axs[i])
+    ax.set(title=f'Cluster {cid}')
+
+
+
+
+
+
+
+../_images/loading_examples_loading_spike_waveforms_5_0.png +
+
+
+
+

More details

+

During spike sorting, pre-processing operations are performed on the voltage data to create the average templates. Although those templates are useful for clustering, they may not be the best description of the neural activity. As such we extract average waveforms for each cluster using a different pre-processing. Details are provided on the spike sorting white paper on +figshare. * Description of datasets

+
+
+

Exploring the raw waveforms

+

For each unit, we compiled up to raw data 256 waveforms chosen randomly from the entire recording. The pre-processing steps included rephasing of the channels, low-cut filtering, bad channel detection and common-average referencing.

+

To perform the loading we will use a convenience.

+

Warning, this will download a few Gigabytes of data to your computer !

+
+

Compute average waveform for cluster

+

Here we will load data from one cluster belonging to the striatum and compute the average waveform.

+

We will do so for several stack orders: 1, 2, 4, 8, 16, 32, 64, 128 and display the resulting waveform, while tracking the signal-to-noise ratio (SNR) for each stack order.

+
+
[7]:
+
+
+
from ibldsp.utils import rms
+# instantiating the waveform loader will download the raw waveform arrays and can take a few minutes (~3Gb)
+wfl = ssl.raw_waveforms()
+ic = np.where(np.logical_and(clusters['acronym'] == 'LSr', clusters['bitwise_fail'] == 0))[0]
+# look at templates
+
+raw_wav, info, channel_map = wfl.load_waveforms(labels=ic[12])
+
+snr = np.zeros(8)
+fig, axs = plt.subplots(2, 4, figsize=(10, 6))
+for i, ax in enumerate(axs.flat):
+    w_stack = np.mean(raw_wav[0, :(2 ** i), :, :], axis=0)
+    ax = ibldsp.waveforms.double_wiggle( w_stack  * 1e6 / 80, fs=30_000, ax=axs.flatten()[i])
+    ax.set_title(f"Stack {2 ** i}")
+    snr[i] = 20 * np.log10(rms(w_stack[19:22, wfl.trough_offset - 10:wfl.trough_offset + 10].flatten()) / np.mean(rms(w_stack)))
+
+
+
+
+
+
+
+
+100%|██████████| 12/12.0 [00:03<00:00,  3.00it/s]
+
+
+
+
+
+
+../_images/loading_examples_loading_spike_waveforms_9_1.png +
+
+

For constant gaussian noise in n repeated experiments, we expect the SNR to scale proportionally to the square root of n. In decibels, this corresponds to 3dB / octave. Let’s see how the data compares to the prediction:

+
+
[5]:
+
+
+
fig, ax = plt.subplots(figsize=(6, 4))
+ax.plot(np.arange(8), snr, '*', label='SNR estimation')
+ax.plot(np.arange(8), np.arange(8) * 3, label='SNR predicted')
+ax.set(xlabel='Stack index (log2(N))', ylabel='SNR (dB)')
+ax.legend()
+
+
+
+
+
Out[5]:
+
+
+
+
+<matplotlib.legend.Legend at 0x7b68b9b9a6d0>
+
+
+
+
+
+
+../_images/loading_examples_loading_spike_waveforms_11_1.png +
+
+
+
+
+ + +
+