Filtering Methods in Analysis of LFP

Filtering Methods in Analysis of LFP

Authors
Dr. Atle Rimehaug | Dr. Nicholas Del Grosso

In this notebook, you will apply filtering methods to real LFP data and combine the results of this filtering with other analyses.

  • Lowpass filters - keep part of signal below a certain frequency.
  • Highpass filters - keep part of signal above a certain frequency.
  • Bandpass filters - keep part of signal inside a certain frequency range.
  • Bandstop filters - keep part of signal outside a certain frequency range.

Credit: allaboutcircuits.com

Setup

Import Libraries

import xarray as xr
from pathlib import Path
import matplotlib.pyplot as plt
from scipy import signal
import numpy as np

Utility Functions

Run the cell below, but feel free to ignore this code — it’s some utility functions we’ll use later for convenience.

def style_plot(title, xlabel, ylabel):
    '''
    Styles plots displaying filters. Adds labels, grids and shows legends

    Args:
        title: title of plot
        xlabel: label x-axis
        ylabel: label y-axis
    '''
    
    plt.title(title)
    plt.xlabel(xlabel)
    plt.ylabel(ylabel)
    plt.margins(0, 0.1)
    plt.grid(which='both', axis='both')
    plt.legend();

class utils:
    style_plot = style_plot

Download dataset

import owncloud

owncloud.Client.from_public_link('https://uni-bonn.sciebo.de/s/M56DCkpyCJ9FRoi', folder_password="ibots"
).get_file('/', 'dataset_session_754312389.nc')
True

Section 1: Filter to obtain LFP from recorded extracellular potential

Lowpass filters are used to obtain LFP from the recorded extracellular potential but there’s no universally agreed upon convention on which cutoff frequency to use. Most labs will use a cutoff frequency between 100 Hz and 500 Hz. If you use higher cutoff frequencies, you are more likely to include multi-unit activity (MUA) - that is, spikes - in your LFP. And spikes is noise if you want to study LFP alone. However, if you use a low cutoff frequency, you may lose important information in your LFP. Your choice of a cutoff frequency will ultimately depend on your analysis, but 300 Hz can be seen as a reasonable default value. It can also be a good idea to experiment with different cutoff values to see how that may affect your analysis.

Code Description
b, a = signal.butter(filter_order, cutoff_frequency, btype = 'lowpass', fs = sampling_frequency) Create the coefficients b and a to be used in the lowpass filtering of the signal.
w, h = signal.freqz(b, a, fs=sampling_frequency) Compute the frequency response of the filter with coefficients b and a. h is the frequency response (the filter’s magnitude at a given frequency), w is the frequencies the response is calculated for.
lfp_trace = signal.filtfilt(b, a, ecp_trace)) Apply the constructed filter to the ecp trace from a single channel using the coefficients b and a.
lfp = signal.filtfilt(b, a, ecp, axis = time_dim)) Apply the constructed filter to an array containing the ecp recorded from all channels using the coefficients b and a. When the ecp data is multidimensional, the axis the filter is applied to needs to be specified to be along the time dimension of the data (default is axis = -1).
plt.plot(x, y) Plot y values against x values.
plt.imshow(data, aspect = 'auto', cmap = 'selected_colormap') Plots a multidimensional array as an image. aspect sets the aspect ratio of the axis. cmap sets the colormap.

Load dataset

Run cells below to load dataset and extract relevant data

Exercises

loadpath = 'dataset_session_754312389.nc'

dataset_path = Path(loadpath)

dataset = xr.load_dataset(dataset_path)
dataset
<xarray.Dataset> Size: 225MB
Dimensions:                 (channel_depth: 23, trial_nr: 75, time_lfp: 1875,
                             time_csd: 1875, time_whole_rec_lfp: 373124,
                             time_whole_rec_ecp: 373124, unit_id_LGN: 27,
                             time_spikes: 1500, unit_id_V1: 91, unit_id_LM: 13,
                             stimulus_start_times: 75, stimulus_stop_times: 75,
                             channel_id: 23)
Coordinates: (12/13)
  * channel_depth           (channel_depth) int64 184B 0 -40 -80 ... -840 -880
  * trial_nr                (trial_nr) int32 300B 0 1 2 3 4 5 ... 70 71 72 73 74
  * time_lfp                (time_lfp) float64 15kB -1e+03 -999.2 ... 499.2
  * time_csd                (time_csd) float64 15kB -1e+03 -999.2 ... 499.2
  * time_whole_rec_lfp      (time_whole_rec_lfp) float64 3MB 1.286e+06 ... 1....
  * time_whole_rec_ecp      (time_whole_rec_ecp) float64 3MB 1.286e+06 ... 1....
    ...                      ...
  * time_spikes             (time_spikes) float64 12kB -999.5 -998.5 ... 499.5
  * unit_id_V1              (unit_id_V1) int32 364B 951795075 ... 951798053
  * unit_id_LM              (unit_id_LM) int32 52B 951791074 ... 951792163
  * stimulus_start_times    (stimulus_start_times) float64 600B 1.29e+06 ... ...
  * stimulus_stop_times     (stimulus_stop_times) float64 600B 1.29e+06 ... 1...
  * channel_id              (channel_id) int32 92B 850144538 ... 850144362
Data variables:
    lfp_V1                  (channel_depth, trial_nr, time_lfp) float64 26MB ...
    csd_V1                  (channel_depth, trial_nr, time_csd) float64 26MB ...
    lfp_whole_recording_V1  (channel_depth, time_whole_rec_lfp) float64 69MB ...
    ecp_whole_recording_V1  (channel_depth, time_whole_rec_ecp) float64 69MB ...
    spike_counts_LGN        (unit_id_LGN, trial_nr, time_spikes) int16 6MB 0 ...
    spike_counts_V1         (unit_id_V1, trial_nr, time_spikes) int16 20MB 0 ...
    spike_counts_LM         (unit_id_LM, trial_nr, time_spikes) int16 3MB 0 ....
    pupil_width             (trial_nr) float64 600B 39.12 40.53 ... 46.57 44.31
    run_speed               (trial_nr) float64 600B 1.155 1.597 ... 1.251 1.711
Attributes:
    time_unit:                  millisecond
    lfp_unit:                   Volt
    channel_depth_unit:         micrometer
    note_channel_depth:         Measured in distance from electrode closest t...
    sampling_frequency_lfp:     1250
    sampling_frequency_spikes:  1000
    sampling_frequency_unit:    Hz
    stimulus_onset:             1000
    stimulus_offset:            1250
