OSCR

Active dissociation of intracortical spiking and high gamma activity.

Code ↔ Paper

6 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 6 matches
  1. [1] § Methods › ONF performance metrics ↔ behavior_analysis.ipynb, lines 194–260 · score 0.62 · entry angle, HG target trials, spike target trials, monkey CE, metrics, cursor
  2. [2] § Methods › Factor analysis ↔ subspace_analysis.ipynb, lines 139–170 · score 0.62 · linear regression model, concatenated spike, scored, CE HGA, spike rates, weight
  3. [3] § Methods › Neural signal recording ↔ neural_signal_analysis.ipynb, lines 52–114 · score 0.60 · low spike rate, shunted electrodes, neural, channel, signals, binned
  4. [4] § Distributed spikes do not leak into HGA ↔ subspace_analysis.ipynb, lines 197–228 · score 0.60 · weighted sum, randomly selected electrodes, DWSS, spike rate, correlated, CE
  5. [5] § Distributed spikes do not leak into HGA ↔ subspace_analysis.ipynb, lines 197–228 · score 0.56 · random DWSS, weighted sum, randomly selected electrodes, Correlation, CE, HGA
  6. [6] § Methods › cPCA ↔ contrastive/__init__.py, lines 12–20 · score 0.55 · eigenvalue decomposition, cPCA, foreground, variance, background

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 · 515 lines · 20 KB · CC-BY-4.0 · 3 matches

  1. # %% [markdown]
  2. # # This Jupyter notebook file produces the subspace analysis results in the paper
  3. # %%
  4. import numpy as np
  5. import scipy
  6. import mat73
  7. import sklearn
  8. from utility_functions import *
  9. import pandas as pd
  10. import matplotlib.pyplot as plt
  11. import matplotlib.patches as Patches
  12. from itertools import chain
  13. from sklearn.preprocessing import StandardScaler
  14. from sklearn.decomposition import FactorAnalysis,PCA
  15. from sklearn.linear_model import LinearRegression
  16. import matplotlib.ticker as mticker
  17. from contrastive import CPCA
  18. plt.rcParams["axes.linewidth"] = 2
  19. plt.rcParams["axes.edgecolor"] = 'k'
  20. plt.rcParams['grid.alpha'] = 0
  21. # %% [markdown]
  22. # # Load the data and meta data
  23. # Here we need to load the entire trials. So `data_type` should be "trials"
  24. # %%
  25. data_type='trials' # this could be 'trials', 'last4s', 'first4s'.
  26. monkey='C' # monkey should be one of 'C', 'J' or 'M'
  27. CE=63 # channel name. NOTE: this is not the channel number or the channel index
  28. # load the grid layout of the monkey
  29. grid=get_grid(monkey)
  30. # load the spike channel number
  31. spike_channel=get_channel(monkey,CE)
  32. # load the location of the CE on the grid
  33. loc_x,loc_y=get_CE_loc(grid,spike_channel)
  34. # load the shunted electrodes for this monkey. This is the python index
  35. shunted_electrodes=load_shunted_electrodes(monkey,spike_channel)
  36. # finally, load the raw data
  37. raw_data=load_ONF_data(data_type, monkey, CE)
  38. # We only need the "learned" files.
  39. raw_data=raw_data[raw_data['file_types']=='learned']
  40. # %% [markdown]
  41. # Optional: Visualize the data table. This may take some time.
  42. # %%
  43. raw_data
  44. # %% [markdown]
  45. # # Pre-process the spike rate and HGA data
  46. #
  47. # 1. include only the successful trials.
  48. # 2. remove shunted channels
  49. # 3. remove NAN and empty channels
  50. # 4. remove channels with low firing rate
  51. # %%
  52. # Here we concatenate all the successful trials together for every file.
  53. spike_rate_all = list(chain.from_iterable([[d[0] for d in data] for data in raw_data['spike_rate'].to_list()]))
  54. HGA_all = list(chain.from_iterable([[d[0] for d in data] for data in raw_data['HGA'].to_list()]))
  55. # We concatenate the trial type
  56. trial_types_all=np.concatenate(raw_data['trial_types'].to_list())
  57. # We concatenate the target type
  58. target_types_all=np.concatenate(raw_data['target_types'].to_list())
  59. # We take out only the successful trials
  60. spike_rate_successful=[a for a,t in zip(spike_rate_all,trial_types_all) if t==1 ]
  61. HGA_successful=[a for a,t in zip(HGA_all,trial_types_all) if t==1]
  62. # concatenate the signals
  63. spike_rate_flat=np.concatenate(spike_rate_successful,axis=-1)
  64. HGA_flat=np.concatenate(HGA_successful,axis=-1)
  65. n_time=spike_rate_flat.shape[0]
  66. # save the indices of the leftover electrodes
  67. leftover_electrode_list=np.arange(0,spike_rate_flat.shape[0])
  68. print(f"Before preprocessing, signals have the shape (chan x (time x n_trials)): {spike_rate_flat.shape}")
  69. # remove shunted electrodes
  70. spike_rate_flat=np.delete(spike_rate_flat,shunted_electrodes,axis=0)
  71. HGA_flat=np.delete(HGA_flat,shunted_electrodes,axis=0)
  72. leftover_electrode_list=np.delete(leftover_electrode_list,shunted_electrodes)
  73. print(f"Removed the shunted electrodes, signals have the shape (chan x (time x n_trials)): {spike_rate_flat.shape}")
  74. # check NAN electrodes in the data
  75. has_nan_spike_rate=np.sum(np.isnan(spike_rate_flat),axis=1)
  76. has_nan_HGA=np.sum(np.isnan(HGA_flat),axis=1)
  77. has_nan=np.where(has_nan_spike_rate+has_nan_HGA)
  78. # remove the electrodes with NAN values
  79. spike_rate_flat=np.delete(spike_rate_flat,has_nan,axis=0)
  80. HGA_flat=np.delete(HGA_flat,has_nan,axis=0)
  81. leftover_electrode_list=np.delete(leftover_electrode_list,has_nan,axis=0)
  82. print(f"Removed the electrodes with NAN values, signals have the shape (chan x (time x n_trials)): {spike_rate_flat.shape}")
  83. # check the electrodes with constant low spike rate
  84. low_spike_rate=5 # spikes/s
  85. has_low_spike_rate=np.sum(spike_rate_flat,axis=1)<n_time*low_spike_rate/20 # 20 Hz
  86. has_low_spike_rate=np.where(has_low_spike_rate==spike_rate_flat.shape[-1])
  87. # remove the electrodes with low spike rate
  88. spike_rate_flat=np.delete(spike_rate_flat,has_low_spike_rate,axis=0)
  89. HGA_flat=np.delete(HGA_flat,has_low_spike_rate,axis=0)
  90. leftover_electrode_list=np.delete(leftover_electrode_list,has_low_spike_rate)
  91. print(f"Removed the electrodes with low spike rate, signals have the shape (chan x (time x n_trials)): {spike_rate_flat.shape}")
  92. # get the number of clean electrodes
  93. n_clean_electrodes=len(leftover_electrode_list)
  94. CE_index=np.where(leftover_electrode_list==spike_channel-1)[0]
  95. # %% [markdown]
  96. # ### Optional: check the correlation between CE neural siginals and velocities
  97. # %%
  98. # get the velocity
  99. velocity=list(chain.from_iterable(raw_data['velocity'].to_list()))
  100. velocity=np.concatenate([v[0] for v, t in zip(velocity,trial_types_all) if t==1.0],axis=0)
  101. # plot the correlation of velocity x and spike rate, velocity y and HGA
  102. fig,ax=plt.subplots(1,2, figsize=(5,3))
  103. ax[0].scatter(np.squeeze(velocity[:,0]),np.squeeze(spike_rate_flat[CE_index,:]),color='b',alpha=0.2,s=2,marker='.')
  104. ax[0].set_xlabel('velocity x')
  105. ax[0].set_ylabel('spike rate (Hz)')
  106. ax[1].scatter(np.squeeze(velocity[:,1]),np.squeeze(HGA_flat[CE_index,:]),color='b',alpha=0.2,s=2,marker='.')
  107. ax[1].set_xlabel('velocity y')
  108. ax[1].set_ylabel('HGA amplitude')
  109. # title
  110. plt.suptitle("Correlation between the neural signals and velocity")
  111. plt.tight_layout()
  112. plt.show()
  113. # %% [markdown]
  114. # # Comparing the first factor to CE HGA and spike rate
  115. # %%
  116. # factor analysis on the mean centered, concatenated spike rate
  117. n_factors=10
  118. # zscore the spike rate data
  119. spike_rate_zscore=scipy.stats.zscore(spike_rate_flat,axis=None)
  120. # run factor analysis
  121. fa=FactorAnalysis(n_components=n_factors,rotation='varimax')
  122. factors=fa.fit_transform(spike_rate_zscore.T)
  123. weights=fa.components_
  124. # calculate the R between the factors and CE neural signals
  125. R_factors_spike_rate=[]
  126. R_factors_HGA=[]
  127. for i in range(n_factors):
  128. # fit a linear regression model on factors and spike rate
  129. lr_factors_spike_rate=LinearRegression().fit(factors[:,:i+1],spike_rate_zscore[CE_index,:].T)
  130. # calculate the R2
  131. r2_factors_spike_rate=lr_factors_spike_rate.score(factors[:,:i+1],spike_rate_zscore[CE_index,:].T)
  132. R_factors_spike_rate.append(np.sqrt(r2_factors_spike_rate))
  133. # fit a linear regression model on factors and HGA
  134. lr_factors_HGA=LinearRegression().fit(factors[:,:i+1],HGA_flat[CE_index,:].T)
  135. # calculate the R2
  136. r2_factors_HGA=lr_factors_HGA.score(factors[:,:i+1],HGA_flat[CE_index,:].T)
  137. R_factors_HGA.append(np.sqrt(r2_factors_HGA))
  138. # %% [markdown]
  139. # plot R and the factor weights
  140. # %%
  141. fig,ax = plt.subplots(1,2, figsize=(7,3))
  142. im=plot_on_grid(ax[0],
  143. leftover_electrode_list,grid,
  144. np.abs(weights[0]),(loc_x,loc_y),
  145. cmap=mpb.cm.viridis
  146. )
  147. im.set_clim(0,1)
  148. cb=plt.colorbar(im,shrink=0.8)
  149. cb.set_label('First factor weights',fontsize=12)
  150. l1=ax[1].plot(np.arange(1,n_factors+1),R_factors_spike_rate,lw=3, label='spike rate',color='magenta')
  151. l2=ax[1].plot(np.arange(1,n_factors+1),R_factors_HGA,lw=3,label='HGA',color='darkcyan')
  152. ax[1].set_xticks(np.arange(1,n_factors+1,3))
  153. ax[1].set_yticks(np.arange(0,1,0.2))
  154. ax[1].set_xlabel('number of factors')
  155. ax[1].set_ylabel('R')
  156. ax[1].legend(title='Factors fitted to')
  157. plt.tight_layout()
  158. plt.show()
  159. # %% [markdown]
  160. # ### here we calculate the correlation between DWSS and HGA
  161. # %%
  162. # calculate the weighted grid for CE
  163. weighted_grid=calculate_weighted_grid((loc_x,loc_y),power=2)
  164. weighted_grid=get_the_weights(weighted_grid,
  165. leftover_electrode_list,
  166. grid.flatten())
  167. # calculate the weighted sum of spike using the weights_grid
  168. corr_HGA_DWSS=np.corrcoef(
  169. np.vstack([HGA_flat[CE_index],np.average(spike_rate_zscore,axis=0,weights=weighted_grid)]))[0,1]
  170. # calculate the weighted grid for 20 random electrodes
  171. n_random_pairs=20
  172. random_pairs=draw_random_pairs(n_random_pairs,except_loc=(loc_x,loc_y))
  173. random_weighted_grids=[get_the_weights(calculate_weighted_grid(r_pairs,power=2,emit_start_location=False),leftover_electrode_list,grid.flatten()) for r_pairs in random_pairs]
  174. # calculate the weighted sum of spike using random weighted grids.
  175. corr_HGA_random_DWSS=[np.corrcoef(np.vstack([HGA_flat[CE_index],np.average(spike_rate_zscore,axis=0,weights=r_grid)]))[0,1] for r_grid in random_weighted_grids]
  176. # calculate the DWSS and HGA on non CE electrodes
  177. # get the randomly selected electrodes
  178. random_electrodes=np.random.choice(np.delete(leftover_electrode_list,np.where(leftover_electrode_list==spike_channel-1)),n_random_pairs,replace=False)+1
  179. random_electrodes_loc=[np.where(grid==r) for r in random_electrodes]
  180. # calculate the DWSS
  181. random_weighted_grids=[get_the_weights(calculate_weighted_grid(r,power=2),leftover_electrode_list,grid.flatten()) for r in random_electrodes_loc]
  182. # calculate the correlation between DWSS and HGA on those random electrodes
  183. corr_random_HGA_DWSS=[np.corrcoef(np.squeeze(HGA_flat[np.where(leftover_electrode_list==random_electrodes[n]-1)[0]]), np.average(spike_rate_zscore,axis=0,weights=random_weighted_grids[n]))[0,1] for n in range(n_random_pairs)]
  184. # %% [markdown]
  185. # plotting
  186. # %%
  187. fig,ax=plt.subplots(1,1,figsize=(3,3))
  188. im=plot_on_grid(ax,leftover_electrode_list,grid,np.sqrt(weighted_grid),(loc_x,loc_y),cmap=mpb.cm.Blues)
  189. im.set_clim(0,0.003)
  190. ax.set_title("distance weights",fontsize=10)
  191. ax.spines[['left','bottom','top','right']].set_linewidth(3)
  192. cb=fig.colorbar(im,shrink=0.8,ticks=[0,0.002],format=mticker.FixedFormatter(['0', '3']))
  193. cb.set_label("weights",size=10)
  194. cb.ax.set_yticklabels(['0', '3']) # vertically oriented colorbar
  195. cb.ax.tick_params(labelsize=10)
  196. fig,ax=plt.subplots(1,1,figsize=(3,2))
  197. ax.bar([0,1,2],
  198. np.abs(np.squeeze([
  199. corr_HGA_DWSS,
  200. np.mean(corr_HGA_random_DWSS),
  201. np.mean(corr_random_HGA_DWSS)]))
  202. ,color=['crimson','grey',"lightblue"],yerr=(0,np.std(corr_HGA_random_DWSS),np.std(corr_random_HGA_DWSS)),error_kw=dict(lw=3))
  203. # plot the raw data
  204. x = np.random.normal(1, 0.05, size=len(corr_HGA_random_DWSS)) # Jitter
  205. ax.scatter(x, np.array(corr_HGA_random_DWSS), color='k', alpha=0.5, edgecolor='black', linewidth=0)
  206. x = np.random.normal(2, 0.05, size=len(corr_random_HGA_DWSS)) # Jitter
  207. ax.scatter(x, np.array(corr_random_HGA_DWSS), color='k', alpha=0.5, edgecolor='black', linewidth=0)
  208. ax.set_xticks([0,1,2],["DWSS","random","non-CE"],rotation=0,fontsize=10)
  209. ax.set_ylabel("R",fontsize=10)
  210. ax.set_ylim(-0.2,0.8)
  211. ax.tick_params(axis='y', labelsize=10)
  212. ax.tick_params(axis='x', labelsize=10)
  213. ax.spines[['top','right']].set_visible(False)
  214. ax.spines[['left','bottom']].set_linewidth(3)
  215. plt.show()
  216. # %% [markdown]
  217. # # PCA and cPCA analysis on the correlation and subspace angles
  218. # 1. define SP+ and HG+ NAS
  219. # 2. perform PCA on the data separately
  220. # 3. perform cPCA on the data separately
  221. # 4. calculate the angles between them
  222. # %%
  223. # calculate the CE spike rate and HGA
  224. CE_spike_rate=StandardScaler().fit_transform(spike_rate_zscore[CE_index].T)
  225. CE_HGA=StandardScaler().fit_transform(HGA_flat[CE_index].T)
  226. # calculated the sp+ and hg+ indices
  227. NAS=np.squeeze(np.arctan2(CE_HGA, CE_spike_rate)*180/np.pi)
  228. sp_ind=(NAS>=-15)&(NAS<=15)
  229. hg_ind=(NAS>=75)&(NAS<=105)
  230. # %% [markdown]
  231. # Quick check of the dimensionality of the two data
  232. # %%
  233. n_PCs=10
  234. pca=PCA(n_components=n_PCs)
  235. plt.figure()
  236. plt.plot(np.cumsum(pca.fit(spike_rate_zscore[:,sp_ind].T).explained_variance_ratio_),lw=3,color='magenta')
  237. plt.plot(np.cumsum(pca.fit(spike_rate_zscore[:,hg_ind].T).explained_variance_ratio_),lw=3,color='darkblue')
  238. plt.ylabel("cumulative VAF")
  239. plt.xlabel("number of PCs")
  240. plt.legend(['SP+','HG+'])
  241. # %%
  242. # first we need to fine the best alpha for cPCA
  243. cpca=CPCA(n_components=n_PCs,standardize=False)
  244. # specify the range the step for the alphas to try
  245. n_alphas=15
  246. max_log_alpha=2
  247. # prepare the data
  248. sp_data=scipy.stats.zscore(spike_rate_zscore[:,sp_ind],axis=None)
  249. hg_data=scipy.stats.zscore(spike_rate_zscore[:,hg_ind],axis=None)
  250. # Use the plots to determin the best alpha
  251. projected_fg,best_alphas=cpca.fit_transform(hg_data.T,
  252. sp_data.T,
  253. n_alphas=n_alphas, max_log_alpha=max_log_alpha, alpha_selection='all',return_alphas=True)
  254. projected_bg=cpca.transform(sp_data.T,n_alphas=n_alphas, max_log_alpha=max_log_alpha, alpha_selection='all')
  255. # %% [markdown]
  256. # - Plotting the background and foreground data with different alpha
  257. # - The peak of the ratio of variances in the second plot is where the best alpha value is
  258. # %%
  259. plt.figure(figsize=[3*n_alphas+1,3])
  260. subsample=0.2 # only plot number of subsample x data
  261. all_ratio=[]
  262. for j, pack in enumerate(zip(projected_fg,projected_bg)):
  263. # retrieve and subsample the background and foreground data
  264. fg,bg=pack
  265. subsample_bg=np.random.choice(len(bg),int(len(bg)*subsample),replace=False)
  266. subsample_fg=np.random.choice(len(fg),int(len(fg)*subsample),replace=False)
  267. # calculate the variance
  268. fg_variance=np.var(fg)
  269. bg_variance=np.var(bg)
  270. plt.subplot(1,n_alphas+1,j+1)
  271. plt.scatter(bg[subsample_bg,0],bg[subsample_bg,1], color='k', alpha=0.5, label='background',s=3)
  272. plt.scatter(fg[subsample_fg,0],fg[subsample_fg,1], color='r', alpha=0.5, label='foreground',s=3)
  273. plt.title('Alpha='+str(np.round(best_alphas[j],2)))
  274. plt.xlabel(f"background {bg_variance:0.2f}\nforeground {fg_variance:0.2f}\nratio {fg_variance/bg_variance:0.2f}")
  275. all_ratio.append(fg_variance/bg_variance)
  276. plt.legend()
  277. # plot the variance ratios for each alpha we selected
  278. plt.figure()
  279. plt.plot(best_alphas,all_ratio,lw=3)
  280. plt.xscale("log")
  281. plt.yticks(np.linspace(1,19,6))
  282. plt.xlabel('alpha value')
  283. plt.ylabel('ratio of variances')
  284. plt.ylim(0,5)
  285. print(f"The best alpha value for this dataset it: {best_alphas[np.argmax(all_ratio)]}")
  286. plt.show()
  287. # %% [markdown]
  288. # ## Now we are using the best alphao to produce the cPCs and correlate them with CE spike rate and CE HGA
  289. # %%
  290. alpha_selected=best_alphas[np.argmax(all_ratio)]
  291. ######################## fg: SP+, bg: HG+ ################################
  292. fg_cov,bg_cov=get_cov(sp_data.T,hg_data.T)
  293. v_top_SP=get_cpca_loadings(fg_cov,bg_cov,alpha_selected,n_PCs)
  294. projected_bg_SP=hg_data.T @ v_top_SP
  295. projected_fg_SP=sp_data.T @ v_top_SP
  296. # fit the unique component one by one and see which component(s) contribute the most
  297. cumsum_R2_SP_CPCA_FR=[]
  298. cumsum_R2_SP_CPCA_HG=[]
  299. for i in range(n_PCs):
  300. R2=(LinearRegression(fit_intercept=False)
  301. .fit(spike_rate_zscore.T@v_top_SP[:,:i+1],np.squeeze(spike_rate_zscore[CE_index]))
  302. .score(spike_rate_zscore.T@v_top_SP[:,:i+1],np.squeeze(spike_rate_zscore[CE_index])))
  303. cumsum_R2_SP_CPCA_FR.append(np.maximum(R2,0))
  304. R2=(LinearRegression(fit_intercept=False)
  305. .fit(spike_rate_zscore.T@v_top_SP[:,:i+1],np.squeeze(HGA_flat[CE_index]))
  306. .score(spike_rate_zscore.T@v_top_SP[:,:i+1],np.squeeze(HGA_flat[CE_index])))
  307. cumsum_R2_SP_CPCA_HG.append(np.maximum(R2,0))
  308. ######################### fg: SP+, bg: HG+ ################################
  309. fg_cov,bg_cov=get_cov(hg_data.T,sp_data.T)
  310. v_top_HG=get_cpca_loadings(fg_cov,bg_cov,alpha_selected,n_PCs)
  311. projected_bg_HG=sp_data.T @ v_top_HG
  312. projected_fg_HG=hg_data.T @ v_top_HG
  313. # fit the unique component one by one and see which component(s) contribute the most
  314. cumsum_R2_HG_CPCA_FR=[]
  315. cumsum_R2_HG_CPCA_HG=[]
  316. for i in range(n_PCs):
  317. R2=(LinearRegression(fit_intercept=False)
  318. .fit(spike_rate_zscore.T@v_top_HG[:,:i+1],np.squeeze(spike_rate_zscore[CE_index,:]))
  319. .score(spike_rate_zscore.T@v_top_HG[:,:i+1],np.squeeze(spike_rate_zscore[CE_index,:])))
  320. cumsum_R2_HG_CPCA_FR.append(np.maximum(R2,0))
  321. R2=(LinearRegression(fit_intercept=False)
  322. .fit(spike_rate_zscore.T@v_top_HG[:,:i+1],np.squeeze(HGA_flat[CE_index,:]))
  323. .score(spike_rate_zscore.T@v_top_HG[:,:i+1],np.squeeze(HGA_flat[CE_index,:])))
  324. cumsum_R2_HG_CPCA_HG.append(np.maximum(R2,0))
  325. # %% [markdown]
  326. # Now we perform PCA on SP+ data and HG+ data separately
  327. # %%
  328. # same analysis using PCA (this equals to fg with alpha=0)
  329. pca_projection_HG=PCA(n_PCs).fit_transform(hg_data.T)
  330. pca_projection_SP=PCA(n_PCs).fit_transform(sp_data.T)
  331. pca_loading_HG=PCA(n_PCs).fit(hg_data.T).components_.T
  332. pca_loading_SP=PCA(n_PCs).fit(sp_data.T).components_.T
  333. shuffle_pca_loading_HG=np.copy(pca_loading_HG)
  334. shuffle_pca_loading_SP=np.copy(pca_loading_SP)
  335. np.random.shuffle(shuffle_pca_loading_HG)
  336. np.random.shuffle(shuffle_pca_loading_SP)
  337. # %% [markdown]
  338. # Prepare the shuffled dataset as control
  339. # %%
  340. sp_fr_shuffle = np.copy(sp_data)
  341. np.random.shuffle(sp_fr_shuffle)
  342. hg_fr_shuffle = np.copy(hg_data)
  343. np.random.shuffle(hg_fr_shuffle)
  344. fg_cov_shuffle,bg_cov_shuffle=get_cov(sp_fr_shuffle.T,hg_fr_shuffle.T)
  345. v_top_SP_shuffle=get_cpca_loadings(fg_cov_shuffle,bg_cov_shuffle,alpha_selected,n_PCs)
  346. np.random.shuffle(sp_fr_shuffle)
  347. np.random.shuffle(hg_fr_shuffle)
  348. bg_cov_shuffle,fg_cov_shuffle=get_cov(sp_fr_shuffle.T,hg_fr_shuffle.T)
  349. v_top_HG_shuffle=get_cpca_loadings(fg_cov_shuffle,bg_cov_shuffle,alpha_selected,n_PCs)
  350. # %% [markdown]
  351. # Calculate the subspace angles between different PCs and cPCs
  352. # %%
  353. subspace_angles_cPCs=scipy.linalg.subspace_angles(v_top_HG, v_top_SP)
  354. subspace_angles_cPCs_shuffle=scipy.linalg.subspace_angles(v_top_HG_shuffle, v_top_SP_shuffle)
  355. subspace_angles_PCs=scipy.linalg.subspace_angles(pca_loading_HG, pca_loading_SP)
  356. subspace_angles_PCs_shuffle=scipy.linalg.subspace_angles(shuffle_pca_loading_HG, shuffle_pca_loading_SP)
  357. subspace_angles_cPCA_PCA_HG=scipy.linalg.subspace_angles(v_top_HG, pca_loading_HG)
  358. subspace_angles_cPCA_PCA_SP=scipy.linalg.subspace_angles(v_top_SP, pca_loading_SP)
  359. subspace_angles_shuffle_cPCA_PCA_HG=scipy.linalg.subspace_angles(v_top_HG_shuffle, shuffle_pca_loading_HG)
  360. subspace_angles_shuffle_cPCA_PCA_SP=scipy.linalg.subspace_angles(v_top_SP_shuffle, shuffle_pca_loading_SP)
  361. # %% [markdown]
  362. # Here we demostrated the correlation (R) between the spike rate, HGA and SP+/HG+ cPCs
  363. # %%
  364. fig,ax=plt.subplots(1,1,figsize=(4,3))
  365. ax.plot(np.linspace(1,3*n_PCs,n_PCs),np.sqrt(cumsum_R2_HG_CPCA_HG),color='tab:blue',lw=3)
  366. ax.plot(np.linspace(1,3*n_PCs,n_PCs),np.sqrt(cumsum_R2_HG_CPCA_FR),color='grey',lw=3)
  367. ax.set_xticks(np.linspace(2,3*n_PCs+0.5,5),np.int16(np.linspace(1,n_PCs,5)),fontsize=14)
  368. ax.set_yticks(np.linspace(0,1,3),np.linspace(0,1,3),fontsize=14)
  369. ax.legend(["HGA","Spike rate"],frameon=False,loc="upper right",title="HG+ cPCs fitted to",title_fontsize=14,fontsize=10)
  370. ax.set_xlabel("num of HG+ cPCs added",fontsize=14)
  371. ax.set_ylabel("R",fontsize=14)
  372. ax.spines['top'].set_visible(False)
  373. ax.spines['right'].set_visible(False)
  374. plt.tight_layout()
  375. fig,ax=plt.subplots(1,1,figsize=(4,3))
  376. ax.plot(np.linspace(1,3*n_PCs,n_PCs),np.sqrt(cumsum_R2_SP_CPCA_HG),color='grey',lw=2)
  377. ax.plot(np.linspace(1,3*n_PCs,n_PCs),np.sqrt(cumsum_R2_SP_CPCA_FR),color='crimson',lw=2)
  378. ax.set_xticks(np.linspace(2,3*n_PCs+0.5,5),np.int16(np.linspace(1,n_PCs,5)),fontsize=14)
  379. ax.set_yticks(np.linspace(0,1,3),np.linspace(0,1,3),fontsize=15)
  380. ax.legend(["HGA","Spike rate"],frameon=False,loc="best",title="SP+ cPCs fitted to",title_fontsize=14,fontsize=10)
  381. ax.set_xlabel("num of SP+ cPCs added",fontsize=14)
  382. ax.set_ylabel("R",fontsize=14)
  383. ax.spines['top'].set_visible(False)
  384. ax.spines['right'].set_visible(False)
  385. plt.tight_layout()
  386. plt.show()
  387. # %% [markdown]
  388. # Here we demonstrate the cPCA weights and that HG+ cPCs have more activation in other electrodes, compared to SP+ cPCs
  389. # %%
  390. ### another figure: plot the first cPC on the grid
  391. first_cPC_weights_SP=np.abs(v_top_SP[:,0])/np.abs(v_top_SP[:,0]).max()
  392. first_cPC_weights_HG=np.abs(v_top_HG[:,0])/np.abs(v_top_HG[:,0]).max()
  393. fig,ax=plt.subplots(1,2,layout='constrained',figsize=(7,4))
  394. im1=plot_on_grid(ax[0],leftover_electrode_list,grid,first_cPC_weights_SP,(loc_x,loc_y),
  395. cmap=mpb.cm.viridis,show_chan_indx=False,CE_color='r')
  396. im2=plot_on_grid(ax[1],leftover_electrode_list,grid,first_cPC_weights_HG,(loc_x,loc_y),
  397. cmap=mpb.cm.viridis,show_chan_indx=False,CE_color='r')
  398. #clim_min=np.min([v_top_SP[:,0],v_top_HG[:,0]])
  399. clim=np.max([first_cPC_weights_SP,first_cPC_weights_HG])
  400. #clim=np.maximum(clim_max,np.abs(clim_min))
  401. im1.set_clim([0,clim-0.3])
  402. im2.set_clim([0,clim-0.1])
  403. cb=fig.colorbar(im2, ax=ax[:], location='right',shrink=0.6,pad=0.1)
  404. cb.set_label("cPC weights",fontsize=12)
  405. cb.ax.tick_params(labelsize=12)
  406. ax[0].set_title("SP+ 1st cPC",fontsize=12)
  407. ax[1].set_title("HG+ 1st cPC",fontsize=12)
  408. ### some quantification: save the first cPC weights
  409. fig,ax=plt.subplots(1,1,figsize=(4,4))
  410. ax.violinplot([first_cPC_weights_SP,first_cPC_weights_HG])
  411. plt.show()
  412. # %% [markdown]
  413. # ---
  414. # Code developed by: [Tianhao Lei](https://github.com/caraido)

