CSC to learn LFP spiking atoms#

Here, we show how CSC can be used to learn spiking atoms from Local Field Potential (LFP) data [1].

[1] Hitziger, Sebastian, et al.

Adaptive Waveform Learning: A Framework for Modeling Variability in Neurophysiological Signals. IEEE Transactions on Signal Processing (2017).

First, let us fetch the data (~14 MB)

import os
from mne.datasets import fetch_dataset
from mne.utils import get_config

url = ('https://github.com/hitziger/AWL/raw/master/Experiments/data/'
       'LFP_data_contiguous_1250_Hz.mat')

folder_name = "LFP"
archive_name = "LFP_data_contiguous_1250_Hz.mat"

fname = fetch_dataset(
    {"dataset_name": "LFP_data",
     "url": url,
     "archive_name": archive_name,
     "folder_name": folder_name,
     "hash": None
     },
    path=None,
    force_update=False
)

fname = os.path.join(fname, archive_name)
Using default location ~/mne_data for LFP_data...
Creating /github/home/mne_data
Downloading file 'LFP_data_contiguous_1250_Hz.mat' from 'https://github.com/hitziger/AWL/raw/master/Experiments/data/LFP_data_contiguous_1250_Hz.mat' to '/github/home/mne_data/LFP'.

  0%|                                              | 0.00/14.4M [00:00<?, ?B/s]
 48%|█████████████████▋                   | 6.90M/14.4M [00:00<00:00, 69.0MB/s]
  0%|                                              | 0.00/14.4M [00:00<?, ?B/s]
100%|█████████████████████████████████████| 14.4M/14.4M [00:00<00:00, 82.2GB/s]
Download complete in 00s (13.8 MB)

It is a mat file, so we use scipy to load it

from scipy import io

data = io.loadmat(fname)
X, sfreq = data['X'].T, float(data['sfreq'])
/github/workspace/examples/csc/plot_lfp_data.py:44: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
  X, sfreq = data['X'].T, float(data['sfreq'])

And now let us look at the data

import numpy as np
import matplotlib.pyplot as plt

start, stop = 11000, 15000
times = np.arange(start, stop) / sfreq
plt.plot(times, X[0, start:stop], color='b')
plt.xlabel('Time (s)')
plt.ylabel(r'$\mu$ V')
plt.xlim([9., 12.])
plot lfp data
(9.0, 12.0)

and filter it using a convenient function from MNE. This will remove low frequency drifts, but we keep the high frequencies

from mne.filter import filter_data
X = filter_data(
    X.astype(np.float64), sfreq, l_freq=1, h_freq=None, fir_design='firwin')
Setting up high-pass filter at 1 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal highpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 1.00
- Lower transition bandwidth: 1.00 Hz (-6 dB cutoff frequency: 0.50 Hz)
- Filter length: 4125 samples (3.300 s)

Now, we define the parameters of our model.

reg = 6.0
n_times = 2500
n_times_atom = 350
n_trials = 100
n_atoms = 3
n_iter = 60

Let’s stick to one random state for now, but if you want to learn how to select the random state, consult this example.

Now, we epoch the trials

overlap = 0
starts = np.arange(0, X.shape[1] - n_times, n_times - overlap)
stops = np.arange(n_times, X.shape[1], n_times - overlap)

X_new = []
for idx, (start, stop) in enumerate(zip(starts, stops)):
    if idx >= n_trials:
        break
    X_new.append(X[0, start:stop])
X_new = np.vstack(X_new)
del X

We remove the mean and scale to unit variance.

The convolutions can result in edge artifacts at the edges of the trials. Therefore, we discount the contributions from the edges by windowing the trials.

from numpy import hamming
X_new *= hamming(n_times)[None, :]

Of course, in a data-limited setting we want to use as much of the data as possible. If this is the case, you can set overlap to non-zero (for example half the epoch length).

Now, we run regular CSC since the trials are not too noisy