xarray.Dataset
    • channel_depth: 23
    • trial_nr: 75
    • time_lfp: 1875
    • time_csd: 1875
    • time_whole_rec_lfp: 373124
    • time_whole_rec_ecp: 373124
    • unit_id_LGN: 27
    • time_spikes: 1500
    • unit_id_V1: 91
    • unit_id_LM: 13
    • stimulus_start_times: 75
    • stimulus_stop_times: 75
    • channel_id: 23
    • channel_depth
      (channel_depth)
      int64
      0 -40 -80 -120 ... -800 -840 -880
      array([   0,  -40,  -80, -120, -160, -200, -240, -280, -320, -360, -400, -440,
             -480, -520, -560, -600, -640, -680, -720, -760, -800, -840, -880])
    • trial_nr
      (trial_nr)
      int32
      0 1 2 3 4 5 6 ... 69 70 71 72 73 74
      array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
             18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
             36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
             54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
             72, 73, 74], dtype=int32)
    • time_lfp
      (time_lfp)
      float64
      -1e+03 -999.2 ... 498.4 499.2
      array([-1000. ,  -999.2,  -998.4, ...,   497.6,   498.4,   499.2])
    • time_csd
      (time_csd)
      float64
      -1e+03 -999.2 ... 498.4 499.2
      array([-1000. ,  -999.2,  -998.4, ...,   497.6,   498.4,   499.2])
    • time_whole_rec_lfp
      (time_whole_rec_lfp)
      float64
      1.286e+06 1.286e+06 ... 1.584e+06
      array([1285609.738645, 1285610.538645, 1285611.338645, ..., 1584106.565384,
             1584107.365384, 1584108.165384])
    • time_whole_rec_ecp
      (time_whole_rec_ecp)
      float64
      1.286e+06 1.286e+06 ... 1.584e+06
      array([1285609.738645, 1285610.538645, 1285611.338645, ..., 1584106.565384,
             1584107.365384, 1584108.165384])
    • unit_id_LGN
      (unit_id_LGN)
      int32
      951782498 951782516 ... 951798528
      array([951782498, 951782516, 951782530, 951782631, 951782683, 951782699,
             951782744, 951782795, 951782832, 951782895, 951787033, 951792341,
             951792375, 951792398, 951792418, 951792441, 951792504, 951792544,
             951797077, 951798318, 951798391, 951798404, 951798553, 951798681,
             951801317, 951798434, 951798528], dtype=int32)
    • time_spikes
      (time_spikes)
      float64
      -999.5 -998.5 ... 498.5 499.5
      array([-999.5, -998.5, -997.5, ...,  497.5,  498.5,  499.5])
    • unit_id_V1
      (unit_id_V1)
      int32
      951795075 951795086 ... 951798053
      array([951795075, 951795086, 951795098, 951795140, 951795163, 951795222,
             951795238, 951795256, 951795269, 951795290, 951795309, 951795317,
             951795403, 951797419, 951795458, 951795474, 951795487, 951795495,
             951795554, 951795680, 951795563, 951795611, 951795663, 951795688,
             951795706, 951795713, 951795721, 951797465, 951795729, 951795737,
             951795747, 951795873, 951795753, 951795782, 951795792, 951795760,
             951795768, 951795775, 951795807, 951795824, 951795832, 951795839,
             951795858, 951795880, 951795886, 951795896, 951795918, 951797520,
             951797539, 951797553, 951795936, 951795943, 951795967, 951795976,
             951797567, 951797489, 951796016, 951796073, 951796125, 951796165,
             951796176, 951796229, 951797633, 951796333, 951797647, 951797664,
             951796352, 951796494, 951797718, 951796542, 951796556, 951797730,
             951796585, 951797742, 951796629, 951796639, 951796660, 951796696,
             951796791, 951797776, 951796866, 951796906, 951796928, 951796949,
             951797595, 951797613, 951797709, 951797764, 951797811, 951797995,
             951798053], dtype=int32)
    • unit_id_LM
      (unit_id_LM)
      int32
      951791074 951791093 ... 951792163
      array([951791074, 951791093, 951791202, 951791214, 951791273, 951791305,
             951791297, 951791401, 951791422, 951791467, 951791442, 951791453,
             951792163], dtype=int32)
    • stimulus_start_times
      (stimulus_start_times)
      float64
      1.29e+06 1.294e+06 ... 1.584e+06
      array([1289613.25125, 1293616.60125, 1299621.62125, 1301623.29125,
             1303624.97125, 1307628.33125, 1309629.95125, 1319638.32125,
             1329646.71125, 1333650.07125, 1337653.40125, 1341656.74125,
             1351665.05125, 1357670.12125, 1361673.43125, 1371681.84125,
             1373683.47125, 1375685.17125, 1383691.83125, 1387695.15125,
             1397703.53125, 1401706.88125, 1403708.58125, 1407711.93125,
             1411715.25125, 1413716.94125, 1415718.63125, 1417720.26125,
             1419721.95125, 1421723.62125, 1423725.25125, 1427728.62125,
             1429730.28125, 1431731.94125, 1433733.62125, 1435735.32125,
             1441740.33125, 1447745.31125, 1451748.67125, 1459755.38125,
             1461757.06125, 1467762.05125, 1473767.09125, 1481773.75125,
             1485777.10125, 1489780.46125, 1491782.12125, 1493783.81125,
             1497787.16125, 1501790.48125, 1503792.14125, 1505793.80125,
             1511798.87125, 1515802.17125, 1517803.89125, 1519805.55125,
             1521807.23125, 1529813.91125, 1531815.57125, 1533817.23125,
             1537820.57125, 1541823.93125, 1543825.60125, 1545827.30125,
             1547828.93125, 1551832.29125, 1553833.96125, 1563842.35125,
             1565843.99125, 1569847.35125, 1571849.02125, 1573850.69125,
             1575852.34125, 1577854.07125, 1583859.05125])
    • stimulus_stop_times
      (stimulus_stop_times)
      float64
      1.29e+06 1.294e+06 ... 1.584e+06
      array([1289863.450591, 1293866.800591, 1299871.820591, 1301873.490591,
             1303875.173091, 1307878.525591, 1309880.158091, 1319888.525591,
             1329896.908091, 1333900.265591, 1337903.598091, 1341906.940591,
             1351915.258091, 1357920.320591, 1361923.638091, 1371932.033091,
             1373933.675591, 1375935.370591, 1383942.035591, 1387945.363091,
             1397953.738091, 1401957.083091, 1403958.778091, 1407962.128091,
             1411965.450591, 1413967.138091, 1415968.823091, 1417970.465591,
             1419972.150591, 1421973.823091, 1423975.460591, 1427978.823091,
             1429980.490591, 1431982.153091, 1433983.830591, 1435985.523091,
             1441990.535591, 1447995.523091, 1451998.875591, 1460005.585591,
             1462007.260591, 1468012.260591, 1474017.293091, 1482023.960591,
             1486027.308091, 1490030.665591, 1492032.325591, 1494034.013091,
             1498037.360591, 1502040.685591, 1504042.350591, 1506044.010591,
             1512049.070591, 1516052.383091, 1518054.088091, 1520055.753091,
             1522057.428091, 1530064.110591, 1532065.775591, 1534067.438091,
             1538070.778091, 1542074.135591, 1544075.808091, 1546077.498091,
             1548079.138091, 1552082.495591, 1554084.165591, 1564092.550591,
             1566094.193091, 1570097.553091, 1572099.225591, 1574100.895591,
             1576102.553091, 1578104.268091, 1584109.255591])
    • channel_id
      (channel_id)
      int32
      850144538 850144530 ... 850144362
      array([850144538, 850144530, 850144522, 850144514, 850144506, 850144498,
             850144490, 850144482, 850144474, 850144466, 850144458, 850144450,
             850144442, 850144434, 850144426, 850144418, 850144410, 850144402,
             850144394, 850144386, 850144378, 850144370, 850144362], dtype=int32)
    • lfp_V1
      (channel_depth, trial_nr, time_lfp)
      float64
      -8.516e-06 -1.156e-05 ... 6.131e-06
      array([[[-8.51569421e-06, -1.15631923e-05, -3.76924291e-07, ...,
               -2.17654367e-06,  1.68119712e-07, -6.94200500e-06],
              [ 2.81771330e-08,  6.29403676e-06,  9.98029865e-06, ...,
                1.01304291e-05,  5.93295341e-06,  1.72969475e-05],
              [-1.27271427e-06, -3.68783112e-06, -4.46340366e-06, ...,
                3.65666426e-06,  2.96670978e-06,  1.24568597e-06],
              ...,
              [ 4.70565253e-06,  1.94317598e-06,  1.20455675e-06, ...,
                4.07503496e-06,  7.65054254e-06,  3.91673831e-06],
              [ 8.38144661e-07,  2.28123812e-06,  3.70896321e-06, ...,
                1.34013793e-05,  1.15462596e-05,  8.42411093e-06],
              [ 7.32479652e-06,  1.07711181e-06,  7.00885884e-06, ...,
                1.92065863e-05,  1.92065863e-05,  1.92065863e-05]],
             [[ 3.91952787e-06,  1.70056905e-06,  5.97275690e-06, ...,
                4.06974274e-06,  4.04094749e-06, -4.68055730e-06],
              [-4.10311367e-06,  5.00856269e-06,  6.20500275e-06, ...,
                8.75868311e-06,  5.69604080e-06,  8.57744575e-06],
              [ 1.09646506e-06,  1.62070262e-06,  1.10953040e-07, ...,
                1.01990671e-05, -8.36754580e-06, -1.43387447e-05],
      ...
              [-6.46738552e-06, -1.19701456e-05, -2.41882630e-05, ...,
               -2.31175915e-05, -2.37374262e-05, -7.53926446e-06],
              [ 2.91850049e-05,  2.81713398e-05,  5.17618757e-05, ...,
                4.44764295e-05,  4.08778688e-05,  3.83233192e-05],
              [-1.80642354e-05, -1.06068608e-05, -3.71177368e-06, ...,
                1.48063260e-05,  1.48063260e-05,  1.48063260e-05]],
             [[ 4.42062699e-05,  5.99208645e-05,  7.60618853e-05, ...,
                9.85508013e-05,  8.87923195e-05,  8.02998878e-05],
              [-6.30930699e-06,  2.14882295e-05,  5.40961203e-06, ...,
               -6.18517313e-05, -8.49961217e-05, -7.07025406e-05],
              [ 1.32347041e-04,  9.66830285e-05,  9.77152893e-05, ...,
               -3.68393584e-05, -2.08663734e-05, -1.27901811e-05],
              ...,
              [ 7.59530363e-07, -9.00525049e-06, -1.95382678e-05, ...,
               -2.78150664e-05, -1.46349189e-05,  3.65395950e-06],
              [ 3.22381374e-05,  3.90163375e-05,  6.39709157e-05, ...,
                5.17422904e-05,  4.94353600e-05,  3.90770304e-05],
              [-2.50190016e-05, -1.82145705e-05, -1.97245214e-05, ...,
                6.13148062e-06,  6.13148062e-06,  6.13148062e-06]]])
    • csd_V1
      (channel_depth, trial_nr, time_csd)
      float64
      -0.04729 -0.04843 ... 0.004271
      array([[[-0.04729251, -0.04843248, -0.03745331, ..., -0.0506883 ,
               -0.05553956, -0.06003479],
              [-0.01093619,  0.01574628,  0.00914164, ...,  0.00598613,
                0.01101591,  0.03123014],
              [-0.03696462, -0.04099173, -0.03852095, ...,  0.00098069,
               -0.0050393 , -0.01264635],
              ...,
              [-0.01247878,  0.00877942,  0.02265446, ...,  0.00331111,
                0.03482657,  0.01182155],
              [-0.00745365, -0.01184343, -0.0143719 , ..., -0.00701411,
               -0.00677321,  0.00801698],
              [-0.00185681,  0.00029347,  0.01247577, ...,  0.04692436,
                0.04692436,  0.04692436]],
             [[-0.05243487, -0.04187987, -0.01951768, ...,  0.00767212,
                0.02122529, -0.00091668],
              [-0.03800667, -0.0267893 ,  0.00151346, ..., -0.06356118,
               -0.05255179, -0.02253284],
              [-0.13918267, -0.15069373, -0.14869443, ..., -0.08287076,
               -0.10533221, -0.11878146],
      ...
              [-0.0317182 , -0.0291328 , -0.04440809, ...,  0.0128667 ,
               -0.00803555,  0.00140582],
              [-0.00313798, -0.02211998,  0.00973861, ...,  0.01834821,
                0.02898202,  0.03053275],
              [ 0.01935376,  0.02458448,  0.02770717, ...,  0.0335107 ,
                0.0335107 ,  0.0335107 ]],
             [[ 0.05527245,  0.06647928,  0.08365367, ...,  0.03946655,
               -0.00638597,  0.01617121],
              [ 0.00484864,  0.04955322,  0.00056809, ..., -0.02495747,
               -0.04720462, -0.05647099],
              [ 0.14248465,  0.09267001,  0.12532577, ..., -0.02102215,
                0.01070787, -0.00105385],
              ...,
              [ 0.02074605,  0.01291679, -0.00810403, ..., -0.00440359,
                0.01890968,  0.03686672],
              [-0.03145411, -0.01193072,  0.05258549, ...,  0.06270049,
                0.05033156,  0.03718263],
              [-0.01626375, -0.00653823, -0.01209703, ...,  0.00427089,
                0.00427089,  0.00427089]]])
    • lfp_whole_recording_V1
      (channel_depth, time_whole_rec_lfp)
      float64
      1.131e-05 2.316e-05 ... 6.131e-06
      array([[ 1.13123117e-05,  2.31601759e-05,  2.56464473e-05, ...,
               2.90957718e-05,  2.80686400e-05,  1.92065863e-05],
             [ 1.25864221e-05,  2.61936713e-05,  2.05721887e-05, ...,
               2.51329249e-05,  1.10562717e-05,  1.14064589e-05],
             [ 1.06294434e-05,  1.20898434e-05,  1.05466595e-05, ...,
               4.39547441e-06,  2.49921901e-06, -3.03491628e-06],
             ...,
             [-1.51105564e-05, -2.86651554e-05, -2.43583384e-05, ...,
               1.55104740e-05,  1.08842192e-05,  7.88508374e-06],
             [-2.60411104e-05, -3.63399664e-05, -2.41173628e-05, ...,
               4.57287775e-05,  2.16324211e-05,  1.48063260e-05],
             [-1.92918607e-05, -3.23216874e-05, -2.71540780e-05, ...,
               5.95832945e-05,  2.76861759e-05,  6.13148062e-06]])
    • ecp_whole_recording_V1
      (channel_depth, time_whole_rec_ecp)
      float64
      1.131e-05 2.316e-05 ... 6.131e-06
      array([[ 1.13123117e-05,  2.31601759e-05,  2.56464473e-05, ...,
               2.90957718e-05,  2.80686400e-05,  1.92065863e-05],
             [ 1.25864221e-05,  2.61936713e-05,  2.05721887e-05, ...,
               2.51329249e-05,  1.10562717e-05,  1.14064589e-05],
             [ 1.06294434e-05,  1.20898434e-05,  1.05466595e-05, ...,
               4.39547441e-06,  2.49921901e-06, -3.03491628e-06],
             ...,
             [-1.51105564e-05, -2.86651554e-05, -2.43583384e-05, ...,
               1.55104740e-05,  1.08842192e-05,  7.88508374e-06],
             [-2.60411104e-05, -3.63399664e-05, -2.41173628e-05, ...,
               4.57287775e-05,  2.16324211e-05,  1.48063260e-05],
             [-1.92918607e-05, -3.23216874e-05, -2.71540780e-05, ...,
               5.95832945e-05,  2.76861759e-05,  6.13148062e-06]])
    • spike_counts_LGN
      (unit_id_LGN, trial_nr, time_spikes)
      int16
      0 0 0 0 0 0 0 0 ... 1 0 0 0 0 0 0 0
      array([[[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
      ...
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 1, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]]], dtype=int16)
    • spike_counts_V1
      (unit_id_V1, trial_nr, time_spikes)
      int16
      0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0
      array([[[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 1, ..., 0, 0, 0],
              ...,
      ...
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]]], dtype=int16)
    • spike_counts_LM
      (unit_id_LM, trial_nr, time_spikes)
      int16
      0 0 0 0 0 0 0 0 ... 0 0 0 0 0 0 0 0
      array([[[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
      ...
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 1],
              ...,
              [0, 0, 0, ..., 0, 1, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]],
             [[0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              ...,
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0],
              [0, 0, 0, ..., 0, 0, 0]]], dtype=int16)
    • pupil_width
      (trial_nr)
      float64
      39.12 40.53 53.87 ... 46.57 44.31
      array([39.11652012, 40.53014519, 53.86896327, 50.82396045, 52.16879212,
             57.96043078, 54.92497335, 55.54967205, 43.90274746, 44.79854897,
             38.38702834, 40.71644189, 37.98256784, 35.66463223, 33.77803219,
             37.33170246, 34.40298455, 33.56487003, 34.8366242 , 34.23566353,
             37.82023369, 36.64031488, 34.90016986, 34.01127022, 34.01676223,
             33.07691675, 36.24898149, 34.31897366, 32.37205121, 32.29131936,
             31.22026173, 32.11667831, 35.9407955 , 33.83439488, 40.32179372,
             40.2773362 , 36.8416765 , 38.28154173, 32.92621992, 34.10611125,
             32.64213782, 35.34716687, 38.83240189, 41.4527341 , 40.80453453,
             34.4518716 , 33.54378434, 34.76110284, 39.39896604, 38.12986388,
             35.01399576, 36.1764032 , 36.91026669, 38.09285533, 37.47092614,
             35.08431869, 34.10750204, 33.98908271, 33.49633874, 31.95082717,
             36.40762283, 33.54301125, 30.16956358, 34.97767163, 35.73862834,
             37.5021579 , 46.90415398, 64.55582159, 65.24024526, 62.84832274,
             63.79648979, 64.1050437 , 54.12537803, 46.57182176, 44.30600894])
    • run_speed
      (trial_nr)
      float64
      1.155 1.597 6.057 ... 1.251 1.711
      array([ 1.15461545,  1.59668837,  6.056658  ,  5.15987544,  8.52760497,
             11.22847943,  4.57915849,  3.55552612,  1.21703267,  1.14123532,
              1.22313026,  1.45219009,  1.27881682,  1.28545771,  1.30252225,
              1.45938978,  1.23683681,  1.35499306,  1.24373061,  1.72814998,
              1.10057218,  1.12037113,  1.0171456 ,  1.12698213,  1.23746278,
              1.0051122 ,  1.22995347,  1.18035543,  1.33973747,  1.31655126,
              1.42316228,  1.16454631,  1.1175898 ,  1.33965127,  1.44008401,
              1.27856107,  1.08369774,  1.2852668 ,  1.22042713,  0.93635542,
              1.10224883,  1.3656531 ,  1.14346642,  1.34443297,  1.27934364,
              1.22477016,  0.89182778,  1.08752668,  1.29883034,  1.38104512,
              0.96683534,  1.19810534,  0.99052094,  0.9343001 ,  1.71404147,
              1.51968473,  1.23845685,  0.84848258,  1.59605601,  1.25635954,
              1.22987165,  1.64782276,  1.75731669,  1.67665638,  1.42473997,
              1.02886151,  5.65275275,  8.34342969,  9.03230058, 10.64468513,
              8.17371765,  1.18762197,  1.36400808,  1.25093511,  1.71061779])
    • channel_depth
      PandasIndex
      PandasIndex(Index([   0,  -40,  -80, -120, -160, -200, -240, -280, -320, -360, -400, -440,
             -480, -520, -560, -600, -640, -680, -720, -760, -800, -840, -880],
            dtype='int64', name='channel_depth'))
    • trial_nr
      PandasIndex
      PandasIndex(Index([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
             18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
             36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
             54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
             72, 73, 74],
            dtype='int32', name='trial_nr'))
    • time_lfp
      PandasIndex
      PandasIndex(Index([           -1000.0, -999.1999999999999,             -998.4,
             -997.5999999999999,             -996.8, -995.9999999999999,
             -995.1999999999998, -994.3999999999999, -993.5999999999998,
             -992.7999999999998,
             ...
             492.00000000004263, 492.80000000004276,  493.6000000000429,
              494.4000000000428, 495.20000000004273, 496.00000000004286,
               496.800000000043,  497.6000000000429, 498.40000000004284,
             499.20000000004296],
            dtype='float64', name='time_lfp', length=1875))
    • time_csd
      PandasIndex
      PandasIndex(Index([           -1000.0, -999.1999999999999,             -998.4,
             -997.5999999999999,             -996.8, -995.9999999999999,
             -995.1999999999998, -994.3999999999999, -993.5999999999998,
             -992.7999999999998,
             ...
             492.00000000004263, 492.80000000004276,  493.6000000000429,
              494.4000000000428, 495.20000000004273, 496.00000000004286,
               496.800000000043,  497.6000000000429, 498.40000000004284,
             499.20000000004296],
            dtype='float64', name='time_csd', length=1875))
    • time_whole_rec_lfp
      PandasIndex
      PandasIndex(Index([1285609.7386445338, 1285610.5386446053,  1285611.338644677,
             1285612.1386447486, 1285612.9386448204, 1285613.7386448921,
             1285614.5386449639, 1285615.3386450356, 1285616.1386451072,
              1285616.938645179,
             ...
             1584100.9653832503, 1584101.7653833218, 1584102.5653833936,
              1584103.365383465, 1584104.1653835368, 1584104.9653836086,
             1584105.7653836801, 1584106.5653837519, 1584107.3653838234,
             1584108.1653838952],
            dtype='float64', name='time_whole_rec_lfp', length=373124))
    • time_whole_rec_ecp
      PandasIndex
      PandasIndex(Index([1285609.7386445338, 1285610.5386446053,  1285611.338644677,
             1285612.1386447486, 1285612.9386448204, 1285613.7386448921,
             1285614.5386449639, 1285615.3386450356, 1285616.1386451072,
              1285616.938645179,
             ...
             1584100.9653832503, 1584101.7653833218, 1584102.5653833936,
              1584103.365383465, 1584104.1653835368, 1584104.9653836086,
             1584105.7653836801, 1584106.5653837519, 1584107.3653838234,
             1584108.1653838952],
            dtype='float64', name='time_whole_rec_ecp', length=373124))
    • unit_id_LGN
      PandasIndex
      PandasIndex(Index([951782498, 951782516, 951782530, 951782631, 951782683, 951782699,
             951782744, 951782795, 951782832, 951782895, 951787033, 951792341,
             951792375, 951792398, 951792418, 951792441, 951792504, 951792544,
             951797077, 951798318, 951798391, 951798404, 951798553, 951798681,
             951801317, 951798434, 951798528],
            dtype='int32', name='unit_id_LGN'))
    • time_spikes
      PandasIndex
      PandasIndex(Index([            -999.5,             -998.5,             -997.5,
             -996.4999999999999,             -995.5, -994.4999999999999,
                         -993.5, -992.4999999999999,             -991.5,
             -990.4999999999999,
             ...
             490.50000000000006, 491.50000000000006, 492.50000000000006,
             493.50000000000006, 494.50000000000006, 495.50000000000006,
             496.50000000000006, 497.50000000000006, 498.50000000000006,
             499.50000000000006],
            dtype='float64', name='time_spikes', length=1500))
    • unit_id_V1
      PandasIndex
      PandasIndex(Index([951795075, 951795086, 951795098, 951795140, 951795163, 951795222,
             951795238, 951795256, 951795269, 951795290, 951795309, 951795317,
             951795403, 951797419, 951795458, 951795474, 951795487, 951795495,
             951795554, 951795680, 951795563, 951795611, 951795663, 951795688,
             951795706, 951795713, 951795721, 951797465, 951795729, 951795737,
             951795747, 951795873, 951795753, 951795782, 951795792, 951795760,
             951795768, 951795775, 951795807, 951795824, 951795832, 951795839,
             951795858, 951795880, 951795886, 951795896, 951795918, 951797520,
             951797539, 951797553, 951795936, 951795943, 951795967, 951795976,
             951797567, 951797489, 951796016, 951796073, 951796125, 951796165,
             951796176, 951796229, 951797633, 951796333, 951797647, 951797664,
             951796352, 951796494, 951797718, 951796542, 951796556, 951797730,
             951796585, 951797742, 951796629, 951796639, 951796660, 951796696,
             951796791, 951797776, 951796866, 951796906, 951796928, 951796949,
             951797595, 951797613, 951797709, 951797764, 951797811, 951797995,
             951798053],
            dtype='int32', name='unit_id_V1'))
    • unit_id_LM
      PandasIndex
      PandasIndex(Index([951791074, 951791093, 951791202, 951791214, 951791273, 951791305,
             951791297, 951791401, 951791422, 951791467, 951791442, 951791453,
             951792163],
            dtype='int32', name='unit_id_LM'))
    • stimulus_start_times
      PandasIndex
      PandasIndex(Index([1289613.2512503476, 1293616.6012503474, 1299621.6212503475,
             1301623.2912503474, 1303624.9712503476, 1307628.3312503474,
             1309629.9512503473, 1319638.3212503474, 1329646.7112503473,
             1333650.0712503474, 1337653.4012503475, 1341656.7412503476,
             1351665.0512503474, 1357670.1212503475, 1361673.4312503475,
             1371681.8412503474, 1373683.4712503473, 1375685.1712503475,
             1383691.8312503477, 1387695.1512503477, 1397703.5312503476,
             1401706.8812503475, 1403708.5812503477, 1407711.9312503475,
             1411715.2512503476, 1413716.9412503475, 1415718.6312503477,
             1417720.2612503476, 1419721.9512503475, 1421723.6212503477,
             1423725.2512503476, 1427728.6212503475, 1429730.2812503476,
             1431731.9412503475, 1433733.6212503477, 1435735.3212503477,
             1441740.3312503477, 1447745.3112503476, 1451748.6712503475,
             1459755.3812503477, 1461757.0612503476, 1467762.0512503476,
             1473767.0912503477, 1481773.7512503476, 1485777.1012503477,
             1489780.4612503476, 1491782.1212503477, 1493783.8112503474,
             1497787.1612503477, 1501790.4812503476, 1503792.1412503477,
             1505793.8012503476, 1511798.8712503477, 1515802.1712503475,
             1517803.8912503475, 1519805.5512503476, 1521807.2312503476,
             1529813.9112503477, 1531815.5712503477, 1533817.2312503476,
             1537820.5712503474, 1541823.9312503478, 1543825.6012503474,
             1545827.3012503476, 1547828.9312503475, 1551832.2912503476,
             1553833.9612503476, 1563842.3512503474, 1565843.9912503478,
             1569847.3512503477, 1571849.0212503476, 1573850.6912503475,
             1575852.3412503474, 1577854.0712503474, 1583859.0512503476],
            dtype='float64', name='stimulus_start_times'))
    • stimulus_stop_times
      PandasIndex
      PandasIndex(Index([1289863.4505906447, 1293866.8005906448, 1299871.8205906446,
              1301873.490590645, 1303875.1730906446, 1307878.5255906447,
             1309880.1580906447, 1319888.5255906447, 1329896.9080906447,
             1333900.2655906447, 1337903.5980906447, 1341906.9405906445,
             1351915.2580906446, 1357920.3205906448, 1361923.6380906447,
             1371932.0330906445, 1373933.6755906448,  1375935.370590645,
             1383942.0355906447, 1387945.3630906448,  1397953.738090645,
             1401957.0830906448, 1403958.7780906446,  1407962.128090645,
              1411965.450590645, 1413967.1380906447,  1415968.823090645,
             1417970.4655906449, 1419972.1505906447, 1421973.8230906448,
             1423975.4605906447,  1427978.823090645, 1429980.4905906448,
             1431982.1530906449, 1433983.8305906449, 1435985.5230906447,
             1441990.5355906447, 1447995.5230906447, 1451998.8755906448,
             1460005.5855906447, 1462007.2605906448, 1468012.2605906453,
             1474017.2930906452,  1482023.960590645, 1486027.3080906447,
             1490030.6655906448, 1492032.3255906447, 1494034.0130906447,
             1498037.3605906449, 1502040.6855906448,  1504042.350590645,
             1506044.0105906448, 1512049.0705906448, 1516052.3830906448,
              1518054.088090645,  1520055.753090645, 1522057.4280906445,
             1530064.1105906446, 1532065.7755906447, 1534067.4380906445,
             1538070.7780906446, 1542074.1355906448, 1544075.8080906447,
             1546077.4980906448, 1548079.1380906447,  1552082.495590645,
              1554084.165590645, 1564092.5505906448,  1566094.193090645,
             1570097.5530906448, 1572099.2255906449, 1574100.8955906448,
             1576102.5530906448, 1578104.2680906446,  1584109.255590645],
            dtype='float64', name='stimulus_stop_times'))
    • channel_id
      PandasIndex
      PandasIndex(Index([850144538, 850144530, 850144522, 850144514, 850144506, 850144498,
             850144490, 850144482, 850144474, 850144466, 850144458, 850144450,
             850144442, 850144434, 850144426, 850144418, 850144410, 850144402,
             850144394, 850144386, 850144378, 850144370, 850144362],
            dtype='int32', name='channel_id'))
  • time_unit :
    millisecond
    lfp_unit :
    Volt
    channel_depth_unit :
    micrometer
    note_channel_depth :
    Measured in distance from electrode closest to scalp
    sampling_frequency_lfp :
    1250
    sampling_frequency_spikes :
    1000
    sampling_frequency_unit :
    Hz
    stimulus_onset :
    1000
    stimulus_offset :
    1250
