Active dissociation of intracortical spiking and high gamma activity.
The 6 matches
- [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] § Methods › Factor analysis ↔ subspace_analysis.ipynb, lines 139–170 · score 0.62 · linear regression model, concatenated spike, scored, CE HGA, spike rates, weight
- [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] § 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] § 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] § 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
- # %% [markdown]
- # # This Jupyter notebook file produces the subspace analysis results in the paper
- # %%
- import numpy as np
- import scipy
- import mat73
- import sklearn
- from utility_functions import *
- import pandas as pd
- import matplotlib.pyplot as plt
- import matplotlib.patches as Patches
- from itertools import chain
- from sklearn.preprocessing import StandardScaler
- from sklearn.decomposition import FactorAnalysis,PCA
- from sklearn.linear_model import LinearRegression
- import matplotlib.ticker as mticker
- from contrastive import CPCA
- plt.rcParams["axes.linewidth"] = 2
- plt.rcParams["axes.edgecolor"] = 'k'
- plt.rcParams['grid.alpha'] = 0
- # %% [markdown]
- # # Load the data and meta data
- # Here we need to load the entire trials. So `data_type` should be "trials"
- # %%
- data_type='trials' # this could be 'trials', 'last4s', 'first4s'.
- monkey='C' # monkey should be one of 'C', 'J' or 'M'
- CE=63 # channel name. NOTE: this is not the channel number or the channel index
- # load the grid layout of the monkey
- grid=get_grid(monkey)
- # load the spike channel number
- spike_channel=get_channel(monkey,CE)
- # load the location of the CE on the grid
- loc_x,loc_y=get_CE_loc(grid,spike_channel)
- # load the shunted electrodes for this monkey. This is the python index
- shunted_electrodes=load_shunted_electrodes(monkey,spike_channel)
- # finally, load the raw data
- raw_data=load_ONF_data(data_type, monkey, CE)
- # We only need the "learned" files.
- raw_data=raw_data[raw_data['file_types']=='learned']
- # %% [markdown]
- # Optional: Visualize the data table. This may take some time.
- # %%
- raw_data
- # %% [markdown]
- # # Pre-process the spike rate and HGA data
- #
- # 1. include only the successful trials.
- # 2. remove shunted channels
- # 3. remove NAN and empty channels
- # 4. remove channels with low firing rate
- # %%
- # Here we concatenate all the successful trials together for every file.
- spike_rate_all = list(chain.from_iterable([[d[0] for d in data] for data in raw_data['spike_rate'].to_list()]))
- HGA_all = list(chain.from_iterable([[d[0] for d in data] for data in raw_data['HGA'].to_list()]))
- # We concatenate the trial type
- trial_types_all=np.concatenate(raw_data['trial_types'].to_list())
- # We concatenate the target type
- target_types_all=np.concatenate(raw_data['target_types'].to_list())
- # We take out only the successful trials
- spike_rate_successful=[a for a,t in zip(spike_rate_all,trial_types_all) if t==1 ]
- HGA_successful=[a for a,t in zip(HGA_all,trial_types_all) if t==1]
- # concatenate the signals
- spike_rate_flat=np.concatenate(spike_rate_successful,axis=-1)
- HGA_flat=np.concatenate(HGA_successful,axis=-1)
- n_time=spike_rate_flat.shape[0]
- # save the indices of the leftover electrodes
- leftover_electrode_list=np.arange(0,spike_rate_flat.shape[0])
- print(f"Before preprocessing, signals have the shape (chan x (time x n_trials)): {spike_rate_flat.shape}")
- # remove shunted electrodes
- spike_rate_flat=np.delete(spike_rate_flat,shunted_electrodes,axis=0)
- HGA_flat=np.delete(HGA_flat,shunted_electrodes,axis=0)
- leftover_electrode_list=np.delete(leftover_electrode_list,shunted_electrodes)
- print(f"Removed the shunted electrodes, signals have the shape (chan x (time x n_trials)): {spike_rate_flat.shape}")
- # check NAN electrodes in the data
- has_nan_spike_rate=np.sum(np.isnan(spike_rate_flat),axis=1)
- has_nan_HGA=np.sum(np.isnan(HGA_flat),axis=1)
- has_nan=np.where(has_nan_spike_rate+has_nan_HGA)
- # remove the electrodes with NAN values
- spike_rate_flat=np.delete(spike_rate_flat,has_nan,axis=0)
- HGA_flat=np.delete(HGA_flat,has_nan,axis=0)
- leftover_electrode_list=np.delete(leftover_electrode_list,has_nan,axis=0)
- print(f"Removed the electrodes with NAN values, signals have the shape (chan x (time x n_trials)): {spike_rate_flat.shape}")
- # check the electrodes with constant low spike rate
- low_spike_rate=5 # spikes/s
- has_low_spike_rate=np.sum(spike_rate_flat,axis=1)<n_time*low_spike_rate/20 # 20 Hz
- has_low_spike_rate=np.where(has_low_spike_rate==spike_rate_flat.shape[-1])
- # remove the electrodes with low spike rate
- spike_rate_flat=np.delete(spike_rate_flat,has_low_spike_rate,axis=0)
- HGA_flat=np.delete(HGA_flat,has_low_spike_rate,axis=0)
- leftover_electrode_list=np.delete(leftover_electrode_list,has_low_spike_rate)
- print(f"Removed the electrodes with low spike rate, signals have the shape (chan x (time x n_trials)): {spike_rate_flat.shape}")
- # get the number of clean electrodes
- n_clean_electrodes=len(leftover_electrode_list)
- CE_index=np.where(leftover_electrode_list==spike_channel-1)[0]
- # %% [markdown]
- # ### Optional: check the correlation between CE neural siginals and velocities
- # %%
- # get the velocity
- velocity=list(chain.from_iterable(raw_data['velocity'].to_list()))
- velocity=np.concatenate([v[0] for v, t in zip(velocity,trial_types_all) if t==1.0],axis=0)
- # plot the correlation of velocity x and spike rate, velocity y and HGA
- fig,ax=plt.subplots(1,2, figsize=(5,3))
- ax[0].scatter(np.squeeze(velocity[:,0]),np.squeeze(spike_rate_flat[CE_index,:]),color='b',alpha=0.2,s=2,marker='.')
- ax[0].set_xlabel('velocity x')
- ax[0].set_ylabel('spike rate (Hz)')
- ax[1].scatter(np.squeeze(velocity[:,1]),np.squeeze(HGA_flat[CE_index,:]),color='b',alpha=0.2,s=2,marker='.')
- ax[1].set_xlabel('velocity y')
- ax[1].set_ylabel('HGA amplitude')
- # title
- plt.suptitle("Correlation between the neural signals and velocity")
- plt.tight_layout()
- plt.show()
- # %% [markdown]
- # # Comparing the first factor to CE HGA and spike rate
- # %%
- # factor analysis on the mean centered, concatenated spike rate
- n_factors=10
- # zscore the spike rate data
- spike_rate_zscore=scipy.stats.zscore(spike_rate_flat,axis=None)
- # run factor analysis
- fa=FactorAnalysis(n_components=n_factors,rotation='varimax')
- factors=fa.fit_transform(spike_rate_zscore.T)
- weights=fa.components_
- # calculate the R between the factors and CE neural signals
- R_factors_spike_rate=[]
- R_factors_HGA=[]
- for i in range(n_factors):
- # fit a linear regression model on factors and spike rate
- lr_factors_spike_rate=LinearRegression().fit(factors[:,:i+1],spike_rate_zscore[CE_index,:].T)
- # calculate the R2
- r2_factors_spike_rate=lr_factors_spike_rate.score(factors[:,:i+1],spike_rate_zscore[CE_index,:].T)
- R_factors_spike_rate.append(np.sqrt(r2_factors_spike_rate))
- # fit a linear regression model on factors and HGA
- lr_factors_HGA=LinearRegression().fit(factors[:,:i+1],HGA_flat[CE_index,:].T)
- # calculate the R2
- r2_factors_HGA=lr_factors_HGA.score(factors[:,:i+1],HGA_flat[CE_index,:].T)
- R_factors_HGA.append(np.sqrt(r2_factors_HGA))
- # %% [markdown]
- # plot R and the factor weights
- # %%
- fig,ax = plt.subplots(1,2, figsize=(7,3))
- im=plot_on_grid(ax[0],
- leftover_electrode_list,grid,
- np.abs(weights[0]),(loc_x,loc_y),
- cmap=mpb.cm.viridis
- )
- im.set_clim(0,1)
- cb=plt.colorbar(im,shrink=0.8)
- cb.set_label('First factor weights',fontsize=12)
- l1=ax[1].plot(np.arange(1,n_factors+1),R_factors_spike_rate,lw=3, label='spike rate',color='magenta')
- l2=ax[1].plot(np.arange(1,n_factors+1),R_factors_HGA,lw=3,label='HGA',color='darkcyan')
- ax[1].set_xticks(np.arange(1,n_factors+1,3))
- ax[1].set_yticks(np.arange(0,1,0.2))
- ax[1].set_xlabel('number of factors')
- ax[1].set_ylabel('R')
- ax[1].legend(title='Factors fitted to')
- plt.tight_layout()
- plt.show()
- # %% [markdown]
- # ### here we calculate the correlation between DWSS and HGA
- # %%
- # calculate the weighted grid for CE
- weighted_grid=calculate_weighted_grid((loc_x,loc_y),power=2)
- weighted_grid=get_the_weights(weighted_grid,
- leftover_electrode_list,
- grid.flatten())
- # calculate the weighted sum of spike using the weights_grid
- corr_HGA_DWSS=np.corrcoef(
- np.vstack([HGA_flat[CE_index],np.average(spike_rate_zscore,axis=0,weights=weighted_grid)]))[0,1]
- # calculate the weighted grid for 20 random electrodes
- n_random_pairs=20
- random_pairs=draw_random_pairs(n_random_pairs,except_loc=(loc_x,loc_y))
- 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]
- # calculate the weighted sum of spike using random weighted grids.
- 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]
- # calculate the DWSS and HGA on non CE electrodes
- # get the randomly selected electrodes
- random_electrodes=np.random.choice(np.delete(leftover_electrode_list,np.where(leftover_electrode_list==spike_channel-1)),n_random_pairs,replace=False)+1
- random_electrodes_loc=[np.where(grid==r) for r in random_electrodes]
- # calculate the DWSS
- random_weighted_grids=[get_the_weights(calculate_weighted_grid(r,power=2),leftover_electrode_list,grid.flatten()) for r in random_electrodes_loc]
- # calculate the correlation between DWSS and HGA on those random electrodes
- 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)]
- # %% [markdown]
- # plotting
- # %%
- fig,ax=plt.subplots(1,1,figsize=(3,3))
- im=plot_on_grid(ax,leftover_electrode_list,grid,np.sqrt(weighted_grid),(loc_x,loc_y),cmap=mpb.cm.Blues)
- im.set_clim(0,0.003)
- ax.set_title("distance weights",fontsize=10)
- ax.spines[['left','bottom','top','right']].set_linewidth(3)
- cb=fig.colorbar(im,shrink=0.8,ticks=[0,0.002],format=mticker.FixedFormatter(['0', '3']))
- cb.set_label("weights",size=10)
- cb.ax.set_yticklabels(['0', '3']) # vertically oriented colorbar
- cb.ax.tick_params(labelsize=10)
- fig,ax=plt.subplots(1,1,figsize=(3,2))
- ax.bar([0,1,2],
- np.abs(np.squeeze([
- corr_HGA_DWSS,
- np.mean(corr_HGA_random_DWSS),
- np.mean(corr_random_HGA_DWSS)]))
- ,color=['crimson','grey',"lightblue"],yerr=(0,np.std(corr_HGA_random_DWSS),np.std(corr_random_HGA_DWSS)),error_kw=dict(lw=3))
- # plot the raw data
- x = np.random.normal(1, 0.05, size=len(corr_HGA_random_DWSS)) # Jitter
- ax.scatter(x, np.array(corr_HGA_random_DWSS), color='k', alpha=0.5, edgecolor='black', linewidth=0)
- x = np.random.normal(2, 0.05, size=len(corr_random_HGA_DWSS)) # Jitter
- ax.scatter(x, np.array(corr_random_HGA_DWSS), color='k', alpha=0.5, edgecolor='black', linewidth=0)
- ax.set_xticks([0,1,2],["DWSS","random","non-CE"],rotation=0,fontsize=10)
- ax.set_ylabel("R",fontsize=10)
- ax.set_ylim(-0.2,0.8)
- ax.tick_params(axis='y', labelsize=10)
- ax.tick_params(axis='x', labelsize=10)
- ax.spines[['top','right']].set_visible(False)
- ax.spines[['left','bottom']].set_linewidth(3)
- plt.show()
- # %% [markdown]
- # # PCA and cPCA analysis on the correlation and subspace angles
- # 1. define SP+ and HG+ NAS
- # 2. perform PCA on the data separately
- # 3. perform cPCA on the data separately
- # 4. calculate the angles between them
- # %%
- # calculate the CE spike rate and HGA
- CE_spike_rate=StandardScaler().fit_transform(spike_rate_zscore[CE_index].T)
- CE_HGA=StandardScaler().fit_transform(HGA_flat[CE_index].T)
- # calculated the sp+ and hg+ indices
- NAS=np.squeeze(np.arctan2(CE_HGA, CE_spike_rate)*180/np.pi)
- sp_ind=(NAS>=-15)&(NAS<=15)
- hg_ind=(NAS>=75)&(NAS<=105)
- # %% [markdown]
- # Quick check of the dimensionality of the two data
- # %%
- n_PCs=10
- pca=PCA(n_components=n_PCs)
- plt.figure()
- plt.plot(np.cumsum(pca.fit(spike_rate_zscore[:,sp_ind].T).explained_variance_ratio_),lw=3,color='magenta')
- plt.plot(np.cumsum(pca.fit(spike_rate_zscore[:,hg_ind].T).explained_variance_ratio_),lw=3,color='darkblue')
- plt.ylabel("cumulative VAF")
- plt.xlabel("number of PCs")
- plt.legend(['SP+','HG+'])
- # %%
- # first we need to fine the best alpha for cPCA
- cpca=CPCA(n_components=n_PCs,standardize=False)
- # specify the range the step for the alphas to try
- n_alphas=15
- max_log_alpha=2
- # prepare the data
- sp_data=scipy.stats.zscore(spike_rate_zscore[:,sp_ind],axis=None)
- hg_data=scipy.stats.zscore(spike_rate_zscore[:,hg_ind],axis=None)
- # Use the plots to determin the best alpha
- projected_fg,best_alphas=cpca.fit_transform(hg_data.T,
- sp_data.T,
- n_alphas=n_alphas, max_log_alpha=max_log_alpha, alpha_selection='all',return_alphas=True)
- projected_bg=cpca.transform(sp_data.T,n_alphas=n_alphas, max_log_alpha=max_log_alpha, alpha_selection='all')
- # %% [markdown]
- # - Plotting the background and foreground data with different alpha
- # - The peak of the ratio of variances in the second plot is where the best alpha value is
- # %%
- plt.figure(figsize=[3*n_alphas+1,3])
- subsample=0.2 # only plot number of subsample x data
- all_ratio=[]
- for j, pack in enumerate(zip(projected_fg,projected_bg)):
- # retrieve and subsample the background and foreground data
- fg,bg=pack
- subsample_bg=np.random.choice(len(bg),int(len(bg)*subsample),replace=False)
- subsample_fg=np.random.choice(len(fg),int(len(fg)*subsample),replace=False)
- # calculate the variance
- fg_variance=np.var(fg)
- bg_variance=np.var(bg)
- plt.subplot(1,n_alphas+1,j+1)
- plt.scatter(bg[subsample_bg,0],bg[subsample_bg,1], color='k', alpha=0.5, label='background',s=3)
- plt.scatter(fg[subsample_fg,0],fg[subsample_fg,1], color='r', alpha=0.5, label='foreground',s=3)
- plt.title('Alpha='+str(np.round(best_alphas[j],2)))
- plt.xlabel(f"background {bg_variance:0.2f}\nforeground {fg_variance:0.2f}\nratio {fg_variance/bg_variance:0.2f}")
- all_ratio.append(fg_variance/bg_variance)
- plt.legend()
- # plot the variance ratios for each alpha we selected
- plt.figure()
- plt.plot(best_alphas,all_ratio,lw=3)
- plt.xscale("log")
- plt.yticks(np.linspace(1,19,6))
- plt.xlabel('alpha value')
- plt.ylabel('ratio of variances')
- plt.ylim(0,5)
- print(f"The best alpha value for this dataset it: {best_alphas[np.argmax(all_ratio)]}")
- plt.show()
- # %% [markdown]
- # ## Now we are using the best alphao to produce the cPCs and correlate them with CE spike rate and CE HGA
- # %%
- alpha_selected=best_alphas[np.argmax(all_ratio)]
- ######################## fg: SP+, bg: HG+ ################################
- fg_cov,bg_cov=get_cov(sp_data.T,hg_data.T)
- v_top_SP=get_cpca_loadings(fg_cov,bg_cov,alpha_selected,n_PCs)
- projected_bg_SP=hg_data.T @ v_top_SP
- projected_fg_SP=sp_data.T @ v_top_SP
- # fit the unique component one by one and see which component(s) contribute the most
- cumsum_R2_SP_CPCA_FR=[]
- cumsum_R2_SP_CPCA_HG=[]
- for i in range(n_PCs):
- R2=(LinearRegression(fit_intercept=False)
- .fit(spike_rate_zscore.T@v_top_SP[:,:i+1],np.squeeze(spike_rate_zscore[CE_index]))
- .score(spike_rate_zscore.T@v_top_SP[:,:i+1],np.squeeze(spike_rate_zscore[CE_index])))
- cumsum_R2_SP_CPCA_FR.append(np.maximum(R2,0))
- R2=(LinearRegression(fit_intercept=False)
- .fit(spike_rate_zscore.T@v_top_SP[:,:i+1],np.squeeze(HGA_flat[CE_index]))
- .score(spike_rate_zscore.T@v_top_SP[:,:i+1],np.squeeze(HGA_flat[CE_index])))
- cumsum_R2_SP_CPCA_HG.append(np.maximum(R2,0))
- ######################### fg: SP+, bg: HG+ ################################
- fg_cov,bg_cov=get_cov(hg_data.T,sp_data.T)
- v_top_HG=get_cpca_loadings(fg_cov,bg_cov,alpha_selected,n_PCs)
- projected_bg_HG=sp_data.T @ v_top_HG
- projected_fg_HG=hg_data.T @ v_top_HG
- # fit the unique component one by one and see which component(s) contribute the most
- cumsum_R2_HG_CPCA_FR=[]
- cumsum_R2_HG_CPCA_HG=[]
- for i in range(n_PCs):
- R2=(LinearRegression(fit_intercept=False)
- .fit(spike_rate_zscore.T@v_top_HG[:,:i+1],np.squeeze(spike_rate_zscore[CE_index,:]))
- .score(spike_rate_zscore.T@v_top_HG[:,:i+1],np.squeeze(spike_rate_zscore[CE_index,:])))
- cumsum_R2_HG_CPCA_FR.append(np.maximum(R2,0))
- R2=(LinearRegression(fit_intercept=False)
- .fit(spike_rate_zscore.T@v_top_HG[:,:i+1],np.squeeze(HGA_flat[CE_index,:]))
- .score(spike_rate_zscore.T@v_top_HG[:,:i+1],np.squeeze(HGA_flat[CE_index,:])))
- cumsum_R2_HG_CPCA_HG.append(np.maximum(R2,0))
- # %% [markdown]
- # Now we perform PCA on SP+ data and HG+ data separately
- # %%
- # same analysis using PCA (this equals to fg with alpha=0)
- pca_projection_HG=PCA(n_PCs).fit_transform(hg_data.T)
- pca_projection_SP=PCA(n_PCs).fit_transform(sp_data.T)
- pca_loading_HG=PCA(n_PCs).fit(hg_data.T).components_.T
- pca_loading_SP=PCA(n_PCs).fit(sp_data.T).components_.T
- shuffle_pca_loading_HG=np.copy(pca_loading_HG)
- shuffle_pca_loading_SP=np.copy(pca_loading_SP)
- np.random.shuffle(shuffle_pca_loading_HG)
- np.random.shuffle(shuffle_pca_loading_SP)
- # %% [markdown]
- # Prepare the shuffled dataset as control
- # %%
- sp_fr_shuffle = np.copy(sp_data)
- np.random.shuffle(sp_fr_shuffle)
- hg_fr_shuffle = np.copy(hg_data)
- np.random.shuffle(hg_fr_shuffle)
- fg_cov_shuffle,bg_cov_shuffle=get_cov(sp_fr_shuffle.T,hg_fr_shuffle.T)
- v_top_SP_shuffle=get_cpca_loadings(fg_cov_shuffle,bg_cov_shuffle,alpha_selected,n_PCs)
- np.random.shuffle(sp_fr_shuffle)
- np.random.shuffle(hg_fr_shuffle)
- bg_cov_shuffle,fg_cov_shuffle=get_cov(sp_fr_shuffle.T,hg_fr_shuffle.T)
- v_top_HG_shuffle=get_cpca_loadings(fg_cov_shuffle,bg_cov_shuffle,alpha_selected,n_PCs)
- # %% [markdown]
- # Calculate the subspace angles between different PCs and cPCs
- # %%
- subspace_angles_cPCs=scipy.linalg.subspace_angles(v_top_HG, v_top_SP)
- subspace_angles_cPCs_shuffle=scipy.linalg.subspace_angles(v_top_HG_shuffle, v_top_SP_shuffle)
- subspace_angles_PCs=scipy.linalg.subspace_angles(pca_loading_HG, pca_loading_SP)
- subspace_angles_PCs_shuffle=scipy.linalg.subspace_angles(shuffle_pca_loading_HG, shuffle_pca_loading_SP)
- subspace_angles_cPCA_PCA_HG=scipy.linalg.subspace_angles(v_top_HG, pca_loading_HG)
- subspace_angles_cPCA_PCA_SP=scipy.linalg.subspace_angles(v_top_SP, pca_loading_SP)
- subspace_angles_shuffle_cPCA_PCA_HG=scipy.linalg.subspace_angles(v_top_HG_shuffle, shuffle_pca_loading_HG)
- subspace_angles_shuffle_cPCA_PCA_SP=scipy.linalg.subspace_angles(v_top_SP_shuffle, shuffle_pca_loading_SP)
- # %% [markdown]
- # Here we demostrated the correlation (R) between the spike rate, HGA and SP+/HG+ cPCs
- # %%
- fig,ax=plt.subplots(1,1,figsize=(4,3))
- ax.plot(np.linspace(1,3*n_PCs,n_PCs),np.sqrt(cumsum_R2_HG_CPCA_HG),color='tab:blue',lw=3)
- ax.plot(np.linspace(1,3*n_PCs,n_PCs),np.sqrt(cumsum_R2_HG_CPCA_FR),color='grey',lw=3)
- ax.set_xticks(np.linspace(2,3*n_PCs+0.5,5),np.int16(np.linspace(1,n_PCs,5)),fontsize=14)
- ax.set_yticks(np.linspace(0,1,3),np.linspace(0,1,3),fontsize=14)
- ax.legend(["HGA","Spike rate"],frameon=False,loc="upper right",title="HG+ cPCs fitted to",title_fontsize=14,fontsize=10)
- ax.set_xlabel("num of HG+ cPCs added",fontsize=14)
- ax.set_ylabel("R",fontsize=14)
- ax.spines['top'].set_visible(False)
- ax.spines['right'].set_visible(False)
- plt.tight_layout()
- fig,ax=plt.subplots(1,1,figsize=(4,3))
- ax.plot(np.linspace(1,3*n_PCs,n_PCs),np.sqrt(cumsum_R2_SP_CPCA_HG),color='grey',lw=2)
- ax.plot(np.linspace(1,3*n_PCs,n_PCs),np.sqrt(cumsum_R2_SP_CPCA_FR),color='crimson',lw=2)
- ax.set_xticks(np.linspace(2,3*n_PCs+0.5,5),np.int16(np.linspace(1,n_PCs,5)),fontsize=14)
- ax.set_yticks(np.linspace(0,1,3),np.linspace(0,1,3),fontsize=15)
- ax.legend(["HGA","Spike rate"],frameon=False,loc="best",title="SP+ cPCs fitted to",title_fontsize=14,fontsize=10)
- ax.set_xlabel("num of SP+ cPCs added",fontsize=14)
- ax.set_ylabel("R",fontsize=14)
- ax.spines['top'].set_visible(False)
- ax.spines['right'].set_visible(False)
- plt.tight_layout()
- plt.show()
- # %% [markdown]
- # Here we demonstrate the cPCA weights and that HG+ cPCs have more activation in other electrodes, compared to SP+ cPCs
- # %%
- ### another figure: plot the first cPC on the grid
- first_cPC_weights_SP=np.abs(v_top_SP[:,0])/np.abs(v_top_SP[:,0]).max()
- first_cPC_weights_HG=np.abs(v_top_HG[:,0])/np.abs(v_top_HG[:,0]).max()
- fig,ax=plt.subplots(1,2,layout='constrained',figsize=(7,4))
- im1=plot_on_grid(ax[0],leftover_electrode_list,grid,first_cPC_weights_SP,(loc_x,loc_y),
- cmap=mpb.cm.viridis,show_chan_indx=False,CE_color='r')
- im2=plot_on_grid(ax[1],leftover_electrode_list,grid,first_cPC_weights_HG,(loc_x,loc_y),
- cmap=mpb.cm.viridis,show_chan_indx=False,CE_color='r')
- #clim_min=np.min([v_top_SP[:,0],v_top_HG[:,0]])
- clim=np.max([first_cPC_weights_SP,first_cPC_weights_HG])
- #clim=np.maximum(clim_max,np.abs(clim_min))
- im1.set_clim([0,clim-0.3])
- im2.set_clim([0,clim-0.1])
- cb=fig.colorbar(im2, ax=ax[:], location='right',shrink=0.6,pad=0.1)
- cb.set_label("cPC weights",fontsize=12)
- cb.ax.tick_params(labelsize=12)
- ax[0].set_title("SP+ 1st cPC",fontsize=12)
- ax[1].set_title("HG+ 1st cPC",fontsize=12)
- ### some quantification: save the first cPC weights
- fig,ax=plt.subplots(1,1,figsize=(4,4))
- ax.violinplot([first_cPC_weights_SP,first_cPC_weights_HG])
- plt.show()
- # %% [markdown]
- # ---
- # Code developed by: [Tianhao Lei](https://github.com/caraido)
subspace_analysis.ipynb at commit 8ca016b, under CC-BY-4.0 · at the source
Overview
- Department of Neurology, Northwestern University Feinberg School of Medicine, Chicago, IL USA
- Department of Computer Science, Northwestern University, Evanston, IL USA
- National Institute for Theory and Mathematics in Biology, Chicago, IL USA
- Department of Neuroscience, Northwestern University Feinberg School of Medicine, Chicago, IL USA
- Department of Physical Medicine and Rehabilitation, Northwestern University Feinberg School of Medicine, Chicago, IL USA
- Department of Biomedical Engineering, Northwestern University, Evanston, IL USA
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
f0d3c330009cfdc76675f95859e70dc96adc3689, 7 October 2025Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
21 files
- build/
lib/ , Python, 458 linescontrastive/ __init__.py - contrastive/
__init__.py , Python, 457 lines, 1 match - experiments/
.ipynb_checkpoints/ , Jupyter, 241 linesComparing cPCA to other Dimensionality Reduction Techniques -checkpoint.ipynb - experiments/
.ipynb_checkpoints/ , Jupyter, 373 linesComparing cPCA to other Techniques (Figure S2)-checkpoint.ipynb - experiments/
.ipynb_checkpoints/ , Jupyter, 267 linesComparing cPCA to other Techniques-checkpoint.ip ynb - experiments/
.ipynb_checkpoints/ , Jupyter, 44 linesIMU Sensors (Figure 6)-checkpoint.ipynb - experiments/
.ipynb_checkpoints/ , Jupyter, 176 linesMNIST Corrupted by Natural Background (Figures 1, 5)-checkpoint.ipynb - experiments/
.ipynb_checkpoints/ , Jupyter, 57 linesMice Protein (Figure 2)-checkpoint.ipynb - experiments/
.ipynb_checkpoints/ , Jupyter, 188 linesSingle-Cell RNA-seq (Figure 3)-checkpoint.ipynb - experiments/
Comparing cPCA to other Techniques (Figure S2).ipynb , Jupyter, 370 lines - experiments/
Comparing cPCA to other Techniques.ipynb , Jupyter, 267 lines - experiments/
IMU Sensors (Figure 6).ipynb , Jupyter, 56 lines - experiments/
MNIST Corrupted by Natural Background (Figures 1, 5).ipynb , Jupyter, 176 lines - experiments/
Mice Protein (Figure 2).ipynb , Jupyter, 57 lines - experiments/
Single-Cell RNA-seq (Figure 3).ipynb , Jupyter, 188 lines - experiments/
pursuit.py , Python, 94 lines - experiments/
supervised.py , Python, 264 lines - experiments/
utils.py , Python, 270 lines - setup.py, Python, 21 lines
- LICENSE.md, License, 31 lines
- README.rst, Text, 187 lines
caraido/spike-highgamma
8ca016b16e8b0452bec726fab1c0b118a77895dc, 4 May 2026Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
6 files
- STA_analysis.ipynb, Jupyter, 349 lines
- behavior_analysis.ipynb, Jupyter, 265 lines, 1 match
- neural_signal_analysis.i
pynb , Jupyter, 320 lines, 1 match - subspace_analysis.ipynb, Jupyter, 515 lines, 3 matches
- utility_functions.py, Python, 291 lines
- README.md, Text, 177 lines
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:
- it points to the authors' code: abidlabs/
contrastive , caraido/spike-highgamma
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
- figshare:31288654, at figshare; found in “Data availability”
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:
- it points to a dataset: figshare 31288654
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://
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/
url = {https://
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/
VL - 654
IS - 8119
SP - 744
EP - 750
SN - 0028-0836
PB - Nature Portfolio
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"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":
"volume": "654",
"issue": "8119",
"page": "744-750",
"DOI": "10.1038/
"PMID": "41922776",
"PMCID": "PMC13067879",
"ISSN": "0028-0836",
"publisher": "Nature Portfolio",
"URL": "https://
"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 AmericaIn 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 biologyIn 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 communicationsIn 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: NeuroinformaticsIn 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 couplingJournal: 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 communicationsIn 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 reportsIn 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: eLifeIn 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 communicationsIn 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.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 24 scripts, and 6 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:1b2a49e90b176351…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
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.
