Filtering Methods in Analysis of LFP
Authors
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.
Setup
Import Libraries
import xarray as xr
from pathlib import Path
import matplotlib.pyplot as plt
from scipy import signal
import numpy as npUtility 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_plotDownload dataset
import owncloud
owncloud.Client.from_public_link('https://uni-bonn.sciebo.de/s/M56DCkpyCJ9FRoi', folder_password="ibots"
).get_file('/', 'dataset_session_754312389.nc')TrueSection 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- 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)int640 -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)int320 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)float641.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)float641.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)int32951782498 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)int32951795075 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)int32951791074 951791093 ... 951792163
array([951791074, 951791093, 951791202, 951791214, 951791273, 951791305, 951791297, 951791401, 951791422, 951791467, 951791442, 951791453, 951792163], dtype=int32) - stimulus_start_times(stimulus_start_times)float641.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)float641.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)int32850144538 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)float641.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)float641.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)int160 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)int160 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)int160 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)float6439.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)float641.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_depthPandasIndex
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_nrPandasIndex
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_lfpPandasIndex
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_csdPandasIndex
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_lfpPandasIndex
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_ecpPandasIndex
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_LGNPandasIndex
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_spikesPandasIndex
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_V1PandasIndex
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_LMPandasIndex
PandasIndex(Index([951791074, 951791093, 951791202, 951791214, 951791273, 951791305, 951791297, 951791401, 951791422, 951791467, 951791442, 951791453, 951792163], dtype='int32', name='unit_id_LM')) - stimulus_start_timesPandasIndex
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_timesPandasIndex
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_idPandasIndex
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_lfpExercise: 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_ecpExercise: 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)