ecp_whole_rec = dataset['ecp_whole_recording_V1']
time_ecp = ecp_whole_rec.time_whole_rec_ecp
time_lfp = time_ecp

sampling_frequency_lfp = dataset.sampling_frequency_lfp

Exercise: Construct and plot a lowpass butterworth filter with a cutoff frequency at 300 Hz. Apply it to the ECP trace from channel index 2 in the recording and plot both the original and the filtered trace together in a new plot. The filtered trace consitutes the LFP.

Solution
filter_order = 5 
cutoff_frequency = 300 # Hz

# creating butter filter
b, a = signal.butter(filter_order, cutoff_frequency, btype = 'low', fs = sampling_frequency_lfp)

w, h = signal.freqz(b, a, fs=sampling_frequency_lfp)

plt.plot(w, np.abs(h), label='Filter')

plt.title('Butterworth filter frequency response')
plt.xlabel('Frequency [Hz]')
plt.ylabel('Amplitude')
plt.margins(0, 0.1)
plt.grid(which='both', axis='both')
plt.axvline(cutoff_frequency, color='green', label='Cutoff frequency')
plt.legend();

ichan = 2
ecp_single_chan = ecp_whole_rec[ichan]

filter_order = 5 #
cutoff_frequency = 300 # Hz

# creating butter filter
b, a = signal.butter(filter_order, cutoff_frequency, btype = 'lowpass', fs = sampling_frequency_lfp)

