OSCR

GluN2A Enables Noradrenergic Control of Prefrontal Oscillations and Cognitive Flexibility.

Code ↔ Paper

2 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 2 matches
  1. [1] § Materials and Methods › Multielectrode array (MEA) recordings ↔ MEA LFP _ Open OnDemand_LC_mPFC.ipynb, lines 375–439 · score 0.69 · power spectral density, Absolute power, Multitaper, windows, band, gamma
  2. [2] § Materials and Methods › Multielectrode array (MEA) recordings ↔ MEA LFP _ Open OnDemand_NE_Pharmacology.ipynb, lines 374–438 · score 0.69 · power spectral density, Absolute power, Multitaper, windows, band, gamma

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Jupyter notebook · 1,495 lines · 52 KB · no license · 1 match

  1. # %%
  2. from scipy.signal import butter, lfilter
  3. from scipy import signal
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. import seaborn as sns
  7. import sklearn as sk
  8. import h5py
  9. import os.path
  10. import pandas as pd
  11. from sklearn.decomposition import PCA
  12. from sklearn.cluster import KMeans
  13. from sklearn.cluster import MiniBatchKMeans
  14. from sklearn.metrics import silhouette_score
  15. from sklearn.cluster import DBSCAN
  16. from sklearn import metrics
  17. import librosa.display
  18. # from kneed import KneeLocator
  19. import os
  20. import urllib
  21. import numpy as np
  22. from scipy.io import loadmat
  23. from tensorpac import Pac, EventRelatedPac, PreferredPhase
  24. from tensorpac.utils import PeakLockedTF, PSD, ITC, BinAmplitude
  25. from tensorpac.signals import pac_signals_wavelet
  26. import matplotlib.pyplot as plt
  27. plt.style.use('seaborn-poster')
  28. # %%
  29. save_path = "/home/hosseini/Desktop/2024/07.08.2024/Preprocessed/Output"
  30. save_pathh = "/home/hosseini/Desktop/2024/07.08.2024/Preprocessed/Output/mPFC_Slice2_Multitaper"
  31. save_path_coherence = "/home/hosseini/Desktop/2024/07.08.2024/Preprocessed/Output/mPFC_coherence_Slice2"
  32. name = '\_07082024_mPFC_Slice2'
  33. name2 = str('_07082024_mPFC_Slice2')
  34. strain = 'WT'
  35. # filenames
  36. data_path = "/home/hosseini/Desktop/2024/07.08.2024/Preprocessed"
  37. file_xtrain_vehicle = data_path + r"/2024-07-08mPFC_Slice2_LC_to_mPFC_CAG_ChR2_eYFP_10Second_Stim_A_Baseline__0002.h5" #### Vehicle
  38. file_xtrain_GFC = data_path + r"/2024-07-08mPFC_Slice2_LC_to_mPFC_CAG_ChR2_eYFP_10Second_Stim_A_Stimulation_.h5" #### NE
  39. file_xtrain_GFC_DOB = data_path + r"/2024-07-08mPFC_Slice2_LC_to_mPFC_CAG_ChR2_eYFP_10Second_Stim_A_Yohimbine+Stimulation__0002.h5" #### NE + DRUG
  40. # file_xtrain_vehicle2 = data_path + r"/2024-06-17__Slice2_WT_mPFC_NE2__0001.h5" #### NE2
  41. ################################################################### Baseline
  42. with h5py.File(file_xtrain_vehicle, 'r') as hdf:
  43. dt1 = hdf.get('Data/Recording_0/AnalogStream/Stream_0')
  44. dt1_items = list(dt1.items())
  45. ChannelData_vehicle = np.array(dt1.get('ChannelData'))
  46. print(ChannelData_vehicle.shape)
  47. ################################################################### Kanaite
  48. with h5py.File(file_xtrain_GFC, 'r') as hdf:
  49. dt1 = hdf.get('Data/Recording_0/AnalogStream/Stream_0')
  50. dt1_items = list(dt1.items())
  51. ChannelData_GFC = np.array(dt1.get('ChannelData'))
  52. print(ChannelData_GFC.shape)
  53. ################################################################### Kanaite + Bicuculline
  54. with h5py.File(file_xtrain_GFC_DOB, 'r') as hdf:
  55. dt1 = hdf.get('Data/Recording_0/AnalogStream/Stream_0')
  56. dt1_items = list(dt1.items())
  57. ChannelData_GFC_DOB = np.array(dt1.get('ChannelData'))
  58. print(ChannelData_GFC_DOB.shape)
  59. sf = 20000 # sampling requency
  60. print(f'time recorded in min: {((len(ChannelData_vehicle[1])/sf)/60)}')
  61. # %%
  62. # dim_x=60 # number of electrodes
  63. # dim_y=2401980 # time in seconds with fs 20k
  64. data_concat = np.concatenate((ChannelData_vehicle[:,:],
  65. ChannelData_GFC[:,:],
  66. ChannelData_GFC_DOB[:,:],
  67. # ChannelData_vehicle2[:,:],
  68. ), axis=1)
  69. np.shape(data_concat)
  70. # %% [markdown]
  71. # # Lowpass filter & downsample the data
  72. # %%
  73. def filter_data(data, low, high, sf, order=2):
  74. # Determine Nyquist frequency
  75. nyq = sf/2
  76. # Set bands
  77. low = low/nyq
  78. high = high/nyq
  79. # Calculate coefficients
  80. b, a = butter(order, [low, high], btype='bandpass',)
  81. # Filter signal
  82. filtered_data = lfilter(b, a, data)
  83. return filtered_data
  84. def downsample_data(data, sf, target_sf):
  85. # Calculate the initial downsampling factor
  86. factor = int(sf / target_sf)
  87. # Check if downsampling is necessary
  88. if factor < 1:
  89. raise ValueError("Target sampling frequency must be less than original sampling frequency.")
  90. # Downsample using multiple steps if necessary
  91. data_down = data
  92. while factor > 1:
  93. # Apply decimation in chunks to avoid excessive decimation
  94. step_factor = min(factor, 10) # Decimate in steps of max 10 to avoid aliasing
  95. data_down = signal.decimate(data_down, step_factor, ftype='fir')
  96. sf = sf / step_factor # Update the sampling rate after each decimation
  97. factor = int(sf / target_sf) # Recalculate the factor for the next iteration
  98. return data_down, int(sf)
  99. # %% [markdown]
  100. # # Apply the eprevious steps (notch 60, filter, downsample) to Combined data
  101. # %%
  102. from scipy import signal
  103. from scipy.signal import butter, lfilter
  104. sf = 20000 # sampling frequency
  105. target_sf = 2000 # target frequency for downsampling
  106. f0 = 60.0 # Frequency to be removed from signal (Hz)
  107. w0 = f0/(target_sf/2) # Normalized Frequency
  108. Q = 30 # Quality factor
  109. # Design notch filter
  110. b, a = signal.iirnotch(w0, Q)
  111. start = 0
  112. end = int(len(data_concat[0])/sf)
  113. ################################################### all data comined
  114. clean_data = []
  115. lfp_power = []
  116. # clean_data_power = []
  117. for chanlNo in range(len(data_concat)):
  118. data = data_concat[chanlNo].ravel()
  119. # compare the raw data with the filtered signal
  120. spike_data = filter_data(data[start*sf:end*sf], low=4, high=400, sf=sf) # Band pass filter the data for LFP analysis
  121. # Now we use the above function to downsample the signal.
  122. lfp_data, sf_lfp = downsample_data(spike_data, sf=sf, target_sf=target_sf)
  123. lfp_power.append(signal.welch(lfp_data, fs=sf_lfp, window='hamming', nperseg=1024, scaling='spectrum'))
  124. clean_data.append(signal.lfilter(b, a, lfp_data))
  125. # %% [markdown]
  126. # # Multitaper Spectrogram
  127. # %%
  128. %run /home/hosseini/Desktop/multitaper_spectrogram.py
  129. # %%
  130. # from multitaper_spectrogram_python import multitaper_spectrogram # import multitaper_spectrogram function from the multitaper_spectrogram_python.py file
  131. # Set spectrogram params
  132. fs = 2000 # Sampling Frequency
  133. frequency_range = [1, 250] # Limit frequencies from 0 to 25 Hz
  134. time_bandwidth = 5 # Set time-half bandwidth
  135. num_tapers = 8 # Set number of tapers (optimal is time_bandwidth*2 - 1)
  136. window_params = [4, 1] # Window size is 4s with step size of 1s
  137. min_nfft = 0 # No minimum nfft
  138. detrend_opt = 'constant' # detrend each window by subtracting the average
  139. multiprocess = True # use multiprocessing
  140. n_jobs = -1 # use 3 cores in multiprocessing
  141. weighting = 'unity' # weight each taper at 1
  142. plot_on = True # plot spectrogram
  143. clim_scale = False # do not auto-scale colormap
  144. verbose = False # print extra info
  145. xyflip = False # do not transpose spect output matrix
  146. # %%
  147. import warnings
  148. warnings.filterwarnings('ignore')
  149. import os.path
  150. #first we filter the raw data and then downsample the raw data
  151. from matplotlib.backends.backend_pdf import PdfPages
  152. pdf_pages = PdfPages(os.path.join(save_pathh,'Multitaper.pdf'))
  153. for i in range (len(clean_data)):
  154. spect, stimes, sfreqs = multitaper_spectrogram(clean_data[i],
  155. fs,
  156. frequency_range,
  157. time_bandwidth,
  158. num_tapers,
  159. window_params, min_nfft, detrend_opt,
  160. multiprocess, n_jobs,
  161. weighting,
  162. # plot_on,
  163. clim_scale,
  164. verbose, xyflip)
  165. # For saving the graphs
  166. spect_data = spect
  167. clim = np.percentile(spect_data, [5, 95]) # Scale colormap from 5th percentile to 95th
  168. # freqs, times, spectrogram = signal.spectrogram(data)#######
  169. fig = plt.figure(figsize=(10, 5))
  170. librosa.display.specshow(librosa.amplitude_to_db(spect, ref=np.max), x_axis='time', y_axis='linear',
  171. x_coords=stimes, y_coords=sfreqs, shading='auto',
  172. cmap= "jet"
  173. )
  174. plt.colorbar(label='Power [dB]')
  175. plt.xlabel("Time [MM:SS]")
  176. plt.ylabel("Frequency [Hz]")
  177. if clim_scale:
  178. plt.clim(clim) # actually change colorbar scale
  179. plt.title(f'Electrod No. : {i}')
  180. plt.axhline(y = 4, color = 'w', linestyle = '--', linewidth= 1.0 )
  181. plt.axhline(y = 10, color = 'w', linestyle = '--', linewidth= 1.0 )
  182. plt.axhline(y = 30, color = 'w', linestyle = '--', linewidth= 1.0 )
  183. plt.axvline(x = 5*60, color = 'w', linestyle = '--', linewidth= 2.0 ) # 5 mins
  184. plt.axvline(x = 10*60, color = 'w', linestyle = '--', linewidth= 2.0 )
  185. # plt.axvline(x = 15*60, color = 'w', linestyle = '--', linewidth= 2.0 )
  186. pdf_pages.savefig(fig)
  187. ######## Saving
  188. # plt.savefig(os.path.join(save_pathh , 'ElectrodeNo{}.svg'.format(i)),bbox_inches = 'tight')
  189. pdf_pages.close()
  190. # %% [markdown]
  191. # # STOP! DELETE UNWANTED CHANNELS
  192. # %%
  193. unwanted_num = [0,1,2,3,4,5,6,7,10,11,12,13,15,19,20,23,27,32,35,38,51,54,
  194. ]# channels to be removed
  195. # List of numbers with corresponding indices as shown in the image
  196. numbers_dict = {
  197. 54: 0, 56: 1, 55: 2, 46: 3, 45: 4, 44: 5, 36: 6, 35: 7, 34: 8, 26: 9,
  198. 16: 10, 25: 11, 15: 12, 24: 13, 14: 14, 13: 15, 23: 16, 12: 17, 22: 18,
  199. 11: 19, 21: 20, 33: 21, 32: 22, 31: 23, 43: 24, 42: 25, 41: 26, 52: 27,
  200. 51: 28, 53: 29, 63: 30, 61: 31, 62: 32, 71: 33, 72: 34, 73: 35, 81: 36,
  201. 82: 37, 83: 38, 91: 39, 101: 40, 92: 41, 102: 42, 93: 43, 103: 44,
  202. 104: 45, 94: 46, 105: 47, 95: 48, 106: 49, 96: 50, 84: 51, 85: 52,
  203. 86: 53, 74: 54, 75: 55, 76: 56, 65: 57, 66: 58, 64: 59
  204. }
  205. # Function to get corresponding indices, excluding unwanted numbers
  206. def get_corresponding_values(selected_nums):
  207. filtered_values = [num for num in selected_nums if num in unwanted_num]
  208. return filtered_values
  209. # L2/3
  210. L_2_3 = get_corresponding_values([
  211. 19, 20, 23, 26, 28, 31, 33, 36, 39, 40,
  212. # 10, 9, 6, 3, 1, 58, 56, 53, 50, 49
  213. ])
  214. # L5
  215. L_5 = get_corresponding_values([
  216. # 19, 20, 23, 26, 28, 31, 33, 36, 39, 40,
  217. 17, 18, 22, 25, 27, 32, 34, 41, 42,
  218. 15, 16, 24, 29, 30, 35, 38, 43, 44,
  219. 13, 8, 0, 59, 54, 51, 46, 45, 12,
  220. 12, 11, 7, 4, 2, 57, 55, 52, 48, 47,
  221. 10, 9, 6, 3, 1, 58, 56, 53, 50, 49
  222. ]
  223. )
  224. print(L_2_3, L_5)
  225. # %%
  226. layer ='Layer2_3'
  227. import warnings
  228. warnings.filterwarnings('ignore')
  229. import os.path
  230. #first we filter the raw data and then downsample the raw data
  231. from matplotlib.backends.backend_pdf import PdfPages
  232. pdf_pages = PdfPages(os.path.join(save_pathh,'Multitaper_Layer2_3.pdf'))
  233. for i in L_2_3:
  234. spect, stimes, sfreqs = multitaper_spectrogram(clean_data[i],
  235. fs,
  236. frequency_range,
  237. time_bandwidth,
  238. num_tapers,
  239. window_params, min_nfft, detrend_opt,
  240. multiprocess, n_jobs,
  241. weighting,
  242. # plot_on,
  243. clim_scale,
  244. verbose, xyflip)
  245. # For saving the graphs
  246. spect_data = spect
  247. clim = np.percentile(spect_data, [5, 95]) # Scale colormap from 5th percentile to 95th
  248. # freqs, times, spectrogram = signal.spectrogram(data)#######
  249. fig = plt.figure(figsize=(10, 5))
  250. librosa.display.specshow(librosa.amplitude_to_db(spect, ref=np.max), x_axis='time', y_axis='linear',
  251. x_coords=stimes, y_coords=sfreqs, shading='auto',
  252. cmap= "jet"
  253. )
  254. # plt.colorbar(label='Power [dB]')
  255. plt.xlabel("Time [MM:SS]")
  256. plt.ylabel("Frequency [Hz]")
  257. plt.title(f'Electrod No. : {i}')
  258. plt.axhline(y = 4, color = 'w', linestyle = '--', linewidth= 2.0 )
  259. plt.axhline(y = 10, color = 'w', linestyle = '--', linewidth= 2.0 )
  260. plt.axhline(y = 25, color = 'w', linestyle = '--', linewidth= 2.0 )
  261. plt.axvline(x = 5*60 ,color = 'w', linestyle = '--', linewidth= 2.0 ) #
  262. plt.axvline(x = 10*60 ,color = 'w', linestyle = '--', linewidth= 2.0 ) #
  263. pdf_pages.savefig(fig)
  264. ######## Saving
  265. plt.savefig(os.path.join(save_pathh , 'ElectrodeNo{}_Layer2_3.svg'.format(i)),bbox_inches = 'tight')
  266. pdf_pages.close()
  267. # %%
  268. layer ='Layer5'
  269. import warnings
  270. warnings.filterwarnings('ignore')
  271. import os.path
  272. #first we filter the raw data and then downsample the raw data
  273. from matplotlib.backends.backend_pdf import PdfPages
  274. pdf_pages = PdfPages(os.path.join(save_pathh,'Multitaper_Layer5.pdf'))
  275. for i in L_5:
  276. spect, stimes, sfreqs = multitaper_spectrogram(clean_data[i],
  277. fs,
  278. frequency_range,
  279. time_bandwidth,
  280. num_tapers,
  281. window_params, min_nfft, detrend_opt,
  282. multiprocess, n_jobs,
  283. weighting,
  284. # plot_on,
  285. clim_scale,
  286. verbose, xyflip)
  287. # For saving the graphs
  288. spect_data = spect
  289. clim = np.percentile(spect_data, [5, 95]) # Scale colormap from 5th percentile to 95th
  290. # freqs, times, spectrogram = signal.spectrogram(data)#######
  291. fig = plt.figure(figsize=(10, 5))
  292. librosa.display.specshow(librosa.amplitude_to_db(spect, ref=np.max), x_axis='time', y_axis='linear',
  293. x_coords=stimes, y_coords=sfreqs, shading='auto',
  294. cmap= "jet"
  295. )
  296. # plt.colorbar(label='Power [dB]')
  297. plt.xlabel("Time [MM:SS]")
  298. plt.ylabel("Frequency [Hz]")
  299. plt.title(f'Electrod No. : {i}')
  300. plt.axhline(y = 4, color = 'w', linestyle = '--', linewidth= 2.0 )
  301. plt.axhline(y = 10, color = 'w', linestyle = '--', linewidth= 2.0 )
  302. plt.axhline(y = 25, color = 'w', linestyle = '--', linewidth= 2.0 )
  303. plt.axvline(x = 5*60 ,color = 'w', linestyle = '--', linewidth= 2.0 ) #
  304. plt.axvline(x = 10*60 ,color = 'w', linestyle = '--', linewidth= 2.0 ) #
  305. pdf_pages.savefig(fig)
  306. ######## Saving
  307. plt.savefig(os.path.join(save_pathh , 'ElectrodeNo{}_Layer5.svg'.format(i)),bbox_inches = 'tight')
  308. pdf_pages.close()
  309. # %%
  310. # %%
  311. # delta = [1, 4]
  312. # theta = [4, 10]
  313. # beta = [10, 30]
  314. # gamma = [30, 80]
  315. # ref = https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4009705/
  316. def bandpower(data, sf, band, method='welch', window_sec=None, relative=False):
  317. """Compute the average power of the signal x in a specific frequency band.
  318. Requires MNE-Python >= 0.14.
  319. Parameters
  320. ----------
  321. data : 1d-array
  322. Input signal in the time-domain.
  323. sf : float
  324. Sampling frequency of the data.
  325. band : list
  326. Lower and upper frequencies of the band of interest.
  327. method : string
  328. Periodogram method: 'welch' or 'multitaper'
  329. window_sec : float
  330. Length of each window in seconds. Useful only if method == 'welch'.
  331. If None, window_sec = (1 / min(band)) * 2.
  332. relative : boolean
  333. If True, return the relative power (= divided by the total power of the signal).
  334. If False (default), return the absolute power.
  335. Return
  336. ------
  337. bp : float
  338. Absolute or relative band power.
  339. """
  340. from scipy.signal import welch
  341. from scipy.integrate import simps
  342. from mne.time_frequency import psd_array_multitaper
  343. band = np.asarray(band)
  344. low, high = band
  345. # Compute the modified periodogram (Welch)
  346. if method == 'welch':
  347. if window_sec is not None:
  348. nperseg = window_sec * sf
  349. else:
  350. nperseg = (2 / low) * sf
  351. freqs, psd = welch(data, sf, nperseg=nperseg)
  352. elif method == 'multitaper':
  353. psd, freqs = psd_array_multitaper(data, sf, adaptive=False,
  354. normalization='full', verbose=0, n_jobs=-1)
  355. # Frequency resolution
  356. freq_res = freqs[1] - freqs[0]
  357. # Find index of band in frequency vector
  358. idx_band = np.logical_and(freqs >= low, freqs <= high)
  359. # Integral approximation of the spectrum using parabola (Simpson's rule)
  360. bp = simps(psd[idx_band], dx=freq_res)
  361. if relative:
  362. bp /= simps(psd, dx=freq_res)
  363. return bp
  364. # %% [markdown]
  365. # # Layer 2/3
  366. # %%
  367. from scipy import signal
  368. from scipy.signal import butter, lfilter
  369. sf = 20000 # sampling frequency
  370. target_sf = 1000 # target frequency for downsampling
  371. f0 = 60.0 # Frequency to be removed from signal (Hz)
  372. w0 = f0/(target_sf/2) # Normalized Frequency
  373. Q = 30 # Quality factor
  374. # Design notch filter
  375. b, a = signal.iirnotch(w0, Q)
  376. start = 0
  377. end = int(len(ChannelData_vehicle[0])/sf)
  378. ################################################### baseline
  379. clean_data_vehicle = []
  380. for chanlNo in (L_2_3):
  381. data = ChannelData_vehicle[chanlNo].ravel()
  382. spike_data = filter_data(data[start*sf:end*sf], low=1, high= 500, sf=sf) # Band pass filter the data for LFP analysis
  383. # Now we use the above function to downsample the signal.
  384. lfp_data, sf_lfp = downsample_data(spike_data, sf=sf, target_sf=target_sf)
  385. clean_data_vehicle.append(signal.lfilter(b, a, lfp_data))
  386. ################################################### kainate
  387. end = int(len(ChannelData_GFC[0])/sf)
  388. clean_data_GFC = []
  389. for chanlNo in (L_2_3):
  390. data = ChannelData_GFC[chanlNo].ravel()
  391. spike_data = filter_data(data[start*sf:end*sf], low=1, high= 500, sf=sf) # Band pass filter the data for LFP analysis
  392. # Now we use the above function to downsample the signal.
  393. lfp_data, sf_lfp = downsample_data(spike_data, sf=sf, target_sf=target_sf)
  394. clean_data_GFC.append(signal.lfilter(b, a, lfp_data))
  395. # ################################################### kainate + bicuculine
  396. end = int(len(ChannelData_GFC_DOB[0])/sf)
  397. clean_data_GFC_DOB = []
  398. for chanlNo in (L_2_3):
  399. data = ChannelData_GFC_DOB[chanlNo].ravel()
  400. spike_data = filter_data(data[start*sf:end*sf], low=1, high= 500, sf=sf) # Band pass filter the data for LFP analysis
  401. # Now we use the above function to downsample the signal.
  402. lfp_data, sf_lfp = downsample_data(spike_data, sf=sf, target_sf=target_sf)
  403. clean_data_GFC_DOB.append(signal.lfilter(b, a, lfp_data))
  404. # %% [markdown]
  405. # # stop ! Change the NAME of the drug
  406. # %%
  407. import warnings
  408. warnings.filterwarnings('ignore')
  409. import numpy as np
  410. import pandas as pd
  411. import os
  412. layer = 'L_2_3'
  413. # Frequency bands and data groupings
  414. freq_bands = {
  415. 'delta': [1, 4],
  416. 'theta': [4, 10],
  417. 'beta': [10, 25],
  418. 'lowgamma': [30, 60],
  419. 'highgamma': [60, 100],
  420. 'gamma': [30, 100],
  421. 'ripple': [100, 200]
  422. }
  423. data_groups = {
  424. 'vehicle': clean_data_vehicle,
  425. 'NE ': clean_data_GFC,
  426. 'NE_YHM': clean_data_GFC_DOB,
  427. }
  428. # Time segments
  429. time_segments = {
  430. '_immediate': (38, 43),
  431. '_delayed': (38, 98),
  432. }
  433. # Function to compute band power for each time segment
  434. def compute_band_power(data, fs, freq_band, start, end):
  435. return bandpower(data[start * fs:end * fs], fs, freq_band, 'welch')
  436. # Initialize results dictionary for band power per time segment
  437. band_results = {band: {group: {segment: [] for segment in time_segments} for group in data_groups} for band in freq_bands}
  438. # Main loop for bandpower calculation with time segments
  439. for i in range(len(L_2_3)):
  440. for band_name, band_range in freq_bands.items():
  441. for group_name, group_data in data_groups.items():
  442. for segment_name, (start, end) in time_segments.items():
  443. # Compute band power for the specific time segment
  444. bp = compute_band_power(group_data[i], target_sf, band_range, start, end)
  445. band_results[band_name][group_name][segment_name].append(bp)
  446. # Prepare data for export, now considering time segments
  447. export_data = {}
  448. for band in freq_bands:
  449. concatenated_data = []
  450. for group in data_groups:
  451. for segment in time_segments:
  452. segment_data = band_results[band][group][segment]
  453. # Ensure the segment data is not empty and contains valid arrays
  454. if len(segment_data) > 0:
  455. # Convert any zero-dimensional arrays into one-dimensional ones
  456. segment_data = [np.atleast_1d(data) for data in segment_data]
  457. concatenated_data.append(np.concatenate(segment_data))
  458. # Only concatenate if we have non-empty arrays
  459. if len(concatenated_data) > 0:
  460. export_data[band] = np.concatenate(concatenated_data)
  461. else:
  462. export_data[band] = np.array([]) # Handle case where there's no data
  463. # Create conditions list, including the data groups and time segments
  464. conditions = np.concatenate([
  465. len(band_results['delta'][group][segment]) * [f"{group}_{segment}"]
  466. for group in data_groups for segment in time_segments if len(band_results['delta'][group][segment]) > 0
  467. ])
  468. # Calculate average band power for each condition
  469. average_cols = {}
  470. for band in freq_bands:
  471. if len(export_data[band]) > 0:
  472. # Split data for the band into separate arrays by condition (group + segment)
  473. data_per_condition = np.array_split(export_data[band], len(data_groups) * len(time_segments))
  474. avg_per_condition = [np.mean(segment) for segment in data_per_condition]
  475. # Create a column repeating the average value for alignment with the DataFrame length
  476. average_col = np.concatenate([[avg] * len(segment) for avg, segment in zip(avg_per_condition, data_per_condition)])
  477. average_cols[f'Average {band}'] = average_col
  478. else:
  479. average_cols[f'Average {band}'] = np.array([]) # Handle empty case
  480. # Metadata (modify or add actual values as needed)
  481. layer_col = [layer] * len(conditions) # Replace with actual data if available
  482. id_col = [name] * len(conditions) # Replace with actual data if available
  483. strain_col = [strain] * len(conditions) # Replace with actual data if available
  484. # Create DataFrame for export, now including time segments
  485. df_export = pd.DataFrame({
  486. **export_data,
  487. 'condition': conditions,
  488. **average_cols,
  489. 'Layer': layer_col,
  490. 'ID': id_col,
  491. 'strain': strain_col
  492. })
  493. # Export to Excel
  494. file_name = f"{name}_BandPower_{layer}.xlsx"
  495. save_path_full = os.path.join(save_path, file_name)
  496. with pd.ExcelWriter(save_path_full, engine='xlsxwriter') as writer:
  497. df_export.to_excel(writer, sheet_name='BandPower', index=False)
  498. print("Data exported successfully.")
  499. # %%
  500. import seaborn as sns
  501. import matplotlib.pyplot as plt
  502. # Load the exported data from the previous code
  503. df = pd.read_excel(os.path.join(save_path, file_name))
  504. # Frequency bands and titles
  505. bands = ['delta', 'theta', 'beta', 'lowgamma', 'highgamma', 'ripple']
  506. titles = [
  507. r'[$\delta$] Power', r'[$\theta$] Power', r'[$\beta$] Power',
  508. r'low [$\gamma$] Power', r'high [$\gamma$] Power', r'Cortical Ripple Power'
  509. ]
  510. # Updated condition labels based on time segments and groups
  511. xticklabels = [
  512. 'Vehicle_immediate', 'Vehicle_delayed',
  513. 'NE_immediate', 'NE_delayed',
  514. 'NE_YHM_immediate', 'NE_YHM_delayed'
  515. ]
  516. # Create subplots for each frequency band
  517. fig, ax = plt.subplots(nrows=2, ncols=3, figsize=(15, 5))
  518. # Loop over each frequency band and create a plot
  519. for i, band in enumerate(bands):
  520. row, col = divmod(i, 3) # Determine row and column for each subplot
  521. # Create boxplot for each condition and frequency band
  522. sns.boxplot(data=df, x='condition', y=band, ax=ax[row, col], showfliers=False)
  523. # Create swarmplot (or stripplot) to show individual points on top of the boxplot
  524. sns.stripplot(data=df, x='condition', y=band, color='k', ax=ax[row, col], dodge=True)
  525. # Set the x-axis labels to the updated condition names
  526. ax[row, col].set_xticklabels(xticklabels)
  527. ax[row, col].tick_params(axis='x', rotation=90, size= 2) # Rotate x-axis labels for readability
  528. # Set title for each subplot based on the band
  529. ax[row, col].set_title(titles[i])
  530. # Set y-axis to log scale for better visualization of power values
  531. ax[row, col].set_yscale("log")
  532. # Remove top and right spines to make the plot look cleaner
  533. ax[row, col].spines['top'].set_visible(False)
  534. ax[row, col].spines['right'].set_visible(False)
  535. # Set y-axis label for the first subplot
  536. if band == 'delta':
  537. ax[row, col].set_ylabel('Power [\u03bc$V^2$]')
  538. # Adjust the layout for better spacing
  539. plt.tight_layout()
  540. # Save the figure as a PDF
  541. plt.savefig(os.path.join(save_path, name + 'BandPower_L2_3.pdf'), bbox_inches='tight')
  542. # Show the plot
  543. plt.show()
  544. # %%
  545. import numpy as np
  546. from scipy import signal
  547. import matplotlib.pyplot as plt
  548. import os
  549. import seaborn as sns
  550. # Helper function to append suffix to filenames
  551. def save_figure_with_suffix(filename_base, suffix='_L2_3', save_path='.', ext='svg'):
  552. filename = f"{filename_base}{suffix}.{ext}"
  553. plt.savefig(os.path.join(save_path, filename), bbox_inches='tight')
  554. # Coherence calculation for data within layer 2/3
  555. def calculate_coherence(data_list, sf, freq_range=None):
  556. num_channels = len(data_list)
  557. coherence_matrix = np.zeros((num_channels, num_channels))
  558. # Loop through all pairs of channels
  559. for i in range(num_channels):
  560. for j in range(i + 1, num_channels):
  561. f, Cxy = signal.coherence(data_list[i], data_list[j], fs=sf, nperseg=1024)
  562. if freq_range:
  563. # Find indices within the desired frequency range
  564. freq_indices = np.where((f >= freq_range[0]) & (f <= freq_range[1]))
  565. coherence_matrix[i, j] = np.mean(Cxy[freq_indices])
  566. else:
  567. coherence_matrix[i, j] = np.mean(Cxy)
  568. return coherence_matrix
  569. def plot_coherence_matrix(matrix, title='', vmin=None, vmax=None):
  570. plt.figure(figsize=(7, 5))
  571. ax = sns.heatmap(matrix, annot=False, cmap='hot', vmin=vmin, vmax=vmax, cbar_kws={'label': 'Coherence'})
  572. ax.set_title(title)
  573. ax.set_xlabel('Channel')
  574. ax.set_ylabel('Channel')
  575. # Define start and end times for base and stim32 separately
  576. intervals = [(38, 98)]
  577. fs = target_sf
  578. # Extract data segments for baseline and stimulation
  579. clean_data_Vehicle = [data[start * fs:end * fs] for data in clean_data_vehicle for start, end in intervals]
  580. clean_data_NE = [data[start * fs:end * fs] for data in clean_data_GFC for start, end in intervals]
  581. clean_data_NE_YHM = [data[start * fs:end * fs] for data in clean_data_GFC_DOB for start, end in intervals]
  582. # Calculate coherence matrices
  583. coherence_matrix_Vehicle = calculate_coherence(clean_data_Vehicle, sf=target_sf, freq_range=(30, 80))
  584. coherence_matrix_NE = calculate_coherence(clean_data_NE, sf=target_sf, freq_range=(30, 80))
  585. coherence_matrix_NE_YHM = calculate_coherence(clean_data_NE_YHM, sf=target_sf, freq_range=(30, 80))
  586. # Find global min and max values for the color scale
  587. min_coherence = min(coherence_matrix_Vehicle.min(), coherence_matrix_NE.min(),
  588. coherence_matrix_NE_YHM.min(), )
  589. max_coherence = max(coherence_matrix_Vehicle.max(), coherence_matrix_NE.max(),
  590. coherence_matrix_NE_YHM.max(), )
  591. # Plot and save coherence matrices
  592. for matrix, label in zip([coherence_matrix_Vehicle, coherence_matrix_NE, coherence_matrix_NE_YHM],
  593. ["Vehicle", "NE", "NE_YHM"]):
  594. plot_coherence_matrix(matrix, title=f"Coherence Matrix - {label}", vmin=min_coherence, vmax=max_coherence)
  595. save_figure_with_suffix(f'coherence_matrix_{label}', save_path=save_path_coherence)
  596. plt.show()
  597. # Calculate and print average coherence for quantification
  598. coherence_avgs = {
  599. "Vehicle": np.mean(coherence_matrix_Vehicle[coherence_matrix_Vehicle > 0]),
  600. "NE": np.mean(coherence_matrix_NE[coherence_matrix_NE > 0]),
  601. "NE_YHM": np.mean(coherence_matrix_NE_YHM[coherence_matrix_NE_YHM > 0]),
  602. }
  603. for label, avg in coherence_avgs.items():
  604. print(f"Average Coherence - {label}:", avg)
  605. # %%
  606. data_vehicle= []
  607. data_GFC= []
  608. data_GFC_DOB= []
  609. start = 38
  610. end = 98
  611. fs = target_sf
  612. for i in range(len(clean_data_vehicle)):
  613. data_vehicle.append(downsample_data(clean_data_vehicle[i][start * fs:end * fs], sf, target_sf)[0]) # [::10] is used for downsampling and faster process
  614. data_GFC.append(downsample_data(clean_data_GFC[i][start * fs:end * fs], sf, target_sf)[0])
  615. data_GFC_DOB.append(downsample_data(clean_data_GFC_DOB[i][start * fs:end * fs], sf, target_sf)[0])
  616. # %%
  617. sf = target_sf
  618. p_obj = Pac(idpac=(4, 0, 0), f_pha=(1, 30, 1, .2), f_amp=(1, 200, 1, 2)) # 1-30 DTAB
  619. pha = p_obj.filter(sf, np.array(data_vehicle), ftype='phase', n_jobs=-1)
  620. amp = p_obj.filter(sf, np.array(data_vehicle), ftype='amplitude', n_jobs=-1)
  621. pac_vehicle = p_obj.fit(pha, amp)
  622. pha = p_obj.filter(sf, np.array(data_GFC), ftype='phase', n_jobs=-1)
  623. amp = p_obj.filter(sf, np.array(data_GFC), ftype='amplitude', n_jobs=-1)
  624. pac_GFC = p_obj.fit(pha, amp)
  625. pha = p_obj.filter(sf, np.array(data_GFC_DOB), ftype='phase', n_jobs=-1)
  626. amp = p_obj.filter(sf, np.array(data_GFC_DOB), ftype='amplitude', n_jobs=-1)
  627. pac_GFC_DOB = p_obj.fit(pha, amp)
  628. # ################# saving ndarrays
  629. import numpy as np
  630. from pathlib import Path
  631. path = Path(save_path).expanduser()
  632. path.mkdir(parents=True, exist_ok=True)
  633. np.save(path/(name + layer + '_pac_vehicle'), pac_vehicle)
  634. np.save(path/(name + layer + '_pac_NE'), pac_GFC)
  635. np.save(path/(name + layer + '_pac_NE_YHM'), pac_GFC_DOB)
  636. # %%
  637. import matplotlib.pyplot as plt
  638. import numpy as np
  639. condition= ['Vehicle', 'NE',
  640. 'NE_YHM',
  641. ]
  642. pac = [pac_vehicle, pac_GFC,
  643. pac_GFC_DOB,
  644. ]
  645. # Set up the figure size for the plot
  646. plt.figure(figsize=(12, 9))
  647. # Initialize variable to store the global vmax
  648. global_vmax = -np.inf # Start with a very low value
  649. # First loop to determine global vmax
  650. for k in pac:
  651. pacc = k.mean(axis=1) # Compute the mean across time points/trials
  652. global_vmax = max(global_vmax, pacc.max()) # Update global vmax with the max value across all conditions
  653. # Loop through each condition and its corresponding PAC
  654. for i, (k, cond) in enumerate(zip(pac, condition)):
  655. pacc = k.mean(axis=-1)
  656. # Check the shape of pacc after mean calculation
  657. print(f"Shape of pacc for {cond}: {pacc.shape}")
  658. # Set up the color scaling using the global vmax
  659. kw = dict(vmax=global_vmax, vmin=0, cmap='jet')
  660. # Plotting the comodulogram
  661. plt.subplot(2, 2, i + 1)
  662. p_obj.comodulogram(pacc, title=f"PAC {cond}", **kw)
  663. # Adding vertical lines at specific points for reference
  664. plt.axvline(x=4, color='k', linestyle='--', linewidth=2.0)
  665. plt.axvline(x=10, color='k', linestyle='--', linewidth=2.0)
  666. # Adjust layout for better spacing
  667. plt.tight_layout()
  668. ####### Saving
  669. import os.path
  670. fileName = name2
  671. plt.savefig(os.path.join(save_path , fileName + layer + '_PAC.svg'),bbox_inches = 'tight')
  672. plt.savefig(os.path.join(save_path , fileName + layer + '_PAC.pdf'), bbox_inches = 'tight')
  673. # %%
  674. # define the preferred phase object
  675. sf = target_sf
  676. # pp_obj = PreferredPhase(f_pha=[1, 4], f_amp=(30, 100, 2, 2)) # alpha (1-4 Hz)
  677. pp_obj = PreferredPhase(f_pha=[4,8], f_amp=(30, 200, 2, 2)) # THETA (4-8 Hz)
  678. n_jobs = -1
  679. # compute the preferred phase (reuse the amplitude computed above)
  680. pha_vehicle = pp_obj.filter(sf, np.array(data_vehicle), ftype='phase', n_jobs=n_jobs)
  681. amp_vehicle = pp_obj.filter(sf, np.array(data_vehicle), ftype='amplitude', n_jobs=n_jobs)
  682. pha_GFC = pp_obj.filter(sf, np.array(data_GFC), ftype='phase', n_jobs=n_jobs)
  683. amp_GFC = pp_obj.filter(sf, np.array(data_GFC), ftype='amplitude', n_jobs=n_jobs)
  684. pha_GFC_DOB = pp_obj.filter(sf, np.array(data_GFC_DOB), ftype='phase', n_jobs=n_jobs)
  685. amp_GFC_DOB = pp_obj.filter(sf, np.array(data_GFC_DOB), ftype='amplitude', n_jobs=n_jobs)
  686. ampbin_vehicle, _, vecbin = pp_obj.fit(pha_vehicle, amp_vehicle, n_bins=72)
  687. ampbin_GFC, _, vecbin = pp_obj.fit(pha_GFC, amp_GFC, n_bins=72)
  688. ampbin_GFC_DOB, _, vecbin = pp_obj.fit(pha_GFC_DOB, amp_GFC_DOB, n_bins=72)
  689. # mean binned amplitude across trials
  690. ampbin_vehicle = np.squeeze(ampbin_vehicle).mean(-1).T
  691. ampbin_GFC = np.squeeze(ampbin_GFC).mean(-1).T
  692. ampbin_GFC_DOB = np.squeeze(ampbin_GFC_DOB).mean(-1).T
  693. ################# saving ndarrays theta
  694. import numpy as np
  695. from pathlib import Path
  696. path.mkdir(parents=True, exist_ok=True)
  697. np.save(path/(name2 + layer + '_ampbin_vehicle'), ampbin_vehicle)
  698. np.save(path/(name2 + layer + '_ampbin_NE'), ampbin_GFC)
  699. np.save(path/(name2 + layer + '_ampbin_NE_YHM'), ampbin_GFC_DOB)
  700. # %%
  701. import numpy as np
  702. import matplotlib.pyplot as plt
  703. # List of all the amplitude bins (all frequency conditions)
  704. ampbins = [ampbin_vehicle, ampbin_GFC, ampbin_GFC_DOB, ]
  705. # Find the global minimum and maximum values across all amplitude bins
  706. global_min = np.min([np.min(amp) for amp in ampbins])
  707. global_max = np.max([np.max(amp) for amp in ampbins])
  708. # Now set vmin and vmax to these global values
  709. vmin_value = global_min
  710. vmax_value = global_max
  711. # Define the plotting dictionary with consistent color range
  712. kw_plt = dict(cmap='RdBu_r', interp=0.1, cblabel='Bins Amplitude',
  713. colorbar=True, y=1.3, fz_title=20, plotas='contour',
  714. vmin=vmin_value, vmax=vmax_value)
  715. # Create the figure
  716. plt.figure(figsize=(27, 23))
  717. # Plot the data for each frequency condition using the updated kw_plt dictionary
  718. pp_obj.polar(ampbin_vehicle, vecbin, pp_obj.yvec, subplot=331, title='vehicle', **kw_plt)
  719. pp_obj.polar(ampbin_GFC, vecbin, pp_obj.yvec, subplot=332, title='NE', **kw_plt)
  720. pp_obj.polar(ampbin_GFC_DOB, vecbin, pp_obj.yvec, subplot=333, title='NE_YHM', **kw_plt)
  721. # Adjust layout to prevent overlapping elements
  722. plt.tight_layout()
  723. ########### Saving
  724. import os.path
  725. fileName = name2
  726. plt.savefig(os.path.join(save_path , fileName + layer + '_PreferredPhase_ThetaGamma.pdf'), bbox_inches = 'tight')
  727. # %%
  728. # %% [markdown]
  729. # # Layer 5
  730. # %%
  731. from scipy import signal
  732. from scipy.signal import butter, lfilter
  733. sf = 20000 # sampling frequency
  734. target_sf = 1000 # target frequency for downsampling
  735. f0 = 60.0 # Frequency to be removed from signal (Hz)
  736. w0 = f0/(target_sf/2) # Normalized Frequency
  737. Q = 30 # Quality factor
  738. # Design notch filter
  739. b, a = signal.iirnotch(w0, Q)
  740. start = 0
  741. end = int(len(ChannelData_vehicle[0])/sf)
  742. ################################################### baseline
  743. clean_data_vehicle = []
  744. for chanlNo in (L_5):
  745. data = ChannelData_vehicle[chanlNo].ravel()
  746. spike_data = filter_data(data[start*sf:end*sf], low=1, high= 500, sf=sf) # Band pass filter the data for LFP analysis
  747. # Now we use the above function to downsample the signal.
  748. lfp_data, sf_lfp = downsample_data(spike_data, sf=sf, target_sf=target_sf)
  749. clean_data_vehicle.append(signal.lfilter(b, a, lfp_data))
  750. ################################################### kainate
  751. end = int(len(ChannelData_GFC[0])/sf)
  752. clean_data_GFC = []
  753. for chanlNo in (L_5):
  754. data = ChannelData_GFC[chanlNo].ravel()
  755. spike_data = filter_data(data[start*sf:end*sf], low=1, high= 500, sf=sf) # Band pass filter the data for LFP analysis
  756. # Now we use the above function to downsample the signal.
  757. lfp_data, sf_lfp = downsample_data(spike_data, sf=sf, target_sf=target_sf)
  758. clean_data_GFC.append(signal.lfilter(b, a, lfp_data))
  759. # ################################################### kainate + bicuculine
  760. end = int(len(ChannelData_GFC_DOB[0])/sf)
  761. clean_data_GFC_DOB = []
  762. for chanlNo in (L_5):
  763. data = ChannelData_GFC_DOB[chanlNo].ravel()
  764. spike_data = filter_data(data[start*sf:end*sf], low=1, high= 500, sf=sf) # Band pass filter the data for LFP analysis
  765. # Now we use the above function to downsample the signal.
  766. lfp_data, sf_lfp = downsample_data(spike_data, sf=sf, target_sf=target_sf)
  767. clean_data_GFC_DOB.append(signal.lfilter(b, a, lfp_data))
  768. # %%
  769. import warnings
  770. warnings.filterwarnings('ignore')
  771. import numpy as np
  772. import pandas as pd
  773. import os
  774. layer = 'L_5'
  775. # Frequency bands and data groupings
  776. freq_bands = {
  777. 'delta': [1, 4],
  778. 'theta': [4, 10],
  779. 'beta': [10, 25],
  780. 'lowgamma': [30, 60],
  781. 'highgamma': [60, 100],
  782. 'gamma': [30, 100],
  783. 'ripple': [100, 200]
  784. }
  785. data_groups = {
  786. 'vehicle': clean_data_vehicle,
  787. 'NE ': clean_data_GFC,
  788. 'NE_YHM': clean_data_GFC_DOB,
  789. }
  790. # Time segments
  791. time_segments = {
  792. '_immediate': (38, 43),
  793. '_delayed': (38, 98),
  794. }
  795. # Function to compute band power for each time segment
  796. def compute_band_power(data, fs, freq_band, start, end):
  797. return bandpower(data[start * fs:end * fs], fs, freq_band, 'welch')
  798. # Initialize results dictionary for band power per time segment
  799. band_results = {band: {group: {segment: [] for segment in time_segments} for group in data_groups} for band in freq_bands}
  800. # Main loop for bandpower calculation with time segments
  801. for i in range(len(L_5)):
  802. for band_name, band_range in freq_bands.items():
  803. for group_name, group_data in data_groups.items():
  804. for segment_name, (start, end) in time_segments.items():
  805. # Compute band power for the specific time segment
  806. bp = compute_band_power(group_data[i], target_sf, band_range, start, end)
  807. band_results[band_name][group_name][segment_name].append(bp)
  808. # Prepare data for export, now considering time segments
  809. export_data = {}
  810. for band in freq_bands:
  811. concatenated_data = []
  812. for group in data_groups:
  813. for segment in time_segments:
  814. segment_data = band_results[band][group][segment]
  815. # Ensure the segment data is not empty and contains valid arrays
  816. if len(segment_data) > 0:
  817. # Convert any zero-dimensional arrays into one-dimensional ones
  818. segment_data = [np.atleast_1d(data) for data in segment_data]
  819. concatenated_data.append(np.concatenate(segment_data))
  820. # Only concatenate if we have non-empty arrays
  821. if len(concatenated_data) > 0:
  822. export_data[band] = np.concatenate(concatenated_data)
  823. else:
  824. export_data[band] = np.array([]) # Handle case where there's no data
  825. # Create conditions list, including the data groups and time segments
  826. conditions = np.concatenate([
  827. len(band_results['delta'][group][segment]) * [f"{group}_{segment}"]
  828. for group in data_groups for segment in time_segments if len(band_results['delta'][group][segment]) > 0
  829. ])
  830. # Calculate average band power for each condition
  831. average_cols = {}
  832. for band in freq_bands:
  833. if len(export_data[band]) > 0:
  834. # Split data for the band into separate arrays by condition (group + segment)
  835. data_per_condition = np.array_split(export_data[band], len(data_groups) * len(time_segments))
  836. avg_per_condition = [np.mean(segment) for segment in data_per_condition]
  837. # Create a column repeating the average value for alignment with the DataFrame length
  838. average_col = np.concatenate([[avg] * len(segment) for avg, segment in zip(avg_per_condition, data_per_condition)])
  839. average_cols[f'Average {band}'] = average_col
  840. else:
  841. average_cols[f'Average {band}'] = np.array([]) # Handle empty case
  842. # Metadata (modify or add actual values as needed)
  843. layer_col = [layer] * len(conditions) # Replace with actual data if available
  844. id_col = [name] * len(conditions) # Replace with actual data if available
  845. strain_col = [strain] * len(conditions) # Replace with actual data if available
  846. # Create DataFrame for export, now including time segments
  847. df_export = pd.DataFrame({
  848. **export_data,
  849. 'condition': conditions,
  850. **average_cols,
  851. 'Layer': layer_col,
  852. 'ID': id_col,
  853. 'strain': strain_col
  854. })
  855. # Export to Excel
  856. file_name = f"{name}_BandPower_{layer}.xlsx"
  857. save_path_full = os.path.join(save_path, file_name)
  858. with pd.ExcelWriter(save_path_full, engine='xlsxwriter') as writer:
  859. df_export.to_excel(writer, sheet_name='BandPower', index=False)
  860. print("Data exported successfully.")
  861. # %%
  862. import seaborn as sns
  863. import matplotlib.pyplot as plt
  864. # Load the exported data from the previous code
  865. df = pd.read_excel(os.path.join(save_path, file_name))
  866. # Frequency bands and titles
  867. bands = ['delta', 'theta', 'beta', 'lowgamma', 'highgamma', 'ripple']
  868. titles = [
  869. r'[$\delta$] Power', r'[$\theta$] Power', r'[$\beta$] Power',
  870. r'low [$\gamma$] Power', r'high [$\gamma$] Power', r'Cortical Ripple Power'
  871. ]
  872. # Updated condition labels based on time segments and groups
  873. xticklabels = [
  874. 'Vehicle_immediate', 'Vehicle_delayed',
  875. 'NE_immediate', 'NE_delayed',
  876. 'NE_YHM_immediate', 'NE_YHM_delayed'
  877. ]
  878. # Create subplots for each frequency band
  879. fig, ax = plt.subplots(nrows=2, ncols=3, figsize=(15, 5))
  880. # Loop over each frequency band and create a plot
  881. for i, band in enumerate(bands):
  882. row, col = divmod(i, 3) # Determine row and column for each subplot
  883. # Create boxplot for each condition and frequency band
  884. sns.boxplot(data=df, x='condition', y=band, ax=ax[row, col], showfliers=False)
  885. # Create swarmplot (or stripplot) to show individual points on top of the boxplot
  886. sns.stripplot(data=df, x='condition', y=band, color='k', ax=ax[row, col], dodge=True)
  887. # Set the x-axis labels to the updated condition names
  888. ax[row, col].set_xticklabels(xticklabels)
  889. ax[row, col].tick_params(axis='x', rotation=90, size= 2) # Rotate x-axis labels for readability
  890. # Set title for each subplot based on the band
  891. ax[row, col].set_title(titles[i])
  892. # Set y-axis to log scale for better visualization of power values
  893. ax[row, col].set_yscale("log")
  894. # Remove top and right spines to make the plot look cleaner
  895. ax[row, col].spines['top'].set_visible(False)
  896. ax[row, col].spines['right'].set_visible(False)
  897. # Set y-axis label for the first subplot
  898. if band == 'delta':
  899. ax[row, col].set_ylabel('Power [\u03bc$V^2$]')
  900. # Adjust the layout for better spacing
  901. plt.tight_layout()
  902. # Save the figure as a PDF
  903. plt.savefig(os.path.join(save_path, name + 'BandPower_L5.pdf'), bbox_inches='tight')
  904. # Show the plot
  905. plt.show()
  906. # %%
  907. import numpy as np
  908. from scipy import signal
  909. import matplotlib.pyplot as plt
  910. import os
  911. import seaborn as sns
  912. # Helper function to append suffix to filenames
  913. def save_figure_with_suffix(filename_base, suffix='_L5', save_path='.', ext='svg'):
  914. filename = f"{filename_base}{suffix}.{ext}"
  915. plt.savefig(os.path.join(save_path, filename), bbox_inches='tight')
  916. # Coherence calculation for data within layer 2/3
  917. def calculate_coherence(data_list, sf, freq_range=None):
  918. num_channels = len(data_list)
  919. coherence_matrix = np.zeros((num_channels, num_channels))
  920. # Loop through all pairs of channels
  921. for i in range(num_channels):
  922. for j in range(i + 1, num_channels):
  923. f, Cxy = signal.coherence(data_list[i], data_list[j], fs=sf, nperseg=1024)
  924. if freq_range:
  925. # Find indices within the desired frequency range
  926. freq_indices = np.where((f >= freq_range[0]) & (f <= freq_range[1]))
  927. coherence_matrix[i, j] = np.mean(Cxy[freq_indices])
  928. else:
  929. coherence_matrix[i, j] = np.mean(Cxy)
  930. return coherence_matrix
  931. def plot_coherence_matrix(matrix, title='', vmin=None, vmax=None):
  932. plt.figure(figsize=(7, 5))
  933. ax = sns.heatmap(matrix, annot=False, cmap='hot', vmin=vmin, vmax=vmax, cbar_kws={'label': 'Coherence'})
  934. ax.set_title(title)
  935. ax.set_xlabel('Channel')
  936. ax.set_ylabel('Channel')
  937. # Define start and end times for base and stim32 separately
  938. intervals = [(0, -1)]
  939. fs = target_sf
  940. # Extract data segments for baseline and stimulation
  941. clean_data_Vehicle = [data[start * fs:end * fs] for data in clean_data_vehicle for start, end in intervals]
  942. clean_data_NE = [data[start * fs:end * fs] for data in clean_data_GFC for start, end in intervals]
  943. clean_data_NE_YHM = [data[start * fs:end * fs] for data in clean_data_GFC_DOB for start, end in intervals]
  944. # Calculate coherence matrices
  945. coherence_matrix_Vehicle = calculate_coherence(clean_data_Vehicle, sf=target_sf, freq_range=(30, 80))
  946. coherence_matrix_NE = calculate_coherence(clean_data_NE, sf=target_sf, freq_range=(30, 80))
  947. coherence_matrix_NE_YHM = calculate_coherence(clean_data_NE_YHM, sf=target_sf, freq_range=(30, 80))
  948. # Find global min and max values for the color scale
  949. min_coherence = min(coherence_matrix_Vehicle.min(), coherence_matrix_NE.min(),
  950. coherence_matrix_NE_YHM.min(), )
  951. max_coherence = max(coherence_matrix_Vehicle.max(), coherence_matrix_NE.max(),
  952. coherence_matrix_NE_YHM.max())
  953. # Plot and save coherence matrices
  954. for matrix, label in zip([coherence_matrix_Vehicle, coherence_matrix_NE, coherence_matrix_NE_YHM, ],
  955. ["Vehicle", "NE", "NE_YHM", ]):
  956. plot_coherence_matrix(matrix, title=f"Coherence Matrix - {label}", vmin=min_coherence, vmax=max_coherence)
  957. save_figure_with_suffix(f'coherence_matrix_{label}', save_path=save_path_coherence)
  958. plt.show()
  959. # Calculate and print average coherence for quantification
  960. coherence_avgs = {
  961. "Vehicle": np.mean(coherence_matrix_Vehicle[coherence_matrix_Vehicle > 0]),
  962. "NE": np.mean(coherence_matrix_NE[coherence_matrix_NE > 0]),
  963. "NE_YHM": np.mean(coherence_matrix_NE_YHM[coherence_matrix_NE_YHM > 0]),
  964. }
  965. for label, avg in coherence_avgs.items():
  966. print(f"Average Coherence - {label}:", avg)
  967. # %% [markdown]
  968. # # Preferred Phase
  969. # %%
  970. # The preferred phase is given by the phase bin at which the amplitude is maximum. Said differently,
  971. # the gamma amplitude is binned according to the alpha phase. The preferred-phase is computed as the
  972. # maximum of this histogram. To identify this preferred-phase, we propose here a polar plotting method to
  973. # visualize retrieve the previous result using the histogram method
  974. # %%
  975. data_vehicle= []
  976. data_GFC= []
  977. data_GFC_DOB= []
  978. start = 38
  979. end = 98
  980. fs = target_sf
  981. for i in range(len(clean_data_vehicle)):
  982. data_vehicle.append(downsample_data(clean_data_vehicle[i][start * fs:end * fs], sf, target_sf)[0]) # [::10] is used for downsampling and faster process
  983. data_GFC.append(downsample_data(clean_data_GFC[i][start * fs:end * fs], sf, target_sf)[0])
  984. data_GFC_DOB.append(downsample_data(clean_data_GFC_DOB[i][start * fs:end * fs], sf, target_sf)[0])
  985. # %% [markdown]
  986. # # PAC (Phase-amplitude coupling) Theta-Gamma
  987. # %% [markdown]
  988. # # stop ! Change the NAME of the drug
  989. # %%
  990. sf = target_sf
  991. p_obj = Pac(idpac=(4, 0, 0), f_pha=(1, 30, 1, .2), f_amp=(1, 200, 1, 2)) # 1-30 DTAB
  992. pha = p_obj.filter(sf, np.array(data_vehicle), ftype='phase', n_jobs=-1)
  993. amp = p_obj.filter(sf, np.array(data_vehicle), ftype='amplitude', n_jobs=-1)
  994. pac_vehicle = p_obj.fit(pha, amp)
  995. pha = p_obj.filter(sf, np.array(data_GFC), ftype='phase', n_jobs=-1)
  996. amp = p_obj.filter(sf, np.array(data_GFC), ftype='amplitude', n_jobs=-1)
  997. pac_GFC = p_obj.fit(pha, amp)
  998. pha = p_obj.filter(sf, np.array(data_GFC_DOB), ftype='phase', n_jobs=-1)
  999. amp = p_obj.filter(sf, np.array(data_GFC_DOB), ftype='amplitude', n_jobs=-1)
  1000. pac_GFC_DOB = p_obj.fit(pha, amp)
  1001. # ################# saving ndarrays
  1002. import numpy as np
  1003. from pathlib import Path
  1004. path = Path(save_path).expanduser()
  1005. path.mkdir(parents=True, exist_ok=True)
  1006. np.save(path/(name + layer + '_pac_vehicle'), pac_vehicle)
  1007. np.save(path/(name + layer + '_pac_NE'), pac_GFC)
  1008. np.save(path/(name + layer + '_pac_NE_YHM'), pac_GFC_DOB)
  1009. # %%
  1010. import matplotlib.pyplot as plt
  1011. import numpy as np
  1012. condition= ['Vehicle', 'NE',
  1013. 'NE_YHM',
  1014. ]
  1015. pac = [pac_vehicle, pac_GFC,
  1016. pac_GFC_DOB,
  1017. ]
  1018. # Set up the figure size for the plot
  1019. plt.figure(figsize=(12, 9))
  1020. # Initialize variable to store the global vmax
  1021. global_vmax = -np.inf # Start with a very low value
  1022. # First loop to determine global vmax
  1023. for k in pac:
  1024. pacc = k.mean(axis=1) # Compute the mean across time points/trials
  1025. global_vmax = max(global_vmax, pacc.max()) # Update global vmax with the max value across all conditions
  1026. # Loop through each condition and its corresponding PAC
  1027. for i, (k, cond) in enumerate(zip(pac, condition)):
  1028. pacc = k.mean(axis=-1)
  1029. # Check the shape of pacc after mean calculation
  1030. print(f"Shape of pacc for {cond}: {pacc.shape}")
  1031. # Set up the color scaling using the global vmax
  1032. kw = dict(vmax=global_vmax, vmin=0, cmap='jet')
  1033. # Plotting the comodulogram
  1034. plt.subplot(2, 2, i + 1)
  1035. p_obj.comodulogram(pacc, title=f"PAC {cond}", **kw)
  1036. # Adding vertical lines at specific points for reference
  1037. plt.axvline(x=4, color='k', linestyle='--', linewidth=2.0)
  1038. plt.axvline(x=10, color='k', linestyle='--', linewidth=2.0)
  1039. # Adjust layout for better spacing
  1040. plt.tight_layout()
  1041. ####### Saving
  1042. import os.path
  1043. fileName = name2
  1044. plt.savefig(os.path.join(save_path , fileName + layer + '_PAC.svg'),bbox_inches = 'tight')
  1045. plt.savefig(os.path.join(save_path , fileName + layer + '_PAC.pdf'), bbox_inches = 'tight')
  1046. # %%
  1047. # %% [markdown]
  1048. # # Preferred Phase Theta-Gamma
  1049. # %% [markdown]
  1050. # # stop ! Change the NAME of the drug
  1051. # %%
  1052. # define the preferred phase object
  1053. sf = target_sf
  1054. # pp_obj = PreferredPhase(f_pha=[1, 4], f_amp=(30, 100, 2, 2)) # alpha (1-4 Hz)
  1055. pp_obj = PreferredPhase(f_pha=[4,8], f_amp=(30, 200, 2, 2)) # THETA (4-8 Hz)
  1056. n_jobs = -1
  1057. # compute the preferred phase (reuse the amplitude computed above)
  1058. pha_vehicle = pp_obj.filter(sf, np.array(data_vehicle), ftype='phase', n_jobs=n_jobs)
  1059. amp_vehicle = pp_obj.filter(sf, np.array(data_vehicle), ftype='amplitude', n_jobs=n_jobs)
  1060. pha_GFC = pp_obj.filter(sf, np.array(data_GFC), ftype='phase', n_jobs=n_jobs)
  1061. amp_GFC = pp_obj.filter(sf, np.array(data_GFC), ftype='amplitude', n_jobs=n_jobs)
  1062. pha_GFC_DOB = pp_obj.filter(sf, np.array(data_GFC_DOB), ftype='phase', n_jobs=n_jobs)
  1063. amp_GFC_DOB = pp_obj.filter(sf, np.array(data_GFC_DOB), ftype='amplitude', n_jobs=n_jobs)
  1064. ampbin_vehicle, _, vecbin = pp_obj.fit(pha_vehicle, amp_vehicle, n_bins=72)
  1065. ampbin_GFC, _, vecbin = pp_obj.fit(pha_GFC, amp_GFC, n_bins=72)
  1066. ampbin_GFC_DOB, _, vecbin = pp_obj.fit(pha_GFC_DOB, amp_GFC_DOB, n_bins=72)
  1067. # mean binned amplitude across trials
  1068. ampbin_vehicle = np.squeeze(ampbin_vehicle).mean(-1).T
  1069. ampbin_GFC = np.squeeze(ampbin_GFC).mean(-1).T
  1070. ampbin_GFC_DOB = np.squeeze(ampbin_GFC_DOB).mean(-1).T
  1071. ################# saving ndarrays theta
  1072. import numpy as np
  1073. from pathlib import Path
  1074. path.mkdir(parents=True, exist_ok=True)
  1075. np.save(path/(name2 + layer + '_ampbin_vehicle'), ampbin_vehicle)
  1076. np.save(path/(name2 + layer + '_ampbin_NE'), ampbin_GFC)
  1077. np.save(path/(name2 + layer + '_ampbin_NE_YHM'), ampbin_GFC_DOB)
  1078. # %%
  1079. import numpy as np
  1080. import matplotlib.pyplot as plt
  1081. # List of all the amplitude bins (all frequency conditions)
  1082. ampbins = [ampbin_vehicle, ampbin_GFC, ampbin_GFC_DOB, ]
  1083. # Find the global minimum and maximum values across all amplitude bins
  1084. global_min = np.min([np.min(amp) for amp in ampbins])
  1085. global_max = np.max([np.max(amp) for amp in ampbins])
  1086. # Now set vmin and vmax to these global values
  1087. vmin_value = global_min
  1088. vmax_value = global_max
  1089. # Define the plotting dictionary with consistent color range
  1090. kw_plt = dict(cmap='RdBu_r', interp=0.1, cblabel='Bins Amplitude',
  1091. colorbar=True, y=1.3, fz_title=20, plotas='contour',
  1092. vmin=vmin_value, vmax=vmax_value)
  1093. # Create the figure
  1094. plt.figure(figsize=(27, 23))
  1095. # Plot the data for each frequency condition using the updated kw_plt dictionary
  1096. pp_obj.polar(ampbin_vehicle, vecbin, pp_obj.yvec, subplot=331, title='vehicle', **kw_plt)
  1097. pp_obj.polar(ampbin_GFC, vecbin, pp_obj.yvec, subplot=332, title='NE', **kw_plt)
  1098. pp_obj.polar(ampbin_GFC_DOB, vecbin, pp_obj.yvec, subplot=333, title='NE_YHM', **kw_plt)
  1099. # Adjust layout to prevent overlapping elements
  1100. plt.tight_layout()
  1101. # ########### Saving
  1102. # import os.path
  1103. # fileName = name2
  1104. # plt.savefig(os.path.join(save_path , fileName + layer + '_PreferredPhase_ThetaGamma.pdf'), bbox_inches = 'tight')
  1105. # %%
  1106. # %%
  1107. # %%
  1108. # %%
  1109. # %%
  1110. # %%
  1111. # %%
  1112. # %%
  1113. # %%
  1114. # %%
  1115. # %%
  1116. # %%

