Statistical Significance of Power Spectra
Authors
A power spectrum may contain many peaks, but it’s hard to tell which ones are from noise and which are from a real signal. We can use statistical tests on the power spectra to figure out which peaks are unlikely to be just noise.
Setup
Import Libraries
import matplotlib.pyplot as plt
from scipy.stats import chi2
import scipy.optimize as opt
import numpy as np
import xarray as xr
from pathlib import Path
import pandas as pd
import pingouin as pg
from scipy import signalUtility Functions
Run the cell below, but feel free to ignore this code — it’s some utility functions we’ll use later for convenience.
def plot_signal_and_power_spectrum(time_vec, sig, freqs, psd):
'''
Makes figure with two subplots where the signal is plotted in one subplot and the power spectrum in the other
Args:
time_vec: array containing time points over which the signal is sampled
sig: array containing signal
freqs: frequencies for which the power spectral density is calculated
psd: power spectral density of the signal
'''
fig, ax = plt.subplots(ncols=2, figsize=(7,4))
ax[0].plot(time_vec, sig, label = 'Signal')
ax[0].set(xlabel = 'Time (s)', ylabel = 'Amplitude', title = 'Signal')
ax[1].loglog(freqs, psd, label = 'Power Spectrum')
ax[1].set(xlabel = 'Frequency (Hz)', ylabel = 'Power spectral density (normalized)', title = 'Power spectrum')
fig.tight_layout()
def red_noise_spectrum(freqs, A, beta):
'''
Estimates the power spectrum values as a function of frequency for a red noise signal
Args:
freqs: frequencies for which the power spectrum should be estimated
A: Amplitude of power spectrum
beta: Shape parameter for the function
returns:
spectrum: red noise power spectrum
'''
spectrum = A*(1.-beta**2)/(1.-2.*beta*np.cos(np.pi*(freqs)/freqs[-1])+beta**2)
return spectrum
def get_significance_threshold_psd(function, freqs, psd_original, n_samples, n_perseg, alpha=0.05,):
'''
Gets threshold for statistical significance for a power spectrum given a certain p-value
Args:
function: function to fit to create equivalent spectrum for a signal only consisting of noise
freqs: frequencies for which the power spectrum should has been estimated
psd_original: power spectrum computed for signal
n_samples: length of signal the power spectrum was calculated for
n_perseg: number of points per segment in Welch's method
alpha: p-value threshold for statistical significance
Return:
psd_expected: equivalent power spectrum if signal only contains noise (noise of the same colour as the original signal)
psd_significance_threshold: threshold for power spectrum peak to be significant
'''
# Fit function to red noise equivalent spectrum(excluding very low frequencies)
valid_idx = freqs > 0
popt, _ = opt.curve_fit(function, freqs[valid_idx], psd_original[valid_idx])
# Compute noise spectrum equivalent
psd_expected = function(freqs, *popt)
# calculate degrees of freedom (dof). dof = 2*number_of_samples/number_of_spectral_estimates = 2*len(your_signal)/n_perseg
dof = 2*n_samples/n_perseg
# Get Chi squared value for the threshold p-value
chi_sq_threshold = chi2.ppf(1-alpha, dof)
# Calculate the psd value at the threshold p-value
psd_significance_threshold = psd_expected*chi_sq_threshold/(dof-1)
return psd_expected, psd_significance_threshold
def get_p_value(idx_frequencies, psd_original, psd_expected, n_samples, n_perseg):
"""Calculates the p-values of the peaks at the selected frequencies"""
# calculate degrees of freedom (dof)
dof = 2*n_samples/n_perseg
chi2_values = (dof-1)*psd_original[idx_frequencies]/psd_expected[idx_frequencies]
# Compute p-values from chi-square CDF
p_values = 1 - chi2.cdf(chi2_values, df=dof)
return p_values
def plot_spectra_with_significance_threshold(freqs, psd_original, psd_expected, n_samples, n_perseg):
'''
Makes loglog plot of power spectrum together with red noise equivalant and thershold for significance.
Used to visualize which peaks are above statistically significant in a spectrum with a 1/f relationship
Args:
psd_original: power spectrum computed for signal
psd_expected: equivalent power spectrum if signal only contains noise (noise of the same colour as the original signal)
psd_significance_threshold: threshold for power spectrum peak to be significant
'''
# calculate degrees of freedom (dof)
dof = 2*n_samples/n_perseg
# Get Chi squared value for the threshold p-value
chi_sq_threshold95 = chi2.ppf(0.95, dof)
chi_sq_threshold99 = chi2.ppf(0.99, dof)
# Calculate the psd value at the threshold p-value
psd_significance_threshold95 = psd_expected*chi_sq_threshold95/(dof-1)
psd_significance_threshold99 = psd_expected*chi_sq_threshold99/(dof-1)
fig, ax = plt.subplots()
ax.loglog(freqs, psd_original, label = 'Original PSD')
ax.loglog(freqs, psd_expected, '--', color = 'r', label = 'Red noise PSD')
ax.loglog(freqs, psd_significance_threshold95, '--', color = 'g', label = f'95 % threshold')
ax.loglog(freqs, psd_significance_threshold99, '--', color = 'black', label = f'99 % threshold')
ax.set(xlabel = 'Frequency (Hz)', ylabel = 'Power spectral density (normalized)', title = 'Power spectrum of signal with\nthreshold for significance')
fig.legend(bbox_to_anchor = (0.9, 0.9))
class utils:
plot_signal_and_power_spectrum = plot_signal_and_power_spectrum
red_noise_spectrum = red_noise_spectrum
get_significance_threshold_psd = get_significance_threshold_psd
get_p_value = get_p_value
plot_spectra_with_significance_threshold = plot_spectra_with_significance_thresholdDownload dataset
import owncloud
owncloud.Client.from_public_link('https://uni-bonn.sciebo.de/s/rY4YCPXmkpespKG').get_file('/', 'dataset_session_754312389.nc')TrueSection 1: Primer on statistical testing
T-tests can be used to compare the means of two samples of data from normally-distributed populations and compute the probability that the populations have the same mean.
| Code | Statistical Test | Description |
|---|---|---|
pg.ttest(x, 0) |
One-Sample T-Test | “Is the mean of my data unlikely to be zero?” |
pg.ttest(x, y) |
Independent-Samples T-Test | “Do the means of x and y indicate they are unlikely to be from the same distribution?” |
expected, observed, stats = pg.chi2_independence(data, x='col_name_A', y='col_name_B') |
test | Is x a predictor of y? expected gives expected values if x is NOT a predictor of y, observed gives actual observed values of y for different values of x, stats gives statistical results of test given these expected and observed values. |
Exercises
Generate Data: Run the code below to create the dataset height containing fictional data on the height of a sample of 30 Germans, 30 Italians and 30 Dutch people.
randomizer = np.random.RandomState(17) # Makes sure the pseudorandom number generators reproduce the same data for us all.
height = pd.DataFrame()
height['german'] = randomizer.normal(0, 15, size=30)+170
height['dutch'] = randomizer.normal(0, 15, size=30)+180
height['italian'] = randomizer.normal(0, 15, size=30)+175
height.plot.box()Analyze the Data with T-Tests in Pingouin
Example: Independent Samples T-Test: Are the heights recorded for the Germans and the Dutch statistically different with p = .05 as the threshold for statistical significance?
pg.ttest(height['german'], height['dutch'])| T | dof | alternative | p-val | CI95% | cohen-d | BF10 | power | |
|---|---|---|---|---|---|---|---|---|
| T-test | -2.86907 | 58 | two-sided | 0.005734 | [-19.52, -3.48] | 0.740791 | 7.346 | 0.805465 |
Exercise: Independent Samples T-Test: Are the heights recorded for the Italians and the Dutch statistically different?
Solution
pg.ttest(height['italian'], height['dutch']) # no| T | dof | alternative | p-val | CI95% | cohen-d | BF10 | power | |
|---|---|---|---|---|---|---|---|---|
| T-test | -1.116856 | 58 | two-sided | 0.268661 | [-13.82, 3.92] | 0.288371 | 0.443 | 0.195583 |
Exercise: Independent Samples T-Test: Are the heights recorded for the Germans and the Italians statistically different?
Solution
pg.ttest(height['italian'], height['german']) # no| T | dof | alternative | p-val | CI95% | cohen-d | BF10 | power | |
|---|---|---|---|---|---|---|---|---|
| T-test | 1.469208 | 58 | two-sided | 0.14718 | [-2.37, 15.48] | 0.379348 | 0.646 | 0.30359 |
Generate Data: Run the code below to create the dataset treatment containing data from a fictional drug trial study. We’ll use this data in the next couple of exercises.
randomizer = np.random.RandomState(15)
treatment = pd.DataFrame()
treatment['drugA'] = randomizer.normal(0, 15, size=30)+10
treatment['drugB'] = randomizer.normal(0, 15, size=30)+1
treatment['control'] = randomizer.normal(0, 15, size=30)
treatment.plot.box()Exercise: Perform a t-test to determine whether the treatment effect of drug A is statistically significant by comparing it to the control group.
Solution
pg.ttest(treatment['drugA'], treatment['control'])| T | dof | alternative | p-val | CI95% | cohen-d | BF10 | power | |
|---|---|---|---|---|---|---|---|---|
| T-test | 2.221458 | 58 | two-sided | 0.030237 | [0.85, 16.32] | 0.573578 | 2.0 | 0.588876 |
Exercise: Perform a t-test to determine whether the treatment effect of drug B is statistically significant by comparing it to the control group.
Solution
pg.ttest(treatment['drugB'], treatment['control'])| T | dof | alternative | p-val | CI95% | cohen-d | BF10 | power | |
|---|---|---|---|---|---|---|---|---|
| T-test | 0.230379 | 58 | two-sided | 0.818608 | [-7.57, 9.54] | 0.059484 | 0.268 | 0.055902 |
Dataset: The Passengers on the Titanic
Below, we load a dataset containing (semi-fictitious) information about the passenger onboard the Titanic. Every row is a passenger, every column is a variable about that passenger. Please run the code and take a look at the dataset.
titanic = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/refs/heads/master/titanic.csv')
titanic| survived | pclass | sex | age | sibsp | parch | fare | embarked | class | who | adult_male | deck | embark_town | alive | alone | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 3 | male | 22.0 | 1 | 0 | 7.2500 | S | Third | man | True | NaN | Southampton | no | False |
| 1 | 1 | 1 | female | 38.0 | 1 | 0 | 71.2833 | C | First | woman | False | C | Cherbourg | yes | False |
| 2 | 1 | 3 | female | 26.0 | 0 | 0 | 7.9250 | S | Third | woman | False | NaN | Southampton | yes | True |
| 3 | 1 | 1 | female | 35.0 | 1 | 0 | 53.1000 | S | First | woman | False | C | Southampton | yes | False |
| 4 | 0 | 3 | male | 35.0 | 0 | 0 | 8.0500 | S | Third | man | True | NaN | Southampton | no | True |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 886 | 0 | 2 | male | 27.0 | 0 | 0 | 13.0000 | S | Second | man | True | NaN | Southampton | no | True |
| 887 | 1 | 1 | female | 19.0 | 0 | 0 | 30.0000 | S | First | woman | False | B | Southampton | yes | True |
| 888 | 0 | 3 | female | NaN | 1 | 2 | 23.4500 | S | Third | woman | False | NaN | Southampton | no | False |
| 889 | 1 | 1 | male | 26.0 | 0 | 0 | 30.0000 | C | First | man | True | C | Cherbourg | yes | True |
| 890 | 0 | 3 | male | 32.0 | 0 | 0 | 7.7500 | Q | Third | man | True | NaN | Queenstown | no | True |
891 rows × 15 columns
A test, like the t-test, can be used to test differences between groups. The difference lies in the use-cases - what kind of data the test is applied to. T-tests are applied when the dependent variable (for example height in the exercises above) is continuous. A test is applied when the dependent variable is categorical. In the exercises below, the dependent variable will be categorical. For example, if a passenger on Titanic survived or did not survive is a categorical outcome - there are no degrees of survival, either they survived or they didn’t. In such cases, a test should be used.
Exercise: Use a independence test to determine whether the chance of survival depended on which class a passenger traveled on. (You don’t need to look at more than the first row of the output in these exercises.)
Solution
expected, observed, stats = pg.chi2_independence(titanic, x='class', y='survived')
stats| test | lambda | chi2 | dof | pval | cramer | power | |
|---|---|---|---|---|---|---|---|
| 0 | pearson | 1.000000 | 102.888989 | 2.0 | 4.549252e-23 | 0.339817 | 1.0 |
| 1 | cressie-read | 0.666667 | 102.741005 | 2.0 | 4.898625e-23 | 0.339573 | 1.0 |
| 2 | log-likelihood | 0.000000 | 103.547124 | 2.0 | 3.273615e-23 | 0.340902 | 1.0 |
| 3 | freeman-tukey | -0.500000 | 105.128509 | 2.0 | 1.484685e-23 | 0.343496 | 1.0 |
| 4 | mod-log-likelihood | -1.000000 | 107.580420 | 2.0 | 4.357212e-24 | 0.347478 | 1.0 |
| 5 | neyman | -2.000000 | 115.324285 | 2.0 | 9.070888e-26 | 0.359767 | 1.0 |
Exercise: Did the chance of survival depend on sex?
Solution
expected, observed, stats = pg.chi2_independence(titanic, x='sex', y='survived')
stats| test | lambda | chi2 | dof | pval | cramer | power | |
|---|---|---|---|---|---|---|---|
| 0 | pearson | 1.000000 | 260.717020 | 1.0 | 1.197357e-58 | 0.540936 | 1.0 |
| 1 | cressie-read | 0.666667 | 260.413906 | 1.0 | 1.394102e-58 | 0.540621 | 1.0 |
| 2 | log-likelihood | 0.000000 | 266.344480 | 1.0 | 7.106137e-60 | 0.546743 | 1.0 |
| 3 | freeman-tukey | -0.500000 | 276.922737 | 1.0 | 3.517208e-62 | 0.557494 | 1.0 |
| 4 | mod-log-likelihood | -1.000000 | 293.475958 | 1.0 | 8.693443e-66 | 0.573915 | 1.0 |
| 5 | neyman | -2.000000 | 349.098487 | 1.0 | 6.659745e-78 | 0.625943 | 1.0 |
Exercise: Did the likelihood that a passenger was an adult male depend on which town they embarked from?
Solution
expected, observed, stats = pg.chi2_independence(titanic, x='embark_town', y='adult_male')
stats| test | lambda | chi2 | dof | pval | cramer | power | |
|---|---|---|---|---|---|---|---|
| 0 | pearson | 1.000000 | 11.053778 | 2.0 | 0.003978 | 0.111382 | 0.854413 |
| 1 | cressie-read | 0.666667 | 10.998406 | 2.0 | 0.004090 | 0.111103 | 0.852555 |
| 2 | log-likelihood | 0.000000 | 10.910445 | 2.0 | 0.004274 | 0.110658 | 0.849563 |
| 3 | freeman-tukey | -0.500000 | 10.863804 | 2.0 | 0.004375 | 0.110421 | 0.847955 |
| 4 | mod-log-likelihood | -1.000000 | 10.833262 | 2.0 | 0.004442 | 0.110266 | 0.846894 |
| 5 | neyman | -2.000000 | 10.819259 | 2.0 | 0.004473 | 0.110194 | 0.846406 |
Demo: How to find the p-value of a test by comparing to the distribution
As for the t-test, the p-value from a test is determined by comparing the score with a distribution of values. This distribution will tell you the probability of observing a certain score in a test. Below is an illustration of how this works.
The green line represents the score obtained from the last test above. The orange line is the cumulative probability density function (CDF) of scores (for two degrees of freedom). The black line from the cross section between the CDF and the observed value tells you the probability of observing this value when traced to the y-axis. This probability is the p-value. As could also be seen from the table with the test result in the last exercise above, the probability is only just above 0.
In the following sections, making comparisons to a distribution is how we determine whether peaks in the frequency spectra are statistically significant or not.
from scipy.stats import chi2
expected, observed, stats = pg.chi2_independence(titanic, x='embark_town', y='adult_male')
x = np.linspace(0,20,100)
plt.plot(x, 1-chi2.cdf(x, 2), color='orange', label = r'Cumulative density function of $\chi^2$ distribution')
idx = np.searchsorted(x, stats['chi2'][0])
plt.axhline(1-chi2.cdf(x, 2)[idx], ls = '--', color = 'black', label = r'Percentile of $\chi^2$ score')
plt.axvline(stats['chi2'][0], ls = '--', color = 'green', label = r'$\chi^2$ score of embark town vs adult male test')
plt.xlabel(r'$\chi^2$')
plt.ylabel(r'Probability of observing a $\chi^2$ score (the p-value)')
plt.title(r'Comparing $\chi^2$-value obtained from test to $\chi^2$-distribution to find p-value')
plt.xlim([0,20])
plt.legend(bbox_to_anchor = (1,0.5));Section 2: Test the statistical significance of peaks in the power spectrum
It’s not clear just from looking at a power spectrum which peaks are due to noise and which represent an oscillation due to a neurological phenomenon. By comparing the power spectrum calculated for the LFP to the power spectrum computed for an equivalent red noise spectrum, we can better assess which peaks are unlikely to only reflect noise.
| Code | Description |
|---|---|
freqs, psd = signal.welch(sig, fs = sampling_frequency, nperseg = some_num) |
Calculate the power spectral density (psd) of a signal sig using Welch’s method. nperseg sets the number of samples of the signal in each segment, default value is 256. |
psd_expected, psd_significance_threshold = utils.get_significance_threshold_psd(function, freqs, psd, n_perseg, n_samples, alpha) |
Get the threshold for a peak in the power spectrum psd to be significant at significance level alpha. For LFP, a function producing a red noise spectrum would typically be passed for function. psd_expected is the expected spectrum if the signal only contains noise. |
utils.plot_spectra_with_significance_threshold(freqs, psd_signal, psd_expected, psd_significance_threshold, n_perseg,n_samples, alpha) |
Plot the power spectrum together with the threshold for peaks to be significant at significance level alpha. |
Extract LFP data
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)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
lfp_V1 = dataset['lfp_V1']
lfp_whole_rec = dataset['lfp_whole_recording_V1']
channels_depth = dataset['channel_depth']
sampling_frequency_lfp = dataset.sampling_frequency_lfp # Hz
time_lfp = lfp_V1.time_lfp.values # msExample: A power spectrum is calculated for a generated signal in the cell below. Use an equivalent red noise spectrum to find and plot the threshold for statistically significant peaks in this power spectrum.
# provided
# Generate signal
np.random.seed(42)
sampling_frequency = 1000
duration = 1 # second
time_vec = np.arange(0, duration, 1/sampling_frequency)
f_peak = 25 # artificial peak
sig = 4*np.sin(2 * np.pi * f_peak * time_vec) + np.cumsum(np.random.randn(len(time_vec))) # 1/f noise + peak
# compute the power spectrum
n_perseg = 256
freqs, psd_signal = signal.welch(sig, fs = sampling_frequency, noverlap=0)
# normalize the power spectrum
psd_signal_norm = psd_signal/psd_signal.max()
# plot signal and power spectrum
plot_signal_and_power_spectrum(time_vec, sig, freqs, psd_signal_norm)Solution
# solution
n_samples = sig.shape[-1]
psd_expected, psd_significance_threshold = utils.get_significance_threshold_psd(red_noise_spectrum, freqs, psd_signal_norm, n_samples, n_perseg,)
# plot computed power spectrum together with threshold for peaks
utils.plot_spectra_with_significance_threshold(freqs, psd_signal_norm, psd_expected, n_samples, n_perseg)Exercise: Use an equivalent red noise spectrum to find and plot the threshold for statistically significant peaks in the power spectrum calculated for the synthetic signal below.
# provided
# Generate signal
np.random.seed(42)
sampling_frequency = 1000
duration = 1 # second
time_vec = np.arange(0, duration, 1/sampling_frequency)
f_peak = 100 # artificial peak
sig = 4*np.sin(2 * np.pi * f_peak * time_vec) + np.cumsum(np.random.randn(len(time_vec))) # 1/f noise + peak
# compute the power spectrum
n_perseg = 256
freqs, psd_signal = signal.welch(sig, fs = sampling_frequency, noverlap=0)
# normalize the power spectrum
psd_signal_norm = psd_signal/psd_signal.max()
# plot signal and power spectrum
plot_signal_and_power_spectrum(time_vec, sig, freqs, psd_signal_norm)Solution
# solution
n_samples = sig.shape[-1]
psd_expected, psd_significance_threshold = utils.get_significance_threshold_psd(red_noise_spectrum, freqs, psd_signal_norm, n_samples, n_perseg,)
# plot computed power spectrum together with threshold for peaks
utils.plot_spectra_with_significance_threshold(freqs, psd_signal_norm, psd_expected, n_samples, n_perseg)Exercise: Use an equivalent red noise spectrum to find and plot the threshold for statistically significant peaks in the power spectrum calculated from the LFP trace from channel index 5 in the first trial.
Hint: Here, what needs to be changed is the n_samples parameter (to the number of samples in the LFP trace) and that it’s the psd_lfp_trace_norm that goes into the utils.get_significance_threshold_psd and utils.plot_spectra_with_significance_threshold functions.
# provided
itrial = 0
ichan = 5
lfp_trace = lfp_V1[ichan, itrial]
time_lfp = lfp_V1.time_lfp
# compute the power spectrum
n_perseg = 256
freqs, psd_lfp_trace = signal.welch(lfp_trace, fs = sampling_frequency_lfp, nperseg=n_perseg, noverlap=0)
# normalize the power spectrum
psd_lfp_trace_norm = psd_lfp_trace/psd_lfp_trace.max()
# plot signal and power spectrum
utils.plot_signal_and_power_spectrum(time_lfp, lfp_trace, freqs, psd_lfp_trace_norm)Solution
# solution
n_samples = lfp_trace.shape[-1]
psd_expected, psd_significance_threshold = utils.get_significance_threshold_psd(red_noise_spectrum, freqs, psd_lfp_trace_norm, n_samples, n_perseg,)
# plot computed power spectrum together with threshold for peaks
utils.plot_spectra_with_significance_threshold(freqs, psd_lfp_trace_norm, psd_expected, n_samples, n_perseg)Exercise: Use an equivalent red noise spectrum to find and plot the threshold for statistically significant peaks in the trial-averaged power spectrum (psd_trial_avg_norm) calculated from the LFP traces from channel index 5.
Hint: The psd_trial_avg_norm variables needs to go into the get_significance_threshold_psd and plot_spectra_with_significance_threshold functions.
# provided
ichan = 5
lfp_traces = lfp_V1[ichan].values
n_perseg = 256
freqs, psd_all_trials = signal.welch(lfp_traces, fs = sampling_frequency_lfp, nperseg=n_perseg, noverlap = 0)
psd_trial_avg = np.mean(psd_all_trials, axis=0)
# normalize the power spectrum
psd_trial_avg_norm = psd_trial_avg/psd_trial_avg.max()Solution
# solution
n_samples = lfp_trace.shape[-1]
psd_expected, psd_significance_threshold = utils.get_significance_threshold_psd(red_noise_spectrum, freqs, psd_trial_avg_norm, n_samples, n_perseg,)
# plot computed power spectrum together with threshold for peaks
utils.plot_spectra_with_significance_threshold(freqs, psd_trial_avg_norm, psd_expected, n_samples, n_perseg)Example: Find the frequencies of the peak(s) in the power spectrum that are above the threshold for statistical significance with p = 0.05 as the limit. Hint: Find where the original PSD is greater than the threshold for significance and use that as an index or a mask for the frequencies.
Solution
# solution
# set threshold for significance
alpha = 0.05
n_samples = lfp_traces.shape[-1]
psd_expected, psd_significance_threshold = utils.get_significance_threshold_psd(red_noise_spectrum, freqs, psd_trial_avg_norm,
n_samples, n_perseg, alpha=alpha)
# find frequencies where the power spectrum is above the threshold for significance
idx_significant_freqs = np.where((psd_trial_avg_norm > psd_significance_threshold))[0]
freqs[idx_significant_freqs]array([53.7109375, 58.59375 ])Exercise: Change the alpha parameter (the threshold for significance) to p = 0.01 and find the frequencies of the peak(s) in the power spectrum that are above the threshold for statistical significance.
Solution
# solution
# set threshold for significance
alpha = 0.01
n_samples = lfp_traces.shape[-1]
psd_expected, psd_significance_threshold = utils.get_significance_threshold_psd(red_noise_spectrum, freqs, psd_trial_avg_norm, n_samples, n_perseg,)
# find frequencies where the power spectrum is above the threshold for significance
idx_significant_freqs = np.where((psd_trial_avg_norm > psd_significance_threshold))[0]
freqs[idx_significant_freqs]array([53.7109375, 58.59375 ])Section 3: Correcting for multiple comparisons - False Discovery Rate
Get p-values and correct for multiple comparisons.
| Code | Description |
|---|---|
np.where(array==value)[0] |
Returns the indeces in an array where the condition is met. |
np.sum(array < some_number) |
Counts the number of elements in the array that are smaller than some_number. |
p_values = utils.get_p_value(idx_freqs_to_test, psd_original,psd_expected, n_samples,n_perseg) |
Calculates the p-values of the peaks in the spectrum psd_original at the frequencies selected by idx_freqs_to_test. |
p_values_adjusted =false_discovery_control(p_values) |
Adjust the p-values to account for multiple statistical tests. |
np.arange(start, stop, step) |
Generates an array of values evenly spaced from start to stop, with the spacing given by step |
Exercises
from scipy.stats import false_discovery_control# provided
lfp_traces = lfp_V1[ichan].values
n_perseg = 256
freqs, psd_all_trials = signal.welch(lfp_traces, fs = sampling_frequency_lfp, nperseg=n_perseg, noverlap=0)
psd_trial_avg = np.mean(psd_all_trials, axis=0)
psd_trial_avg_norm = psd_trial_avg/psd_trial_avg.max()
n_samples = lfp_traces.shape[-1]
psd_expected, psd_significance_threshold = utils.get_significance_threshold_psd(red_noise_spectrum, freqs, psd_trial_avg_norm, n_samples, n_perseg,)Exercise: Get the p-value of the power spectral density at 4 Hz (the frequency at index 1 in the power spectrum).
Solution
# solution
index_peak = 1
freq_to_test = freqs[index_peak]
idx_freqs_to_test = np.where(freqs==freq_to_test)[0]
p_values = utils.get_p_value(idx_freqs_to_test, psd_trial_avg_norm, psd_expected, n_samples, n_perseg)
print(f'Frequency: {np.round(freq_to_test, 3)}, p_values: {p_values}')Frequency: 4.883, p_values: [0.53966323]Exercise: Change the index_peak variable to 12 to get the p-value for the peak at 58 Hz.
Solution
freq_to_test = freqs[12]
idx_freqs_to_test = np.where(freqs==freq_to_test)[0]
p_values = utils.get_p_value(idx_freqs_to_test, psd_trial_avg_norm, psd_expected, n_samples, n_perseg)
print(f'Frequency: {np.round(freq_to_test, 3)}, p_values: {p_values}')Frequency: 58.594, p_values: [0.00331165]Exercise: Pick a frequency yourself and check the p-value for the PSD at this frequency.
Solution
freq_to_test = freqs[3]
idx_freqs_to_test = np.where(freqs==freq_to_test)[0]
p_values = utils.get_p_value(idx_freqs_to_test, psd_trial_avg_norm, psd_expected, n_samples, n_perseg)
print(f'Frequency: {np.round(freq_to_test, 3)}, p_values: {p_values}')Frequency: 14.648, p_values: [0.69768094]Example: For the list of p-values below, use the function false_discovery_control to adjust the p-values for multiple tests and print the adjusted values.
pvalues = [0.01,0.001, 0.05, 0.20, 0.15, 0.15]
print(false_discovery_control(pvalues))[0.03 0.006 0.1 0.2 0.18 0.18 ]Example: Calculate the p-values for all frequencies in the spectrum and print them.
freq_to_test = freqs[12]
idx_freqs_to_test = np.arange(len(freqs))
p_values = utils.get_p_value(idx_freqs_to_test, psd_trial_avg_norm, psd_expected, n_samples, n_perseg)
p_valuesarray([0.99994182, 0.53966323, 0.37461806, 0.69768094, 0.8437394 ,
0.83954932, 0.78209327, 0.63652384, 0.55241563, 0.54448858,
0.35513472, 0.02124864, 0.00331165, 0.15382451, 0.46759613,
0.49673074, 0.38721897, 0.40848411, 0.45124855, 0.51715938,
0.59512564, 0.63416958, 0.59640879, 0.5247559 , 0.58267034,
0.70482311, 0.69319825, 0.70374299, 0.81361991, 0.88745834,
0.86759701, 0.89620194, 0.89785376, 0.88179782, 0.87863781,
0.86271158, 0.87561069, 0.91624073, 0.90923992, 0.91074673,
0.90154141, 0.94412918, 0.95738849, 0.95157662, 0.94073517,
0.93839015, 0.94689053, 0.9439922 , 0.94951468, 0.95963682,
0.96086268, 0.95994646, 0.97019623, 0.9698779 , 0.95351398,
0.95096987, 0.95822741, 0.97178855, 0.96026926, 0.95326415,
0.95414415, 0.96522751, 0.95835848, 0.94013732, 0.94623332,
0.94633128, 0.94752047, 0.95072628, 0.95313756, 0.95460715,
0.9626944 , 0.96588426, 0.97225063, 0.97406425, 0.95528308,
0.96232144, 0.96616054, 0.96780375, 0.96891313, 0.96513378,
0.96866497, 0.96789064, 0.96984792, 0.96485843, 0.97035351,
0.97243048, 0.95669288, 0.95163354, 0.94801019, 0.94842228,
0.95139092, 0.96563306, 0.96390652, 0.95258153, 0.94662519,
0.95205831, 0.95766357, 0.97200653, 0.97421254, 0.97004718,
0.9791819 , 0.96709422, 0.95781304, 0.96658175, 0.97823302,
0.98888737, 0.99755821, 0.99946247, 0.99992888, 0.99998283,
0.99998806, 0.99999233, 0.99999484, 0.99999476, 0.99999614,
0.99999634, 0.9999972 , 0.99999705, 0.9999949 , 0.99999461,
0.99999567, 0.99999626, 0.99999656, 0.99999526, 0.999992 ,
0.99999324, 0.99999346, 0.99999458, 0.99999995])Exercise: How many frequencies have p-values below 0.05 according to this naive test?
Solution
np.sum(p_values < 0.05)2Exercise: Above we tested every frequency in the spectrum, which means that we carried out 129 tests for significance. With this many tests, we would actually expect up to 6 frequencies to have peaks with p-values < 0.05 even if the peaks are only due to noise. Use the false_discovery_control function to adjust the p_values obtained above for doing multiple tests.
Solution
print(false_discovery_control(p_values))[0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.42720283 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995 0.99999995 0.99999995 0.99999995
0.99999995 0.99999995 0.99999995]Exercise: How many of the adjusted p-values are <0.05?
Solution
sum(false_discovery_control(p_values) < 0.05)0