lfp_single_chan = signal.filtfilt(b, a, ecp_single_chan)

plt.plot(time_ecp, ecp_single_chan, label = 'ECP trace')
plt.plot(time_ecp, lfp_single_chan, label = 'LFP trace')
plt.legend()
plt.xlabel('Time (s)')
plt.ylabel('Amplitude (V)');

Exercise: Construct and apply a lowpass butterworth filter (you don’t need to plot it) with a cutoff frequency at 100 Hz to the ECP trace from channel index 2 in the recording. Plot the original and the filtered signals together.

Solution
ichan = 2
ecp_single_chan = ecp_whole_rec[ichan]

filter_order = 5
cutoff_frequency = 100 # Hz

# creating butter filter
b, a = signal.butter(filter_order, cutoff_frequency, btype = 'low', fs = sampling_frequency_lfp)

lfp_single_chan = signal.filtfilt(b, a, ecp_single_chan)

plt.plot(time_ecp, ecp_single_chan, label = 'ECP trace')
plt.plot(time_ecp, lfp_single_chan, label = 'LFP trace')
plt.legend()
plt.xlabel('Time (s)')
plt.ylabel('Amplitude (V)');

Demo:

It’s a bit easier to see the effects of applying the lowpass filter if we limit the x-axis to focus on a smaller time period, like in the plot below.