MEA LFP _ Open OnDemand_LC_mPFC.ipynb at commit 29e3811, no license · at the source

Overview

Authors: Hassan Hosseini1, Sky Evans-Martin1, Emma Bogomilsky1, Kevin S Jones1,2
ORCID iDs: Kevin S Jones
  1. Department of Pharmacology, University of Michigan Medical School, Ann Arbor, Michigan 48109
  2. Neuroscience Graduate Program, University of Michigan Medical School, Ann Arbor, Michigan 48109
Institutions: University of Michigan (United States); Michigan Medicine (United States)
Journal: eNeuro, volume 13, issue 8, pages ENEURO.0050-26.2026
Dates: received 7 February 2026; accepted 17 June 2026; published online 4 August 2026; in print August 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1523/eneuro.0050-26.2026 · PMID 42498666 · PMCID PMC13456957 · OpenAlex W7170856490
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: mouse (organism)
Methods: Spectral & time-frequency, Preprocessing, Connectivity, Statistics, fMRI & imaging
Keywords: cognitive flexibility, gamma oscillations, locus ceruleus, medial prefrontal cortex, NMDA receptor GluN2A, norepinephrine
MeSH: Cognitive Flexibility*, Locus Coeruleus*, Norepinephrine*, Prefrontal Cortex*, Receptors, N-Methyl-D-Aspartate*, Animals, Male, Mice, Inbred C57BL, Mice, Knockout, Neural Pathways, Optogenetics, Reversal Learning (* major topic)
Topic: Neuroscience and Neuropharmacology Research (Cellular and Molecular Neuroscience, Neuroscience), according to OpenAlex
Funding: BI | Stanley Center for Psychiatric Research, Broad Institute
Citations: not cited yet (Europe PMC); 33 references in the paper