subspace_analysis.ipynb at commit 8ca016b, under CC-BY-4.0 · at the source

Overview

Authors: Tianhao Lei1, Michael R Scheid1, Robert D Flint1, Joshua I Glaser1,2,3, Marc W Slutzky1,4,5,6
  1. Department of Neurology, Northwestern University Feinberg School of Medicine, Chicago, IL USA
  2. Department of Computer Science, Northwestern University, Evanston, IL USA
  3. National Institute for Theory and Mathematics in Biology, Chicago, IL USA
  4. Department of Neuroscience, Northwestern University Feinberg School of Medicine, Chicago, IL USA
  5. Department of Physical Medicine and Rehabilitation, Northwestern University Feinberg School of Medicine, Chicago, IL USA
  6. Department of Biomedical Engineering, Northwestern University, Evanston, IL USA
Institutions: Northwestern University (United States)
Journal: Nature, volume 654, issue 8119, pages 744-750
Dates: received 6 September 2024; accepted 26 February 2026; published online 1 April 2026; in print 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1038/s41586-026-10331-y · PMID 41922776 · PMCID PMC13067879 · OpenAlex W7147612737
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Methods: Spectral & time-frequency, Connectivity, Statistics, Preprocessing, Graphs
Keywords: Neuronal physiology, Network models, Biophysical models, Neurophysiology, Extracellular recording
MeSH: Action Potentials*, Gamma Rhythm*, Macaca mulatta*, Neurons*, Animals, Brain-Computer Interfaces, Male, Models, Neurological, Synaptic Potentials (* major topic)
Topic: Neural dynamics and brain function (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: NINDS NIH HHS (RF1 NS125026, T32 NS047987, K08 NS060223, R01 NS112942, R01 NS094748, R00 NS119787, R01 NS099210); NIBIB NIH HHS (T32 EB009406)
Citations: cited by 8 papers (Europe PMC); 65 references in the paper

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repositories

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

abidlabs/contrastive

License: MIT
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: f0d3c330009cfdc76675f95859e70dc96adc3689, 7 October 2025
Languages: Jupyter (13), Python (6)
Size: 49 files, 19 scripts
Software Heritage: archived
Found in: “Code availability”
Holds: README, license file, environment (setup.py), 6 notebooks
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: NumPy (19 files), Matplotlib (15 files), scikit-learn (12 files), SciPy (7 files), Pillow (3 files), h5py (2 files), TensorFlow (2 files)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
21 files

caraido/spike-highgamma

License: CC-BY-4.0
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 8ca016b16e8b0452bec726fab1c0b118a77895dc, 4 May 2026
Languages: Jupyter (4), Python (1)
Size: 9 files, 5 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README, environment (environment.yaml), 4 notebooks
Not found: license file, CITATION.cff, tests, continuous integration, documentation
Tools: Matplotlib (5 files), SciPy (5 files), NumPy (4 files), pandas (4 files), scikit-learn (4 files)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
6 files

Code availability statement

The paper has a code availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1038/s41586-026-10331-y.

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:

  • 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 24 scripts, each with its path and the digest of its content;
  • 6 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

Datasets cited

Data availability statement

The paper has a data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1038/s41586-026-10331-y.

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 2, 28 September 2026

  • Publisher: n/a → Nature Portfolio

Version 1, 28 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 5 keywords, 9 MeSH terms, 2 funders, 64 references.

Cite

This paper

Lei, T., Scheid, M. R., Flint, R. D., Glaser, J. I., & Slutzky, M. W. (2026). Active dissociation of intracortical spiking and high gamma activity. Nature, 654(8119), 744-750. https://doi.org/10.1038/s41586-026-10331-y

BibTeX

@article{lei2026active,
author = {Lei, Tianhao and Scheid, Michael R and Flint, Robert D and Glaser, Joshua I and Slutzky, Marc W},
title = {{Active dissociation of intracortical spiking and high gamma activity}},
journal = {Nature},
year = {2026},
month = apr,
volume = {654},
number = {8119},
pages = {744--750},
publisher = {Nature Portfolio},
issn = {0028-0836},
doi = {10.1038/s41586-026-10331-y},
url = {https://doi.org/10.1038/s41586-026-10331-y},
pmid = {41922776},
pmcid = {PMC13067879}
}

RIS

TY - JOUR
AU - Lei, Tianhao
AU - Scheid, Michael R
AU - Flint, Robert D
AU - Glaser, Joshua I
AU - Slutzky, Marc W
TI - Active dissociation of intracortical spiking and high gamma activity
T2 - Nature
J2 - Nature
PY - 2026
DA - 2026/04/01
VL - 654
IS - 8119
SP - 744
EP - 750
SN - 0028-0836
PB - Nature Portfolio
DO - 10.1038/s41586-026-10331-y
UR - https://doi.org/10.1038/s41586-026-10331-y
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41586-026-10331-y",
"type": "article-journal",
"title": "Active dissociation of intracortical spiking and high gamma activity",
"container-title": "Nature",
"author": [
{
"family": "Lei",
"given": "Tianhao"
},
{
"family": "Scheid",
"given": "Michael R"
},
{
"family": "Flint",
"given": "Robert D"
},
{
"family": "Glaser",
"given": "Joshua I"
},
{
"family": "Slutzky",
"given": "Marc W"
}
],
"container-title-short": "Nature",
"volume": "654",
"issue": "8119",
"page": "744-750",
"DOI": "10.1038/s41586-026-10331-y",
"PMID": "41922776",
"PMCID": "PMC13067879",
"ISSN": "0028-0836",
"publisher": "Nature Portfolio",
"URL": "https://doi.org/10.1038/s41586-026-10331-y",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
1
]
]
}
}

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.1073/pnas.2516293123
Distinct laminar origins of sensory-evoked high-gamma and low-frequency ECoG signals revealed by optogenetics.
Journal: Proceedings of the National Academy of Sciences of the United States of America
In common: 13 references
[2] doi:10.1371/journal.pbio.3003873 [code]
Distinct sources of decision-related signals in visual cortex are represented in different local field potential bands.
Journal: PLoS biology
In common: pandas, Matplotlib, NumPy, extracellular electrophysiology (units, LFP), non-human primate, 6 references
[3] doi:10.1038/s41467-026-75455-1 [code]
Shared latent representations of speech production for cross-patient speech decoding.
Journal: Nature communications
In common: TensorFlow, h5py, scikit-learn, 4 other tools, 4 references
[4] doi:10.1007/s12021-026-09807-z [code]
Optimal Size of Electrocorticography Grids for Classification of Hand Movements.
Journal: Neuroinformatics
In common: scikit-learn, pandas, SciPy, 2 other tools, 5 references
[5] doi:10.64898/2026.03.12.710517 [code]
Cortical excitability inversely modulates fMRI connectivity via low-frequency neuronal coupling
Journal: bioRxiv (preprint)
In common: h5py, pandas, SciPy, 2 other tools, 5 references
[6] doi:10.1038/s41467-026-72444-2 [code]
Broadband synergy versus oscillatory redundancy in the visual cortex.
Journal: Nature communications
In common: SciPy, NumPy, extracellular electrophysiology (units, LFP), non-human primate, 5 references
[7] doi:10.1002/advs.202519893 [code]
NeuroSuite for Long-Term Functional and Structural Studies of Air-Liquid Interface Cerebral Organoids.
Journal: Advanced science (Weinheim, Baden-Wurttemberg, Germany)
In common: Pillow, pandas, SciPy, 2 other tools, extracellular electrophysiology (units, LFP), 3 references
[8] doi:10.1038/s41598-026-52253-9 [code]
Sequential visual stimuli increase high frequency power in the visual cortex.
Journal: Scientific reports
In common: h5py, Pillow, scikit-learn, 4 other tools, 2 references
[9] doi:10.7554/elife.103046 [code]
Dichotomy between extracellular signatures of active dendritic chemical synapses and gap junctions.
Journal: eLife
In common: Matplotlib, NumPy, extracellular electrophysiology (units, LFP), 5 references
[10] doi:10.1038/s41467-026-76109-y [code]
Assistive algorithms influence neural representations in motor brain-computer interfaces.
Journal: Nature communications
In common: scikit-learn, pandas, SciPy, 2 other tools, non-human primate, 3 references

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.