ichan = 2
ecp_single_chan = ecp_whole_rec[ichan]

filter_order = 5
cutoff_frequency = 100 # Hz

# creating butter filter
b, a = signal.butter(filter_order, cutoff_frequency, btype = 'low', fs = sampling_frequency_lfp)

lfp_single_chan = signal.filtfilt(b, a, ecp_single_chan)

plt.plot(time_ecp, ecp_single_chan, label = 'ECP trace')
plt.plot(time_ecp, lfp_single_chan, label = 'LFP trace')
plt.legend()
plt.xlabel('Time (s)')
plt.ylabel('Amplitude (V)')
plt.xlim(1.3*1E6,1.301*1E6);

Exercise: Construct and apply a butterworth filter with cutoff frequency 300 Hz to LFP from all channels. Hint: By specifying the axis in the data to which the filter is applied in the filtfilt function, you don’t need to select the trace from a single channel.

Solution
filter_order = 5
cutoff_frequency = 100 # Hz

# creating butter filter
b, a = signal.butter(filter_order, cutoff_frequency, btype = 'low', fs = sampling_frequency_lfp)

lfp = signal.filtfilt(b, a, ecp_whole_rec, axis=-1)

Exercise: Construct and apply a highpass butterworth filter with a cutoff frequency at 600 Hz to the ECP. Plot single channel traces from the ECP and the MUA (the resulting highpass-filtered signal) together.