Abstract

Cognitive flexibility—the ability to adapt behavior when contingencies change—is impaired in psychiatric disorders involving prefrontal dysfunction. The medial prefrontal cortex (mPFC) relies on noradrenergic input from the locus ceruleus (LC), yet the molecular mechanisms enabling this neuromodulatory control remain unclear. Here we show that GluN2A-containing NMDA receptors are required for LC–mPFC regulation of network dynamics and reversal learning in male mice. Optogenetic activation of LC→mPFC projections enhanced reversal learning in wild-type (WT) and heterozygous mice but not in global Grin2a knock-outs, whereas LC inhibition impaired performance only in WT animals. In slices, norepinephrine (NE) and LC stimulation induced gamma and high-frequency oscillations in WT mPFC that were blocked by α2-adrenergic antagonism, but these oscillatory responses were undetectable in Grin2a mutants. Grin2a mutants also exhibited increased LC axonal density and elevated NE transporter expression in the prelimbic cortex, consistent with enhanced noradrenergic clearance capacity. Together, these findings identify GluN2A as a key determinant of LC–prefrontal circuit function supporting cognitive flexibility. They suggest that functional deficits in these mutants should be interpreted within the context of compensatory structural hyperinnervation resulting from global GluN2A deficiency, which may reflect a developmental adaptation rather than acute signaling loss. Furthermore, these results promote α2-adrenergic pathways as potential entry points for restoring prefrontal network coordination.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repositories