Coordinate descent loop 0 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 51134.32096508
Using method dual for projection
[seed 10] Objective (d) 45299.80903125
Coordinate descent loop 1 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 26399.47853871
Using method dual for projection
[seed 10] Objective (d) 25948.80915409
Coordinate descent loop 2 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 23777.89064427
Using method dual for projection
[seed 10] Objective (d) 23704.90593344
Coordinate descent loop 3 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 22233.60227587
Using method dual for projection
[seed 10] Objective (d) 22221.01870097
Coordinate descent loop 4 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 21202.43876006
Using method dual for projection
[seed 10] Objective (d) 21169.49202496
Coordinate descent loop 5 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 20579.63611160
Using method dual for projection
[seed 10] Objective (d) 20618.98107838
Coordinate descent loop 6 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 20283.55110757
Using method dual for projection
[seed 10] Objective (d) 20210.75292119
Coordinate descent loop 7 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 19971.53116238
Using method dual for projection
[seed 10] Objective (d) 19970.52325693
Coordinate descent loop 8 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 19708.44554814
Using method dual for projection
[seed 10] Objective (d) 19691.20196937
Coordinate descent loop 9 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 19462.43966049
Using method dual for projection
[seed 10] Objective (d) 19467.01239142
Coordinate descent loop 10 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 19233.45092821
Using method dual for projection
[seed 10] Objective (d) 19214.12766228
Coordinate descent loop 11 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 19085.64469638
Using method dual for projection
[seed 10] Objective (d) 19084.13170902
Coordinate descent loop 12 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18945.61954180
Using method dual for projection
[seed 10] Objective (d) 18980.31578132
Coordinate descent loop 13 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18876.43985523
Using method dual for projection
[seed 10] Objective (d) 18836.38353365
Coordinate descent loop 14 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18720.74584343
Using method dual for projection
[seed 10] Objective (d) 18721.54845148
Coordinate descent loop 15 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18623.64002130
Using method dual for projection
[seed 10] Objective (d) 18621.18904483
Coordinate descent loop 16 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18547.81611675
Using method dual for projection
[seed 10] Objective (d) 18545.24281147
Coordinate descent loop 17 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18471.88752074
Using method dual for projection
[seed 10] Objective (d) 18470.42055100
Coordinate descent loop 18 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18393.84202337
Using method dual for projection
[seed 10] Objective (d) 18391.78226335
Coordinate descent loop 19 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18331.73169078
Using method dual for projection
[seed 10] Objective (d) 18328.84451721
Coordinate descent loop 20 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18274.15271778
Using method dual for projection
[seed 10] Objective (d) 18297.40702027
Coordinate descent loop 21 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18257.44912778
Using method dual for projection
[seed 10] Objective (d) 18276.17809040
Coordinate descent loop 22 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18243.49274502
Using method dual for projection
[seed 10] Objective (d) 18261.00618884
Coordinate descent loop 23 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18234.36110469
Using method dual for projection
[seed 10] Objective (d) 18208.94505739
Coordinate descent loop 24 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18179.04646179
Using method dual for projection
[seed 10] Objective (d) 18179.60670548
Coordinate descent loop 25 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18156.84098690
Using method dual for projection
[seed 10] Objective (d) 18166.58627364
Coordinate descent loop 26 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18145.85002188
Using method dual for projection
[seed 10] Objective (d) 18152.93056892
Coordinate descent loop 27 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18131.29820871
Using method dual for projection
[seed 10] Objective (d) 18131.16932915
Coordinate descent loop 28 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18119.63496984
Using method dual for projection
[seed 10] Objective (d) 18111.53151557
Coordinate descent loop 29 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18095.94338304
Using method dual for projection
[seed 10] Objective (d) 18106.50710835
Coordinate descent loop 30 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18098.05242539
Using method dual for projection
[seed 10] Objective (d) 18097.82996143
Coordinate descent loop 31 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18084.15739396
Using method dual for projection
[seed 10] Objective (d) 18025.10058690
Coordinate descent loop 32 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18015.48730478
Using method dual for projection
[seed 10] Objective (d) 18015.15573460
Coordinate descent loop 33 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18009.09810823
Using method dual for projection
[seed 10] Objective (d) 18010.63893464
Coordinate descent loop 34 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18004.88799480
Using method dual for projection
[seed 10] Objective (d) 18058.43317878
Coordinate descent loop 35 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18049.18862388
Using method dual for projection
[seed 10] Objective (d) 17997.43410150
Coordinate descent loop 36 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17990.54847851
Using method dual for projection
[seed 10] Objective (d) 17990.87720591
Coordinate descent loop 37 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17985.96104640
Using method dual for projection
[seed 10] Objective (d) 17988.72473113
Coordinate descent loop 38 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17984.75060597
Using method dual for projection
[seed 10] Objective (d) 18049.41997042
Coordinate descent loop 39 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 18046.54412956
Using method dual for projection
[seed 10] Objective (d) 17984.35883312
Coordinate descent loop 40 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17980.96932270
Using method dual for projection
[seed 10] Objective (d) 17980.80725811
Coordinate descent loop 41 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17972.69499835
Using method dual for projection
[seed 10] Objective (d) 17972.04185710
Coordinate descent loop 42 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17966.98681665
Using method dual for projection
[seed 10] Objective (d) 17966.83182704
Coordinate descent loop 43 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17962.44110583
Using method dual for projection
[seed 10] Objective (d) 17977.54578172
Coordinate descent loop 44 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17975.04937168
Using method dual for projection
[seed 10] Objective (d) 17966.02802390
Coordinate descent loop 45 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17963.62647362
Using method dual for projection
[seed 10] Objective (d) 17978.84149617
Coordinate descent loop 46 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17976.70690623
Using method dual for projection
[seed 10] Objective (d) 17972.68476399
Coordinate descent loop 47 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17970.72104894
Using method dual for projection
[seed 10] Objective (d) 17972.31717250
Coordinate descent loop 48 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17970.26965194
Using method dual for projection
[seed 10] Objective (d) 17963.57955947
Coordinate descent loop 49 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17961.46583197
Using method dual for projection
[seed 10] Objective (d) 17970.78125247
Coordinate descent loop 50 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17966.72856396
Using method dual for projection
[seed 10] Objective (d) 17966.31368074
Coordinate descent loop 51 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17964.29576923
Using method dual for projection
[seed 10] Objective (d) 17966.82685224
Coordinate descent loop 52 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17965.40231911
Using method dual for projection
[seed 10] Objective (d) 17963.41091765
Coordinate descent loop 53 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17961.32976779
Using method dual for projection
[seed 10] Objective (d) 17965.71068930
Coordinate descent loop 54 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17963.57567750
Using method dual for projection
[seed 10] Objective (d) 17954.36795789
Coordinate descent loop 55 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17953.27474312
Using method dual for projection
[seed 10] Objective (d) 17949.58276945
Coordinate descent loop 56 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17948.66798435
Using method dual for projection
[seed 10] Objective (d) 17954.20157560
Coordinate descent loop 57 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17953.36360049
Using method dual for projection
[seed 10] Objective (d) 17954.79840556
Coordinate descent loop 58 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17953.98623213
Using method dual for projection
[seed 10] Objective (d) 17956.77334880
Coordinate descent loop 59 / 60 [n_jobs=1]
[seed 10] Objective (z_hat) : 17955.57707174
Using method dual for projection
[seed 10] Objective (d) 17948.78204378

Let’s look at the atoms now.

plt.figure()
plt.plot(d_hat.T)
plt.show()
plot lfp data

Total running time of the script: (2 minutes 8.717 seconds)

Gallery generated by Sphinx-Gallery