This high-frequency part of the signal should mainly contain the multi-unit activity (MUA) - the spikes - in the extracellular potential. Is the amplitude of the resulting MUA signal what you would expect when you compare it to the amplitude of the LFP above?

Hint: Change the btype parameter to create a highpass filter.

Solution
filter_order = 5
cutoff_frequency = 600 # Hz
sampling_frequency = 1250 # Hz

# creating butter filter
b, a = signal.butter(filter_order, cutoff_frequency, btype = 'highpass', fs = sampling_frequency_lfp)

mua_whole_rec = signal.filtfilt(b, a, ecp_whole_rec, axis=-1)

ichan = 2
ecp_single_chan = ecp_whole_rec[ichan]
mua_single_chan = mua_whole_rec[ichan]

plt.plot(time_ecp, ecp_single_chan, label = 'Original signal')
plt.plot(time_ecp, mua_single_chan, label = 'Filtered signal')
plt.legend()
plt.xlabel('Time (s)')
plt.ylabel('Amplitude (V)');

Section 2: Apply a bandpass filter to extract signal frequencies within specific range.

Code Description
b, a = ss.butter(filter_order, [start_freq_band, end_freq_band], btype = 'bandpass', fs = sampling_frequency) Create the coefficients b and a to be used in the bandpass (or bandstop) filtering of the signal. To create a bandstop filter instead of a bandpass filter, simply set btype = 'bandstop'
w, h = ss.freqz(b, a, fs=sampling_frequency) Compute the frequency response of the filter with coefficients b and a. h is the frequency response (the filter’s magnitude at a given frequency), w is the frequencies at which the response is calculated.
lfp_trace = ss.filtfilt(b, a, ecp_trace)) Apply the constructed filter to the ecp signal at a specific channel using the coefficients b and a.
plt.plot(x, y) Plot y values against x values.
plt.imshow(data, aspect = 'auto', cmap = 'selected_colormap') Plots a multidimensional array as an image. aspect sets the aspect ratio of the axis. cmap sets the colormap.