Its files are read in the Code ↔ Paper reader above, with 2 matches between paragraphs and lines of code.

NeuroDataa/PatchClampAnalysis

License: none: the authors keep all their rights
State: the link is dead, verified on 27 September 2026
Evidence: found in the paper
Software Heritage: not archived
Found in: the text, “Patch-clamp data analysis”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link is dead
  • 27 September 2026: the link is dead

NeuroDataa/Grin2a_LC_mPFC_pMEA_Analysis

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 29e3811b47f2151df71c5dd3445ada824d04a819, 13 May 2025
Languages: Jupyter (2)
Size: 2 files, 2 scripts
Software Heritage: not archived
Found in: the text, “Multielectrode array (MEA) recordings”
Holds: 2 notebooks
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: h5py (2 files), Matplotlib (2 files), MNE-Python (2 files), NumPy (2 files), pandas (2 files), scikit-learn (2 files), SciPy (2 files), seaborn (2 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
2 files

NeuroData/PatchClampAnalysis

License: none: the authors keep all their rights
State: the link is dead, verified on 27 September 2026
Evidence: found in the paper
Software Heritage: not archived
Found in: “Data and code availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link is dead
  • 27 September 2026: the link is dead

NeuroDataa/MEA_Analysis

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 0 files, 0 scripts
Software Heritage: not archived
Found in: “Data and code availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 4 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 2 scripts, each with its path and the digest of its content;
  • 2 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data and code availability

All original analysis code generated for this study has been deposited in publicly accessible GitHub repositories:

https://github.com/NeuroData/PatchClampAnalysis https://github.com/NeuroDataa/MEA_Analysis

This study did not generate new or unique reagents.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 4 authors, 6 keywords, 12 MeSH terms, 1 funder, 33 references.

Cite

This paper

Hosseini, H., Evans-Martin, S., Bogomilsky, E., & Jones, K. S. (2026). GluN2A Enables Noradrenergic Control of Prefrontal Oscillations and Cognitive Flexibility. eNeuro, 13(8), ENEURO.0050-26.2026. https://doi.org/10.1523/eneuro.0050-26.2026

BibTeX

@article{hosseini2026glun2a,
author = {Hosseini, Hassan and Evans-Martin, Sky and Bogomilsky, Emma and Jones, Kevin S},
title = {{GluN2A Enables Noradrenergic Control of Prefrontal Oscillations and Cognitive Flexibility}},
journal = {eNeuro},
year = {2026},
month = aug,
volume = {13},
number = {8},
pages = {ENEURO.0050--26.2026},
publisher = {Society for Neuroscience},
issn = {2373-2822},
doi = {10.1523/eneuro.0050-26.2026},
url = {https://doi.org/10.1523/eneuro.0050-26.2026},
pmid = {42498666},
pmcid = {PMC13456957}
}

RIS

TY - JOUR
AU - Hosseini, Hassan
AU - Evans-Martin, Sky
AU - Bogomilsky, Emma
AU - Jones, Kevin S
TI - GluN2A Enables Noradrenergic Control of Prefrontal Oscillations and Cognitive Flexibility
T2 - eNeuro
J2 - eNeuro
PY - 2026
DA - 2026/08/07
VL - 13
IS - 8
SP - ENEURO.0050
EP - 26.2026
SN - 2373-2822
PB - Society for Neuroscience
DO - 10.1523/eneuro.0050-26.2026
UR - https://doi.org/10.1523/eneuro.0050-26.2026
LA - en
ER -

CSL-JSON

{
"id": "10.1523/eneuro.0050-26.2026",
"type": "article-journal",
"title": "GluN2A Enables Noradrenergic Control of Prefrontal Oscillations and Cognitive Flexibility",
"container-title": "eNeuro",
"author": [
{
"family": "Hosseini",
"given": "Hassan"
},
{
"family": "Evans-Martin",
"given": "Sky"
},
{
"family": "Bogomilsky",
"given": "Emma"
},
{
"family": "Jones",
"given": "Kevin S"
}
],
"container-title-short": "eNeuro",
"volume": "13",
"issue": "8",
"page": "ENEURO.0050-26.2026",
"DOI": "10.1523/eneuro.0050-26.2026",
"PMID": "42498666",
"PMCID": "PMC13456957",
"ISSN": "2373-2822",
"publisher": "Society for Neuroscience",
"URL": "https://doi.org/10.1523/eneuro.0050-26.2026",
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
7
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s41593-026-02258-4 [code]
Laminar organization of cellular microcircuits modulating human interictal epileptiform discharges.
Journal: Nature neuroscience
In common: MNE-Python, seaborn, scikit-learn, 4 other tools, 1 reference
[2] doi:10.3389/fpsyg.2026.1774068 [code]
Analysis of cognitive mechanisms in phoneme perception and pronunciation errors among Korean language learners.
Journal: Frontiers in psychology
In common: MNE-Python, h5py, seaborn, 5 other tools
[3] doi:10.3389/fncom.2026.1786996 [code]
Schumann-anchored golden ratio organization of human neural oscillations.
Journal: Frontiers in computational neuroscience
In common: MNE-Python, h5py, seaborn, 5 other tools
[4] doi:10.1038/s41598-026-50946-9 [code]
Fixation-related potentials reveal that confusing program code elicits a late frontal positivity.
Journal: Scientific reports
In common: MNE-Python, h5py, seaborn, 5 other tools
[5] doi:10.1038/s41593-026-02285-1 [code]
Fixation duration on natural scenes is explained by memory encoding not processing demand.
Journal: Nature neuroscience
In common: MNE-Python, h5py, seaborn, 5 other tools
[6] doi:10.1162/imag.a.1227 [code]
Large language models reveal the neural tracking of linguistic context in attended and unattended multi-talker speech.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: MNE-Python, h5py, seaborn, 5 other tools
[7] doi:10.1038/s41531-026-01372-1 [code]
Varying patterns of association between cortical large-scale networks and subthalamic nucleus activity in Parkinson's disease.
Journal: NPJ Parkinson's disease
In common: MNE-Python, h5py, seaborn, 5 other tools
[8] doi:10.7554/elife.106543 [code]
Stimulus dependencies-rather than next-word prediction-can explain pre-onset brain encoding in naturalistic listening designs.
Journal: eLife
In common: MNE-Python, h5py, seaborn, 5 other tools
[9] doi:10.1117/1.nph.13.2.025001 [code]
Surface-based image reconstruction optimization for high-density functional near-infrared spectroscopy.
Journal: Neurophotonics
In common: MNE-Python, h5py, seaborn, 5 other tools
[10] doi:10.1038/s41597-025-05174-7 [code]
A large-scale MEG and EEG dataset for object recognition in naturalistic scenes
Journal: n/a
In common: MNE-Python, h5py, seaborn, 5 other tools

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.