Load LFP from whole recording

Exercises

lfp_whole_rec = dataset['lfp_whole_recording_V1']
time_lfp = lfp_whole_rec.time_whole_rec_lfp
time_lfp = time_ecp

Exercise: Construct, plot, and apply a bandpass butterworth filter with a low cutoff at 250 Hz and a high cutoff at 500 Hz to the whole LFP data. Plot a single channel trace from the original and the filtered signal together in one plot.

Solution
filter_order = 5 
sampling_frequency = 1250 # Hz
lowcut = 250 # Hz
highcut = 500 # Hz

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'band', fs = sampling_frequency_lfp)

w, h = signal.freqz(b, a, fs=sampling_frequency) 

plt.plot(w, np.abs(h), label='Filter')
plt.axvline(lowcut, color='red', label='Lower band cutoff')
plt.axvline(highcut, color='green', label='Upper band cutoff')

utils.style_plot(title='Butterworth filter frequency response', xlabel = 'Frequency [Hz]', ylabel='Amplitude')

lfp_bandpass_filtered = signal.filtfilt(b, a, lfp_whole_rec, axis = -1)

ichan = 5
lfp_single_chan = lfp_whole_rec[ichan]
lfp_bandpass_single_chan = lfp_bandpass_filtered[ichan]
plt.plot(time_lfp, lfp_single_chan, label = 'Original signal')
plt.plot(time_lfp, lfp_bandpass_single_chan , label = 'Filtered signal')
plt.legend()
plt.xlabel('Time (s)')
plt.ylabel('Amplitude (V)');

Exercise: Construct and apply a bandpass butterworth filter with a low cutoff at 50 Hz and a high cutoff at 70 Hz to the whole LFP data. Plot a single channel trace from the original and the filtered signal together in one plot.

Solution
filter_order = 5
sampling_frequency = 1250 # Hz
lowcut = 50 # Hz
highcut = 70 # Hz

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'bandpass', fs = sampling_frequency_lfp)

bandpass_filtered_lfp = signal.filtfilt(b, a, lfp_whole_rec, axis = -1)

ichan = 5
lfp_single_chan = lfp_whole_rec[ichan]
bp_lfp_single_chan = bandpass_filtered_lfp[ichan]
plt.plot(time_lfp, lfp_single_chan, label = 'Original signal')
plt.plot(time_lfp, bp_lfp_single_chan, label = 'Filtered signal')
plt.legend()
plt.xlabel('Time (s)');

Exercise: Use the code below that gets the delta band LFP (1-4 Hz) to construct and apply filters to get the theta (4-8 Hz), alpha (8-13 Hz), beta (13-30 Hz), and gamma band (30-120 Hz) LFP. Comment: filter_order = 5 creates a bandpass filter that’s too sharp for the lower, narrower frequency bands (delta, alpha), it will result in NaNs. A lower filter order should be used for the low and narrow frequency bands. A higher filter_order is fine for the higher, broader frequency bands.

# provided

filter_order = 2

''' Filter for delta band'''
lowcut = 4 # Hz
highcut = 100 # Hz

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'bandpass', fs = sampling_frequency_lfp)

lfp_delta = signal.filtfilt(b, a, ecp_whole_rec, axis = -1)
Solution
''' Filter for theta band'''
filter_order = 2
lowcut = 4 # Hz
highcut = 8 # Hz

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'bandpass', fs = sampling_frequency_lfp)

lfp_theta = signal.filtfilt(b, a, ecp_whole_rec, axis = -1)

''' Filter for alpha band'''
filter_order = 2
lowcut = 8 # Hz
highcut = 13 # Hz

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'bandpass', fs = sampling_frequency_lfp)

lfp_alpha = signal.filtfilt(b, a, ecp_whole_rec, axis = -1)

''' Filter for beta band'''
filter_order = 5
lowcut = 13 # Hz
highcut = 30 # Hz

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'bandpass', fs = sampling_frequency_lfp)

lfp_beta = signal.filtfilt(b, a, ecp_whole_rec, axis = -1)

''' Filter for gamma band'''
filter_order = 5
lowcut = 30 # Hz
highcut = 120 # Hz

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'bandpass', fs = sampling_frequency_lfp)

lfp_gamma = signal.filtfilt(b, a, ecp_whole_rec, axis = -1)

Section 3: Relate frequencies to behavior

LFP oscillations in certain frequency bands have been shown to reflect both neural phenomena and behavior (and if they don’t reflect a behavior or a neural phenomenon, they reflect an artifact of the experiment). In this section you’ll see how filtering can be used to uncover associations between the power of an oscillation and behavioral data like running speed and pupil size.

Code Description
b, a = signal.butter(filter_order, [start_freq_band, end_freq_band], btype = 'bandpass', fs = sampling_frequency) Create the coefficients b and a to be used in the lowpass filtering of the signal.
lfp = signal.filtfilt(b, a, ecp, axis = time_dim)) Apply the constructed filter to an array containing the ecp recorded from all channels using the coefficients b and a. When the ecp data is multidimensional, the axis at which the filter is applied needs to be specified to be along the time dimension of the data.
plt.plot(x, y) Plot y values against x values.
fig, ax = plt.subplots(nrows, ncols, figsize = (width, height)) Make a 2x2 grid of subplots with 2 rows and 2 columns. ax can be indexed to refer to a specific subplot, f.ex. the subplot on the first row and second column would be ax[0,1].
ax_twin = ax1.twinx() Makes an extra axis in a subplot (the subplot of ax1) such that data with different units can be plotted together in the same plot.
np.mean(data, axis = (dim_num)) or data.mean(axis = dim_num) Calculate the average of the data across the dim_num dimension of the array.

Load behavioral data: Pupil width and running speed

Exercises

# Extract pupil width
avg_pupil_width = dataset['pupil_width']

# Extract running speed
avg_run_speed = dataset['run_speed']

Load LFP data organized by trials

# extract LFP trial data

lfp_V1 = dataset['lfp_V1']

Example: Calculate the average1 absolute amplitude of the LFP in each trial. Plot the average LFP amplitude from channel index 5 together with the average running speed in each trial. 1The average over the whole time period in a trial, not the trial average.

lfp_avg_abs_amp = np.mean(np.abs(lfp_V1), axis = -1)

fig, ax = plt.subplots()

ax_run = ax
ax_run.plot(avg_run_speed, label = 'Running speed')

ax_lfp = ax.twinx()

ichan = 5
ax_lfp.plot(lfp_avg_abs_amp[ichan], color = 'g', label = 'LFP avg. absolute amplitude')

ax.set_xlabel('Trial #')
ax_run.set_ylabel('Running speed (a.u.)')
ax_lfp.set_ylabel('LFP (V)')

fig.legend();

Exercise: Plot the average absolute amplitude of the LFP from channel index 5 together with the average pupil width in each trial.

Solution
lfp_avg_abs_amp = np.mean(np.abs(lfp_V1), axis = -1)

fig, ax = plt.subplots()

ax_run = ax
ax_run.plot(avg_pupil_width, label = 'Pupil width')

ax_lfp = ax.twinx()

ichan = 5
ax_lfp.plot(lfp_avg_abs_amp[ichan], color = 'g', label = 'LFP avg. absolute amplitude')

ax.set_xlabel('Trial #')
ax_run.set_ylabel('Running speed (a.u.)')
ax_lfp.set_ylabel('LFP (V)')

fig.legend();

Exercise: Construct and apply a bandpass filter with a low cutoff at 50 Hz and a high cutoff at 70 to get the narrow gamma range LFP.

Solution
filter_order = 5 #
lowcut = 50 # Hz
highcut = 70 # Hz

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'bandpass', fs = sampling_frequency_lfp)
lfp_narrow_gamma = signal.filtfilt(b, a, lfp_V1, axis = -1)

Exercise: Plot the average amplitude of the filtered gamma range LFP together with the average pupil size in each trial.

Compare the overlap between pupil size and narrow gamma-band amplitude LFP in this plot to the overlap between pupil size and the total LFP amplitude in the exercise above. What do you notice?

Solution
fig, ax = plt.subplots()

ax_pupil = ax
ax_pupil.plot(avg_pupil_width, label = 'Pupil width')

ax_lfp = ax.twinx()

lfp_narrow_gamma_abs_amp = np.mean(np.abs(lfp_narrow_gamma[ichan]), axis = 1)

ax_lfp.plot(lfp_narrow_gamma_abs_amp, color = 'g', label = 'LFP avg. absolute amplitude')
ax.set_xlabel('Trial #')
ax_pupil.set_ylabel('Pupil width (a.u.)')
ax_lfp.set_ylabel('LFP (V)')

fig.legend()

Demo: Run the cell below the average running speed together with the average pupil size in each trial.

fig, ax = plt.subplots()

ax_run = ax
ax_run.plot(avg_run_speed, label = 'Running speed')

ax_pupil = ax.twinx()
ax_pupil.plot(avg_pupil_width, 'r', label = 'Pupil size')

ax.set_xlabel('Trial #')
ax_run.set_ylabel('Running speed (a.u.)')
ax_pupil.set_ylabel('Pupil size (a.u.)')

fig.legend()

Section 4: Apply bandstop (notch) filters to remove frequencies and to eliminate artifacts.

Overly consistent frequencies across a recording period - stripes across a whole spectrogram in all channels, for example - may indicate an artifact. In the figure below you can see an example of spectrograms computed for LFP that has such stripes. Those artifacts can be removed with a bandstop filter (also called a notch filter - a notch filter is simply a bandstop filter with a narrow range.)

Note: The illustration plot is from a different animal than the one used in this workshop.

Code Description
b, a = signal.butter(filter_order, [start_freq_band, end_freq_band], btype = 'bandpass', fs = sampling_frequency) Create the coefficients b and a to be used in the lowpass filtering of the signal.
w, h = signal.freqz(b, a, fs=sampling_frequency) Compute the frequency response of the filter with coefficients b and a. h is the frequency response (the filter’s magnitude at a given frequency), w is the frequencies at which the response is calculated.
lfp_trace = signal.filtfilt(b, a, ecp_trace)) Apply the constructed filter to the ecp signal at a specific channel using the coefficients b and a.
lfp_filtered = signal.filtfilt(b, a, lfp, axis = time_dim)) Apply the constructed filter to an array containing the LFP recorded from all channels using the coefficients b and a. When the LFP data is multidimensional, the axis the filter is applied to needs to be specified to be along the time dimension of the data.
plt.plot(x, y) Plot y values against x values.

Exercise: Construct, plot, and apply a bandstop butterworth filter with a low cutoff at 50 Hz and a high cutoff at 60 Hz to the LFP.

Solution
filter_order = 5
lowcut = 50 # Hz
highcut = 60 # Hz

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'bandstop', fs = sampling_frequency_lfp)

w, h = signal.freqz(b, a, fs=sampling_frequency_lfp) # freqz because digital filter

plt.plot(w, np.abs(h), label='Filter')

plt.title('Butterworth filter frequency response')
plt.xlabel('Frequency [Hz]')
plt.ylabel('Amplitude')
plt.margins(0, 0.1)
plt.grid(which='both', axis='both')
plt.axvline(lowcut, color='red', label='Lower band cutoff') # cutoff frequency
plt.axvline(highcut, color='green', label='Upper band cutoff') # cutoff frequency
plt.legend();

lfp_bandstopped = signal.filtfilt(b, a, lfp_whole_rec, axis = -1)

ichan = 5
lfp_single_chan = lfp_whole_rec[ichan]
bandstopped_lfp_single_chan = lfp_bandstopped[ichan]
plt.plot(time_lfp, lfp_single_chan, label = 'Original signal')
plt.plot(time_lfp, bandstopped_lfp_single_chan, label = 'Filtered signal', ls = '--')
plt.legend()
plt.xlabel('Time (s)')

Exercise: Construct and apply a bandstop butterworth filter with a low cutoff at 380 Hz and a high cutoff at 420 Hz to the LFP. Plot a single channel trace from the original LFP and the bandstop filtered LFP together.

Solution
filter_order = 5
lowcut = 300 # Hz
highcut = 350 # Hz
sampling_frequency = 1250

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'bandstop', fs = sampling_frequency)

lfp_bandstopped = signal.filtfilt(b, a, lfp_whole_rec, axis = -1)

ichan = 5
lfp_single_chan = lfp_whole_rec[ichan]
bandstopped_lfp_single_chan = lfp_bandstopped[ichan]
plt.plot(time_lfp, lfp_single_chan, label = 'Original signal')
plt.plot(time_lfp, bandstopped_lfp_single_chan, label = 'Filtered signal')
plt.legend()
plt.xlabel('Time (s)')

Exercise: In the figure provided in the introduction to this section, there’s a highly consistent and prominent horizontal stripe in the frequency spectrum at a little below 400 Hz in all channels. This is a candidate for an artifact. Remove this (potential) artifact from the LFP using a bandstop filter.

Solution
filter_order = 5
lowcut = 380 # Hz
highcut = 400 # Hz
sampling_frequency = 1250

# creating butter filter
b, a = signal.butter(filter_order, [lowcut, highcut], btype = 'bandstop', fs = sampling_frequency)

lfp_bandstopped = signal.filtfilt(b, a, lfp, axis = -1)