Sensorimotor remapping drives task specialization in prefrontal cortex.
The 21 matches
- [1] § Results › mPFC neurons are most involved in the representation of variables in their preferred task ↔ Response-switch/Pseudo_pop/regression.ipynb, lines 762–793 · score 0.70 · post stimulus activity, pre stimulus, task variables, task selective, motor, trained
- [2] § Methods › Ridge regression › cvR2 scores and statistics ↔ Response-switch/Individuals/regression.ipynb, lines 68–212 · score 0.68 · cvR2, cross validation, variance explained, permutation, pipeline, splitting
- [3] § Methods › Ridge regression › cvR2 scores and statistics ↔ Response-switch/Pseudo_pop/regression.ipynb, lines 102–243 · score 0.68 · cvR2, cross validation, variance explained, permutation, pipeline, splitting
- [4] § Results › Population modularity can be explained by an interaction between task signal and global inhibition ↔ Simulation/model_response_switch.ipynb, lines 207–285 · score 0.65 · activation function, projection weight, standard deviations, task signal, networks, modulation
- [5] § Methods › Subpopulations and Gaussian mixture › Gaussian mixture ↔ Response-switch/Pseudo_pop/gaussian_mixture.ipynb, lines 128–148 · score 0.65 · BayesianGaussianMixture, cross validated, split, GM, components, scores
- [6] § Methods › Subpopulations and Gaussian mixture › Gaussian mixture ↔ Rule-switch/Pseudo_pop/gaussian_mixture_rs.ipynb, lines 134–154 · score 0.65 · BayesianGaussianMixture, cross validated, split, GM, components, scores
- [7] § Results › Population modularity can be explained by an interaction between task signal and global inhibition ↔ Simulation/model_rule_switch.ipynb, lines 192–277 · score 0.65 · activation function, projection weight, standard deviations, task signal, networks, Rule switch
- [8] § Results › Task selectivity delineates three neural subpopulations with distinct functional roles ↔ Response-switch/Pseudo_pop/gaussian_mixture.ipynb, lines 654–689 · score 0.63 · population LR, Gaussian Mixture, Population GNG, NoGo, GM
- [9] § Methods › Analyses of neural selectivity ↔ Response-switch/Pseudo_pop/regression.ipynb, lines 762–793 · score 0.62 · pre stimulus, post stimulus, task selectivity, interaction, Lick, trained
- [10] § Methods › Dimension reduction ↔ Response-switch/Pseudo_pop/PCA.ipynb, lines 226–279 · score 0.62 · cross validated, variance explained, favored, cvPCA, covariance, trained
- [11] § Methods › Recurrent neural network modeling › Model performance on each dual-task paradigm ↔ Simulation/model_response_switch.ipynb, lines 1188–1208 · score 0.62 · direction readout, Lick readout, perceptron, spout, simulated, response switch
- [12] § Results › Population modularity can be explained by an interaction between task signal and global inhibition ↔ Simulation/model_response_switch.ipynb, lines 207–285 · score 0.60 · activation functions, projections weights, task signal, networks, modulated, model
- [13] § Results › Task selectivity delineates three neural subpopulations with distinct functional roles ↔ Response-switch/Pseudo_pop/gaussian_mixture.ipynb, lines 809–921 · score 0.60 · Support Vector Classifiers, classifiers trained, Gaussian mixture, SVCs, decoding, GNG
- [14] § Results › Population modularity can be explained by an interaction between task signal and global inhibition ↔ Simulation/model_rule_switch.ipynb, lines 192–277 · score 0.59 · activation functions, projections weights, task signal, networks, modulated, model
- [15] § Methods › Ridge regression › Regressor matrix construction ↔ Response-switch/Individuals/regression.ipynb, lines 68–212 · score 0.58 · standard scaler, cross validation, fold, predict, R2, regressor
- [16] § Methods › Ridge regression › Regressor matrix construction ↔ Response-switch/Pseudo_pop/regression.ipynb, lines 102–243 · score 0.58 · standard scaler, cross validation, fold, predict, R2, regressor
- [17] § Methods › Ridge regression › Regressor matrix construction ↔ Response-switch/Pseudo_pop/regression.ipynb, lines 433–467 · score 0.55 · Akaike Information Criterion, AIC, split, regressors, scores, model
- [18] § Methods › Pseudo-population ↔ Response-switch/Pseudo_pop/gaussian_mixture.ipynb, lines 217–245 · score 0.53 · conservative criterion, Gaussian mixture, pseudo, Response switch, neurons, population
- [19] § Methods › Analyses of neural selectivity ↔ Response-switch/Pseudo_pop/gaussian_mixture.ipynb, lines 548–569 · score 0.52 · Lick LR, absolute selectivity, variables, regression
- [20] § Results › Modularity of mPFC population decreases in a paradigm with less sensorimotor remapping ↔ Rule-switch/Pseudo_pop/gaussian_mixture_rs.ipynb, lines 579–668 · score 0.51 · Support vector classifiers, Gaussian mixture, SVCs, decoding, splits, trained
- [21] § Results › Modularity of mPFC population decreases in a paradigm with less sensorimotor remapping ↔ Response-switch/Pseudo_pop/gaussian_mixture.ipynb, lines 809–921 · score 0.51 · Support vector classifiers, Gaussian mixture, SVCs, decoding, splits, trained
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,366 lines · 45 KB · no license · 6 matches
- # %%
- import numpy as np
- import pandas as pd
- import pickle
- import matplotlib.pyplot as plt
- from tqdm.auto import tqdm
- import matplotlib as mpl
- from matplotlib.patches import Ellipse
- from matplotlib.lines import Line2D
- import matplotlib.transforms as transforms
- import seaborn as sns
- from scipy.interpolate import make_interp_spline, BSpline, splrep, splev
- from scipy.stats import mannwhitneyu, wilcoxon
- plt.style.use('seaborn-v0_8-ticks')
- mpl_config = pd.read_csv('../../mpl_config.csv').to_dict(orient='records')[0]
- mpl.rcParams.update(mpl_config)
- from pathlib import Path
- import sys
- ROOT = Path.cwd().parents[1] # go up 2 levels
- sys.path.insert(0, str(ROOT))
- from export_source import export_plot_data
- # %%
- def standardize(M) :
- """ Standardization by column for 2D matrices """
- M_copy = np.copy(M)
- M_copy = M_copy.astype(float)
- for j in range(np.shape(M)[1]) :
- if np.std(M[:,j]) != 0 :
- M_copy[:,j] = (M[:,j].astype(float) - float(np.mean(M[:,j])))/float(np.std(M[:,j]))
- else :
- M_copy[:,j] = M[:,j].astype(float) - float(np.mean(M[:,j]))
- return M_copy
- def confidence_ellipse(x, y, ax, n_std=2.0, edgecolor = 'black',facecolor='none', **kwargs):
- """
- Create a plot of the covariance confidence ellipse of `x` and `y`
- """
- if x.size != y.size:
- raise ValueError("x and y must be the same size")
- cov = np.cov(x, y)
- pearson = cov[0, 1]/np.sqrt(cov[0, 0] * cov[1, 1])
- # Using a special case to obtain the eigenvalues of this
- # two-dimensionl dataset.
- ell_radius_x = np.sqrt(1 + pearson)
- ell_radius_y = np.sqrt(1 - pearson)
- ellipse = Ellipse((0, 0),
- width=ell_radius_x * 2,
- height=ell_radius_y * 2,
- facecolor=facecolor,
- edgecolor=edgecolor,
- **kwargs)
- # Calculating the stdandard deviation of x from
- # the squareroot of the variance and multiplying
- # with the given number of standard deviations.
- scale_x = np.sqrt(cov[0, 0]) * n_std
- mean_x = np.mean(x)
- # calculating the stdandard deviation of y ...
- scale_y = np.sqrt(cov[1, 1]) * n_std
- mean_y = np.mean(y)
- transf = transforms.Affine2D() \
- .rotate_deg(45) \
- .scale(scale_x, scale_y) \
- .translate(mean_x, mean_y)
- ellipse.set_transform(transf + ax.transData)
- return ax.add_patch(ellipse)
- def smooth(x,y,nb_point) :
- """Smooth a (x,y) serie by inserting nb_point with spline interpolation"""
- x_smooth = np.linspace(np.min(x), np.max(x), nb_point)
- y_smooth = make_interp_spline(x, y, k=3)(x_smooth)
- return x_smooth, y_smooth
- # %%
- ## We load here the selected ridge and PCA models
- with open('Models/ridge.pickle', 'rb') as f:
- ridge_output = pickle.load(f)
- with open('Models/pca_notime.pickle', 'rb') as f:
- pca1 = pickle.load(f)
- with open('../DATA/Dataframes/df_pseudo.pickle', 'rb') as f:
- merged_data = pickle.load(f)
- res = ridge_output['Models']
- reg_scores = ridge_output['Scores']
- # %% [markdown]
- # ### Selectivity matrix construction
- # %%
- plt.figure(figsize=(7,7))
- m = res[np.argmax(reg_scores)]
- S = m['Ridge'].coef_.T
- ax = plt.gca()
- plt.scatter(S[0,:],S[5,:],color='grey', s = 4)
- ax.spines['left'].set_position('center')
- ax.spines['bottom'].set_position('center')
- ax.spines['left'].set_linewidth(1.4)
- ax.spines['bottom'].set_linewidth(1.4)
- ax.spines['right'].set_color('none')
- ax.spines['top'].set_color('none')
- plt.xticks([])
- plt.yticks([])
- # %% [markdown]
- # ### Components estimation
- # %%
- from sklearn.model_selection import KFold, GridSearchCV, RepeatedKFold, cross_validate
- from sklearn.mixture import GaussianMixture, BayesianGaussianMixture
- from tqdm.auto import tqdm
- def gm_components_estimation(S_mat, n_repeat=50) :
- """Determine the number of components to keep in Gaussian mixture through a 2-fold cross-validation procedure (50 reps)"""
- gm_scores = []
- rkf = RepeatedKFold(n_splits = 2, n_repeats = n_repeat)
- for n_comp in tqdm(range(1,6)) :
- gm = BayesianGaussianMixture(n_components=n_comp ,n_init = 5, max_iter=100)
- cv_scores = cross_validate(gm,S_mat.T,cv=rkf)
- gm_scores.append(cv_scores['test_score'])
- return np.array(gm_scores)
- # %%
- import warnings
- warnings.filterwarnings("ignore")
- gm_scores_full = gm_components_estimation(S)
- diff_gm_scores = np.diff(gm_scores_full,axis=0) ## Computing relative increase in model performance after the addition of each component
- # %%
- import matplotlib as mpl
- plt.figure(figsize=(8,3))
- yerr = np.abs(np.mean(diff_gm_scores,axis=1) - np.percentile(diff_gm_scores,(5,95),axis=1))
- _,caps,_ = plt.errorbar([2,3,4,5],np.mean(diff_gm_scores,axis=1),yerr=yerr,linewidth=3,capsize=5,elinewidth=2.5,color='#323232')
- for cap in caps:
- cap.set_color('#323232')
- cap.set_markeredgewidth(2)
- plt.scatter([2,3,4,5],np.mean(diff_gm_scores,axis=1),marker = 's',s=150, color='#323232')
- #plt.scatter([2,3,4,5],np.percentile(np.diff(gm_scores_full,axis=0),5,axis=1),marker='s', s=120,color='#276690')
- plt.fill_between([1.9,3,4,5.1],-0.2,0, color='black',alpha=0.05)
- plt.axhline(0,color="black",linestyle='--',alpha=0.2)
- plt.xlabel('# Subpopulations',fontsize=30)
- plt.xlim((1.9,5.1))
- plt.xticks([2,3,4,5],fontsize=28)
- plt.ylabel('LL increase \n in model fitting',fontsize=30)
- #plt.ylim((-0.15,0.4))
- plt.ylim((-0.1,0.6))
- #plt.yticks([-0.05,0,0.05,0.1])
- plt.yticks([0,0.2,0.4,0.6],fontsize=28)
- #plt.savefig('Plots/SVG/gm_comp.SVG', dpi = 300,bbox_inches='tight')
- #plt.savefig('Plots/PNG/gm_comp.PNG', dpi = 300,bbox_inches='tight')
- # %%
- # Store data (serialize)
- with open('Models/gm_components.pickle', 'wb') as handle:
- pickle.dump(np.diff(gm_scores_full,axis=0) , handle, protocol=pickle.HIGHEST_PROTOCOL)
- # %%
- with open('Models/gm_components.pickle', 'rb') as f:
- diff_gm_scores = pickle.load(f)
- # %%
- export_plot_data('../../Source_Data.xlsx','Figure4c',LL_increase=diff_gm_scores)
- # %% [markdown]
- # ### GM model
- # %%
- from sklearn.mixture import GaussianMixture, BayesianGaussianMixture
- gm = BayesianGaussianMixture(n_components = 3,n_init = 20, max_iter=100) ## Keeping 3 components (see above)
- gm.fit(S.T)
- prob = gm.predict_proba(S.T)
- # %% [markdown]
- # ### Define population
- # %%
- pop1 = prob[:,0] >= 0.9 #Conservative criterion to associate each neuron with a GM component.
- pop2 = prob[:,1] >= 0.9 #A neuron is associated to a population if the weight associated to it is more than 90%
- pop3 = prob[:,2] >= 0.9
- pops = [pop1,pop2,pop3]
- S_pop = [S[:,pop1],S[:,pop2],S[:,pop3]]
- mean_ctx_s = [np.mean(S_pop[0][5,:]),np.mean(S_pop[1][5,:]),np.mean(S_pop[2][5,:])]
- print(mean_ctx_s)
- S_B = S_pop[np.argsort(mean_ctx_s)[2]]
- popB = pops[np.argsort(mean_ctx_s)[2]]
- S_A = S_pop[np.argsort(mean_ctx_s)[0]]
- popA = pops[np.argsort(mean_ctx_s)[0]]
- S_0 = S_pop[np.argsort(mean_ctx_s)[1]]
- pop0 = pops[np.argsort(mean_ctx_s)[1]]
- print(len(S.T))
- print(np.sum(popA),np.sum(popA)/len(S.T))
- print(np.sum(popB),np.sum(popB)/len(S.T))
- print(np.sum(pop0),np.sum(pop0)/len(S.T))
- # %%
- with open('../DATA/Dataframes/df_full.pickle', 'rb') as f:
- data_full = pickle.load(f)
- files = ['M12','M13','M14','M15','M16','M17','M18','M19','M20']
- neuron_per_animal = []
- for file in files :
- neuron_per_animal.append(len(data_full[file]['Spike rate'][0]))
- animal_slice = np.insert(np.cumsum(neuron_per_animal), 0, 0, axis=0)
- popA_per_animal = [np.sum(popA[animal_slice[i]:animal_slice[i+1]]) for i in range(len(animal_slice)-1)]
- popB_per_animal = [np.sum(popB[animal_slice[i]:animal_slice[i+1]]) for i in range(len(animal_slice)-1)]
- pop0_per_animal = [np.sum(pop0[animal_slice[i]:animal_slice[i+1]]) for i in range(len(animal_slice)-1)]
- def plot_composition_bars(list1, list2, list3, labels=("Population GNG", "Population LR", "Population 0")):
- list1 = np.array(list1)
- list2 = np.array(list2)
- list3 = np.array(list3)
- n = len(list1)
- x = np.arange(n)
- plt.figure(figsize=(10, 7))
- # Bottom segment
- plt.bar(x, list1, label=labels[0],color='#2E548A')
- # Middle segment
- plt.bar(x, list2, bottom=list1, label=labels[1],color='#E63946')
- # Top segment
- plt.bar(x, list3, bottom=list1 + list2, label=labels[2],color='#5F8162')
- plt.xticks(range(len(files)),files)
- plt.ylabel("# Neurons")
- plt.ylim(0,400)
- plt.legend()
- plt.tight_layout()
- plot_composition_bars(popA_per_animal,popB_per_animal,pop0_per_animal)
- #plt.savefig('Plots/SVG/pop_repartition_resp.SVG', dpi = 300,bbox_inches='tight')
- #plt.savefig('Plots/PNG/pop_repartition_resp.PNG', dpi = 300,bbox_inches='tight')
- # %%
- from scipy.stats import binomtest
- from statsmodels.stats.multitest import multipletests
- pop_table = np.stack((popA_per_animal,popB_per_animal,pop0_per_animal)).T
- def binomial_enrichment_pmatrix(contingency, method = "bonferroni"):
- """
- Binomial enrichment test for all (sample, category) pairs.
- Parameters
- ----------
- contingency : np.ndarray
- 2D array with shape (n_samples, n_categories)
- method : str
- Multiple testing correction method (default: FDR Benjamini–Hochberg)
- Returns
- -------
- np.ndarray
- 2D array of corrected p-values with same shape as contingency
- """
- contingency = np.asarray(contingency, dtype=int)
- T = contingency.sum()
- row_totals = contingency.sum(axis=1) # samples
- col_totals = contingency.sum(axis=0) # categories
- n_samples, n_categories = contingency.shape
- pvals = np.ones((n_samples, n_categories))
- for i in range(n_samples):
- p0 = row_totals[i] / T if T > 0 else 0.0
- for j in range(n_categories):
- k = contingency[i, j]
- n = col_totals[j]
- if n > 0 and p0 > 0:
- pvals[i, j] = binomtest(
- k, int(n), p=0.5, alternative="greater"
- ).pvalue
- else:
- pvals[i, j] = 1.0
- # multiple testing correction
- pvals_corr = multipletests(
- pvals.ravel(), method=method
- )[1].reshape(pvals.shape)
- return pvals_corr
- binomial_enrichment_pmatrix(pop_table)
- # %%
- def permutation_global_driver_test(
- counts,
- threshold = 0.5,
- n_permutations = 10000,
- random_state = None):
- """
- Global permutation test for whether at least one category
- is mainly driven by one sample.
- Parameters
- ----------
- counts : np.ndarray
- Contingency table of shape (n_samples, n_categories)
- threshold : float
- Dominance threshold (default: 0.5)
- n_permutations : int
- Number of permutations
- random_state : int or None
- Random seed
- Returns
- -------
- float
- Global p-value
- """
- rng = np.random.default_rng(random_state)
- counts = np.asarray(counts, dtype=int)
- n_samples, n_categories = counts.shape
- col_totals = counts.sum(axis=0)
- grand_total = counts.sum()
- # global sample proportions (null model)
- sample_probs = counts.sum(axis=1) / grand_total
- # observed global statistic
- with np.errstate(divide="ignore", invalid="ignore"):
- observed_shares = counts / col_totals
- observed_shares[:, col_totals == 0] = 0.0
- T_obs = observed_shares.max()
- # early exit: nothing exceeds the threshold
- if T_obs <= threshold:
- return 1.0
- exceed_count = 0
- for _ in range(n_permutations):
- T_perm = 0.0
- for j in range(n_categories):
- n_j = col_totals[j]
- if n_j == 0:
- continue
- # multinomial draw under null
- perm_counts = rng.multinomial(n_j, sample_probs)
- T_perm = max(T_perm, perm_counts.max() / n_j)
- # early stopping for speed
- if T_perm >= T_obs:
- break
- if T_perm >= T_obs:
- exceed_count += 1
- # +1 correction for unbiased p-value
- p_value = (exceed_count + 1) / (n_permutations + 1)
- return p_value
- permutation_global_driver_test(pop_table)
- # %% [markdown]
- # ### Plots of the cloud of dots
- # %%
- plt.figure(figsize=(6,6))
- plt.xlim((-1.2,1.2))
- plt.ylim((-1.2,1.2))
- ax = plt.gca()
- mode = 'center'
- if mode == 'center' :
- ax.spines['left'].set_position('center')
- ax.spines['bottom'].set_position('center')
- ax.spines['left'].set_linewidth(1.4)
- ax.spines['bottom'].set_linewidth(1.4)
- ax.spines['right'].set_color('none')
- ax.spines['top'].set_color('none')
- plt.xticks([])
- plt.yticks([])
- elif mode == 'normal' :
- ax.spines[['top','right']].set_visible(False)
- plt.yticks([-1,0,1],fontsize=25)
- plt.xticks([-1,0,1],fontsize=25)
- ax.set_xlabel(r'$\beta_{Category G/NG}$')
- ax.set_ylabel(r'$\beta_{Task}$')
- plt.scatter(S_0[0,:],S_0[5,:], color = 'green', s = 8, alpha = 0.4,label='Population 0'\
- ,edgecolors='#EEEEEE',lw=0.7,antialiased=True, zorder=3)
- plt.scatter(S_B[0,:],S_B[5,:], color = 'red', s = 20, alpha = 1,label='Population LR'\
- ,edgecolors='#EEEEEE',lw=0.7,antialiased=True, zorder=3)
- plt.scatter(S_A[0,:],S_A[5,:], color = 'blue', s = 20, alpha = 1,label='Population GNG'\
- ,edgecolors='#EEEEEE',lw=0.7,antialiased=True, zorder=3)
- #plt.legend(frameon=False)
- confidence_ellipse(S_B[0,:],S_B[5,:],ax, edgecolor='red', n_std=2, alpha = 0.5,linewidth=2)
- confidence_ellipse(S_A[0,:],S_A[5,:],ax, edgecolor='blue', n_std=2, alpha = 0.5,linewidth=2)
- plt.savefig('Plots/SVG/gm_catGNG_task_' + mode + '.SVG', dpi = 300,bbox_inches='tight')
- plt.savefig('Plots/PNG/gm_catGNG_task_' + mode + '.PNG', dpi = 300,bbox_inches='tight')
- # %% [markdown]
- # #### 3D
- # %%
- pv.set_jupyter_backend('client')
- p = pv.Plotter(window_size = (800,600))
- p.set_background('white')
- p.subplot(0,0)
- #p.set_position(np.mean(S[GNG_trials,:3],axis=0))
- cord = [2,3,5]
- #labels = dict(ztitle=r'$\beta_{Task}$', xtitle=r'$\beta_{Category GNG}$', ytitle=r'$\beta_{Choice GNG}$')
- #----- This block generates the sphere for each trial label and render them in the 3D plot----
- radius = 0.01
- n_perpop = 400
- popGNG_spheres = [pv.Sphere(center = S[:,popA][cord,i], radius = radius) for i in range(n_perpop)]
- popLR_spheres = [pv.Sphere(center = S[:,popB][cord,i], radius = radius) for i in range(n_perpop)]
- pop0_spheres = [pv.Sphere(center = S[:,pop0][cord,i], radius = radius) for i in range(n_perpop)]
- for sphere in popGNG_spheres :
- p.add_mesh(sphere, color='blue', opacity = 0.4, lighting=False)
- for sphere in popLR_spheres :
- p.add_mesh(sphere, color='red', opacity = 0.4, lighting=False)
- for sphere in pop0_spheres :
- p.add_mesh(sphere, color='green', opacity = 0.4, lighting=False)
- torus_GNG = pv.ParametricTorus(ringradius=2*np.std(S[:,popA][cord[:2],:n_perpop]), crosssectionradius=5*10**(-3),center=np.mean(S[:,popA][cord,:n_perpop],axis=1))
- p.add_mesh(torus_GNG, color='blue', lighting=False)
- torus_LR = pv.ParametricTorus(ringradius=2*np.std(S[:,popB][cord[:2],:n_perpop]), crosssectionradius=5*10**(-3),center=np.mean(S[:,popB][cord,:n_perpop],axis=1))
- p.add_mesh(torus_LR, color='red', lighting=False)
- torus_0 = pv.ParametricTorus(ringradius=2*np.std(S[:,pop0][cord[:2],:n_perpop]), crosssectionradius=5*10**(-3),center=np.mean(S[:,pop0][cord,:n_perpop],axis=1))
- p.add_mesh(torus_0, color='green', lighting=False)
- #p.set_position(np.array([40,0,0]))
- #p.show_grid(**labels)
- p.show()
- # %% [markdown]
- # ### Violin
- # %%
- ## Building a dataframe with data to use seaborn module for prettier plots
- plot_labels=['Category GNG', 'Choice GNG','Category LR', 'Choice LR', 'Lick LR', 'Task']
- s_todf = []
- reg_todf = []
- cluster_todf = []
- for k in range(np.shape(S)[1]) :
- s_todf.append(S[:6,k])
- reg_todf.append(plot_labels)
- if popA[k] :
- cluster_todf.append(['A']*(np.shape(S)[0] - 1))
- elif popB[k]:
- cluster_todf.append(['B']*(np.shape(S)[0] - 1))
- else :
- cluster_todf.append(['else']*(np.shape(S)[0] - 1))
- df_const = {'Selectivity' : np.array(s_todf).flatten(), 'Abs selectivity' : np.array(np.abs(s_todf)).flatten() ,'Regressor' : np.array(reg_todf).flatten(), 'Cluster' : np.array(cluster_todf).flatten()}
- plot_df = pd.DataFrame(df_const)
- # %%
- ## Absolute selectivity across task variables for different populations
- plt.figure(figsize=(12,9))
- plot_dfAB = plot_df[(plot_df['Cluster'] != 'else')&(plot_df['Regressor'] != 'Task')&(plot_df['Regressor'] != 'Lick LR')]
- p = sns.violinplot(data = plot_dfAB, x='Regressor',y='Abs selectivity',hue='Cluster',\
- hue_order=['A','B'],alpha=0.7,palette=['blue','red'],cut=0,common_norm=True,\
- inner_kws=dict(box_width=7, whis_width=2))
- plt.ylabel(r'|$\beta$|',fontsize=40)
- plt.xlabel('')
- plt.legend([],[], frameon=False)
- plt.axvline(x = 1.5, color = 'black', linewidth=1.6)
- plt.ylim(0,1)
- plt.yticks([0,0.2,0.4,0.6,0.8,1])
- plt.savefig('Plots/SVG/violin_absolute.SVG', dpi = 300,bbox_inches='tight')
- plt.savefig('Plots/PNG/violin_absolute.PNG', dpi = 300,bbox_inches='tight')
- # %%
- export_plot_data('../../Source_Data.xlsx','Figure4f',CategoryGNG_popA=np.abs(S[0,popA]),CategoryGNG_popB=np.abs(S[0,popB]),
- ChoiceGNG_popA=np.abs(S[1,popA]),ChoiceGNG_popB=np.abs(S[1,popB]),
- CategoryLR_popA=np.abs(S[2,popA]),CategoryLR_popB=np.abs(S[2,popB]),
- ChoiceLR_popA=np.abs(S[3,popA]),ChoiceLR_popB=np.abs(S[3,popB]))
- # %%
- ## Absolute selectivity across task variables for different populations
- plt.figure(figsize=(12,8))
- plot_dfAB = plot_df[(plot_df['Regressor'] != 'Task')&(plot_df['Regressor'] != 'Lick LR')]
- p = sns.violinplot(data = plot_dfAB, x='Regressor',y='Abs selectivity',hue='Cluster',\
- hue_order=['A','B','else'],alpha=0.7,palette=['blue','red','green'],cut=0,common_norm=False,\
- inner_kws=dict(box_width=7, whis_width=2))
- plt.ylabel(r'|$\beta$|',fontsize=40)
- plt.xlabel('')
- plt.legend([],[], frameon=False)
- plt.axvline(x = 1.5, color = 'black', linewidth=1.6)
- plt.ylim(0,1)
- plt.yticks([0,0.2,0.4,0.6,0.8,1])
- plt.savefig('Plots/SVG/violin_absolute_all.SVG', dpi = 300,bbox_inches='tight')
- plt.savefig('Plots/PNG/violin_absolute_all.PNG', dpi = 300,bbox_inches='tight')
- # %%
- from scipy.stats import mannwhitneyu
- for reg_label in plot_dfAB['Regressor'].unique() :
- one_reg = plot_dfAB[(plot_dfAB['Regressor'] == reg_label)]
- print(reg)
- print(mannwhitneyu(one_reg[one_reg['Cluster'] == 'A']['Abs selectivity'],\
- one_reg[one_reg['Cluster'] == 'B']['Abs selectivity']))
- # %%
- plt.figure(figsize=(6,10))
- plot_dfall = plot_df
- task_only = True
- if task_only :
- plot_dfall = plot_dfall[plot_dfall['Regressor'] == 'Task']
- plt.figure(figsize=(2.5,8))
- p = sns.violinplot(data = plot_dfall, x='Regressor',y='Selectivity',hue='Cluster',hue_order=['A','B','else'],\
- alpha=0.7,palette=['blue','red','green'],inner_kws=dict(box_width=7, whis_width=2))
- plt.ylabel(r'$\beta_i$', fontsize=35)
- plt.xlabel('')
- plt.ylim((-1.2,1.2))
- plt.legend([],[], frameon=False)
- if task_only :
- plt.axhline(0,color='black',linestyle='--',linewidth=1.5)
- plt.ylabel(r'$\beta_{Task}$', fontsize=32)
- plt.xticks([])
- ax = plt.gca()
- ax.spines['bottom'].set_visible(False)
- ax.get_xaxis().set_visible(False)
- plt.savefig('Plots/SVG/violin_task.SVG', dpi = 300,bbox_inches='tight')
- plt.savefig('Plots/PNG/violin_task.PNG', dpi = 300,bbox_inches='tight')
- else :
- plt.axvline(x = 1.5, color = 'black', linewidth=1.5)
- plt.axvline(x = 4.5, color = 'black', linewidth=1.5)
- plt.savefig('Plots/SVG/violin.SVG', dpi = 300,bbox_inches='tight')
- plt.savefig('Plots/PNG/violin.PNG', dpi = 300,bbox_inches='tight')
- # %%
- print(mannwhitneyu(S[5,popA],S[5,popB],alternative = 'two-sided'))
- # %%
- export_plot_data('../../Source_Data.xlsx','Figure4b',popA = S[5,popA], popB = S[5,popB], pop0 = S[5,pop0])
- # %% [markdown]
- # ### Variance across time
- # %%
- fig, ax = plt.subplots(figsize=(7,7))
- M = np.stack(merged_data['Trajectory'])
- t_GNG = merged_data['Context'] == 0
- t_LR = merged_data['Context'] == 1
- var_GNG = np.std(M[t_GNG,:,:],axis=(0,1))
- var_LR = np.std(M[t_LR,:,:],axis=(0,1))
- nb_neurons = 200
- ax.scatter(var_GNG[pop0][:nb_neurons],var_LR[pop0][:nb_neurons],color='green',s=35, alpha = 0.65,\
- edgecolors='white',lw=1,antialiased=True,label='Population 0')
- ax.scatter(var_GNG[popA][:nb_neurons],var_LR[popA][:nb_neurons],color='blue',s=35, alpha = 0.65,\
- edgecolors='white',lw=1,antialiased=True,label='Population GNG')
- ax.scatter(var_GNG[popB][:nb_neurons],var_LR[popB][:nb_neurons],color='red',s=35, alpha = 0.65,\
- edgecolors='white',lw=1,antialiased=True,label='Population LR')
- confidence_ellipse(var_GNG[popA][:nb_neurons],var_LR[popA][:nb_neurons],ax, edgecolor='blue', n_std=1, alpha = 0.6,linewidth=3, zorder=3)
- confidence_ellipse(var_GNG[popB][:nb_neurons],var_LR[popB][:nb_neurons],ax, edgecolor='red', n_std=1, alpha = 0.6,linewidth=3, zorder=3)
- confidence_ellipse(var_GNG[pop0][:nb_neurons],var_LR[pop0][:nb_neurons],ax, edgecolor='green', n_std=1, alpha = 0.6,linewidth=3, zorder=3)
- plt.xlim((0,0.035))
- plt.ylim((0,0.035))
- plt.plot(np.linspace(0,0.035,100),np.linspace(0,0.035,100),alpha=0.5,color='black',linestyle='--')
- plt.xlabel('Time variance in Go/NoGo')
- plt.ylabel('Time variance in Left/Right')
- plt.xticks([0,0.035],[0,0.035])
- plt.yticks([0,0.035],[0,0.035])
- lgnd = plt.legend(frameon=False,fontsize=18)
- #plt.savefig('Plots/PNG/variance_time_gm.PNG', dpi = 300, bbox_inches='tight')
- #plt.savefig('Plots/SVG/variance_time_gm.SVG', dpi = 300, bbox_inches='tight')
- # %%
- plt.figure(figsize=(6,5))
- u = np.array([1/np.sqrt(2),-1/np.sqrt(2)])
- M_A = np.array([var_GNG[popA][:nb_neurons],var_LR[popA][:nb_neurons]]).T
- M_B = np.array([var_GNG[popB][:nb_neurons],var_LR[popB][:nb_neurons]]).T
- M_0 = np.array([var_GNG[pop0][:nb_neurons],var_LR[pop0][:nb_neurons]]).T
- ymin,ymax = -0.01, 0.01
- nb_bin=20
- hA = plt.hist(M_A@u,color='blue',density=True,range=(ymin,ymax), bins=nb_bin,alpha = 0.3,edgecolor = "black");
- hB = plt.hist(M_B@u,color='red',density=True, range=(ymin,ymax), bins=nb_bin,alpha = 0.3,edgecolor = "black");
- h0 = plt.hist(M_0@u,color='green',density=True,range=(ymin,ymax), bins=nb_bin,alpha = 0.3,edgecolor = "black");
- plt.clf()
- spl_A = splrep(hA[1], list(hA[0]) + [0], s=0.01, per=False)
- spl_B = splrep(hB[1], list(hB[0]) + [0], s=0.01, per=False)
- spl_0 = splrep(h0[1], list(h0[0]) + [0], s=0.01, per=False)
- x = np.linspace(ymin, ymax, 200)
- yA = splev(x, spl_A)
- yB = splev(x, spl_B)
- y0 = splev(x, spl_0)
- plt.fill_between(x,y1=0,y2=yA,color='blue',alpha=0.5)
- plt.fill_between(x,y1=0,y2=yB,color='red',alpha=0.5)
- plt.fill_between(x,y1=0,y2=y0,color='green',alpha=0.5)
- plt.ylim((0,300))
- plt.yticks([])
- plt.ylabel("Density")
- plt.xlim((ymin,ymax))
- plt.xticks([0],[''])
- #plt.savefig('Plots/PNG/variance_time_hist.PNG', dpi = 300, bbox_inches='tight')
- #plt.savefig('Plots/SVG/variance_time_hist.SVG', dpi = 300, bbox_inches='tight')
- # %%
- nb_bin=25
- bin_v = np.array([(k+0.5)*np.pi/(2*nb_bin) for k in range(0,nb_bin)])
- thetas = np.arctan(var_GNG/var_LR)
- hA = plt.hist(thetas[popA],color='blue',density=True,range=(0,np.pi/2), bins=nb_bin,alpha = 0.4);
- hB = plt.hist(thetas[popB],color='red',density=True, range=(0,np.pi/2), bins=nb_bin,alpha = 0.4);
- h0 = plt.hist(thetas[pop0],color='green',density=True,range=(0,np.pi/2), bins=nb_bin,alpha = 0.4);
- plt.clf()
- # %%
- from scipy.optimize import curve_fit
- plt.figure(figsize=(7,7))
- plt.hist(thetas[popA],color='red',density=True,range=(0,np.pi/2), bins=nb_bin, alpha = 0.3,edgecolor = "black")
- #plt.plot(bin_v,fit_y_popA,color='red',linewidth=3)
- plt.hist(thetas[popB],color='blue',density=True,range=(0,np.pi/2), bins=nb_bin, alpha = 0.3,edgecolor = "black")
- #plt.plot(bin_v,fit_y_popB,color='blue',linewidth=3)
- plt.hist(thetas[pop0],color='green',density=True,range=(0,np.pi/2), bins=nb_bin, alpha = 0.3,edgecolor = "black")
- #plt.plot(bin_v,fit_y_pop0,color='green',linewidth=3)
- plt.xlabel(r'$\alpha$',fontsize=30)
- plt.xticks([0,np.pi/4,np.pi/2],['0',r'$\frac{\pi}{4}$',r'$\frac{\pi}{2}$'])
- plt.ylabel('Density',fontsize=25)
- plt.xlim(0,np.pi/2)
- plt.yticks([0,1,2,3])
- """
- plt.savefig('Plots/PNG/variance_time_angle_gm.PNG', dpi = 300, bbox_inches='tight')
- plt.savefig('Plots/SVG/variance_time_angle_gm.SVG', dpi = 300, bbox_inches='tight')
- """
- # %%
- from scipy.stats import ttest_ind
- print(ttest_ind(thetas[popA],thetas[pop0]))
- print(ttest_ind(thetas[popB],thetas[pop0]))
- print(ttest_ind(thetas[popA],thetas[popB]))
- # %% [markdown]
- # ### Population decoding
- # %%
- M = np.stack(merged_data['Spike rate'])
- M = pca1.transform(M)
- M = pca1.inverse_transform(M)
- M = standardize(M)
- hit_GNG = merged_data['Label'] == 1
- cr_GNG = merged_data['Label'] == 3
- left_LR = merged_data['Label'] == 5
- right_LR = merged_data['Label'] == 8
- GNG_task = merged_data['Context'] == 0
- LR_task = 1-GNG_task
- M_GNG_hit = M[GNG_task&hit_GNG,:]
- M_GNG_cr = M[GNG_task&cr_GNG,:]
- M_LR_left = M[~GNG_task&left_LR,:]
- M_LR_right = M[~GNG_task&right_LR,:]
- X_GNG = np.concatenate((M_GNG_hit,M_GNG_cr),axis=0)
- X_LR = np.concatenate((M_LR_right,M_LR_left),axis=0)
- Y_GNG = np.array([1 for k in range(len(M_GNG_hit))] + [-1 for k in range(len(M_GNG_cr))])
- Y_LR = np.array([1 for k in range(len(M_LR_right))] + [-1 for k in range(len(M_LR_left))])
- # %%
- from tqdm.auto import tqdm
- from sklearn.svm import SVC
- nb_decoding = 100
- nb_neuron = 100
- def pop_decoding(nb_neuron = nb_neuron, nb_decoding = nb_decoding, return_inter_task=False) :
- """Method performing population decoding with Support Vector Classifier trained on nb_neuron different cells .
- We train and test models on every combinations of epochs"""
- scores_A_GNG = []
- scores_A_LR = []
- scores_B_GNG = []
- scores_B_LR = []
- scores_0_GNG = []
- scores_0_LR = []
- scores_A_GNG_to_LR = []
- scores_A_LR_to_GNG = []
- scores_B_GNG_to_LR = []
- scores_B_LR_to_GNG = []
- scores_0_GNG_to_LR = []
- scores_0_LR_to_GNG = []
- masks = []
- for n in tqdm(range(nb_decoding)) :
- svc_GNG_popA = SVC(kernel='linear',gamma='auto')
- svc_GNG_popB = SVC(kernel='linear',gamma='auto')
- svc_GNG_pop0 = SVC(kernel='linear',gamma='auto')
- svc_LR_popA = SVC(kernel='linear',gamma='auto')
- svc_LR_popB = SVC(kernel='linear',gamma='auto')
- svc_LR_pop0 = SVC(kernel='linear',gamma='auto')
- mask_A = np.random.choice(range(np.sum(popA)),nb_neuron,replace = False) # We do not replace to ensure independent decoding scores
- mask_B = np.random.choice(range(np.sum(popB)),nb_neuron,replace = False)
- mask_0 = np.random.choice(range(np.sum(pop0)),nb_neuron,replace = False)
- split_GNG = np.random.choice(range(len(X_GNG)),len(X_GNG)//2,replace=False)
- split_LR = np.random.choice(range(len(X_LR)),len(X_LR)//2,replace=False)
- X_GNG_train = X_GNG[split_GNG,:]
- Y_GNG_train = Y_GNG[split_GNG]
- X_GNG_test = np.delete(X_GNG,split_GNG,axis=0)
- Y_GNG_test = np.delete(Y_GNG,split_GNG,axis=0)
- X_LR_train = X_LR[split_LR,:]
- Y_LR_train = Y_LR[split_LR]
- X_LR_test = np.delete(X_LR,split_LR,axis=0)
- Y_LR_test = np.delete(Y_LR,split_LR,axis=0)
- svc_GNG_popA.fit(X_GNG_train[:,popA][:,mask_A],Y_GNG_train)
- svc_GNG_popB.fit(X_GNG_train[:,popB][:,mask_B],Y_GNG_train)
- svc_GNG_pop0.fit(X_GNG_train[:,pop0][:,mask_0],Y_GNG_train)
- svc_LR_popA.fit(X_LR_train[:,popA][:,mask_A],Y_LR_train)
- svc_LR_popB.fit(X_LR_train[:,popB][:,mask_B],Y_LR_train)
- svc_LR_pop0.fit(X_LR_train[:,pop0][:,mask_0],Y_LR_train)
- scores_A_GNG.append(svc_GNG_popA.score(X_GNG_test[:,popA][:,mask_A],Y_GNG_test))
- scores_B_GNG.append(svc_GNG_popB.score(X_GNG_test[:,popB][:,mask_B],Y_GNG_test))
- scores_0_GNG.append(svc_GNG_pop0.score(X_GNG_test[:,pop0][:,mask_0],Y_GNG_test))
- scores_A_LR.append(svc_LR_popA.score(X_LR_test[:,popA][:,mask_A],Y_LR_test))
- scores_B_LR.append(svc_LR_popB.score(X_LR_test[:,popB][:,mask_B],Y_LR_test))
- scores_0_LR.append(svc_LR_pop0.score(X_LR_test[:,pop0][:,mask_0],Y_LR_test))
- masks.append([mask_A, mask_B, mask_0])
- if return_inter_task :
- scores_A_GNG_to_LR.append(svc_GNG_popA.score(X_LR_test[:,popA][:,mask_A],Y_LR_test))
- scores_B_GNG_to_LR.append(svc_GNG_popB.score(X_LR_test[:,popB][:,mask_B],Y_LR_test))
- scores_0_GNG_to_LR.append(svc_GNG_pop0.score(X_LR_test[:,pop0][:,mask_0],Y_LR_test))
- scores_A_LR_to_GNG.append(svc_LR_popA.score(X_GNG_test[:,popA][:,mask_A],Y_GNG_test))
- scores_B_LR_to_GNG.append(svc_LR_popB.score(X_GNG_test[:,popB][:,mask_B],Y_GNG_test))
- scores_0_LR_to_GNG.append(svc_LR_pop0.score(X_GNG_test[:,pop0][:,mask_0],Y_GNG_test))
- scores_A_GNG = np.array(scores_A_GNG)
- scores_A_LR = np.array(scores_A_LR)
- scores_B_GNG = np.array(scores_B_GNG)
- scores_B_LR = np.array(scores_B_LR)
- scores_0_GNG = np.array(scores_0_GNG)
- scores_0_LR = np.array(scores_0_LR)
- scores_A_GNG_to_LR = np.array(scores_A_GNG_to_LR)
- scores_A_LR_to_GNG = np.array(scores_A_LR_to_GNG)
- scores_B_GNG_to_LR = np.array(scores_B_GNG_to_LR)
- scores_B_LR_to_GNG = np.array(scores_B_LR_to_GNG)
- scores_0_GNG_to_LR = np.array(scores_0_GNG_to_LR)
- scores_0_LR_to_GNG = np.array(scores_0_LR_to_GNG)
- masks = np.array(masks)
- if return_inter_task :
- return [scores_A_GNG, scores_A_LR, scores_A_GNG_to_LR, scores_A_LR_to_GNG],\
- [scores_B_GNG, scores_B_LR, scores_B_GNG_to_LR, scores_B_LR_to_GNG],\
- [scores_0_GNG, scores_0_LR, scores_0_GNG_to_LR, scores_0_LR_to_GNG], masks
- else :
- return [scores_A_GNG, scores_A_LR], [scores_B_GNG, scores_B_LR], [scores_0_GNG, scores_0_LR]
- # %%
- scores_A, scores_B, scores_0, _ = pop_decoding(2,100,return_inter_task=True)
- scores_A_many, scores_B_many, scores_0_many, _ = pop_decoding(20,return_inter_task=True)
- # %% [markdown]
- # ### Go/NoGo - Go/NoGo
- # %%
- from scipy.interpolate import splev, splrep
- fig,axs = plt.subplots(1,2,figsize=(6,8),gridspec_kw={'width_ratios': [3, 1]})
- #fig.subplots_adjust(hspace=0)
- df_dict = {'Population\nGNG' : scores_A[0], 'Population\nLR' : scores_B[0]}
- test = pd.DataFrame(df_dict)
- sns.stripplot(test,alpha=0.7,palette=['blue','red'],jitter=0.2,size=12,ax=axs[0])
- axs[0].errorbar([0,1],[np.mean(scores_A[0]),np.mean(scores_B[0])],yerr=[np.std(scores_A[0])/np.sqrt(nb_decoding),np.std(scores_B[0])/np.sqrt(nb_decoding)],\
- color='black',marker='_',markersize=85,elinewidth=3.5,capsize=7,zorder=10,linestyle='',markeredgewidth=3.5)
- ymin,ymax = 0.3,1
- axs[0].set_xlim((-0.5,1.5))
- axs[0].set_ylim((ymin,ymax+0.05))
- axs[0].set_yticks([0.5,0.75,1],['50%','75%','100%'])
- axs[0].set_ylabel('Population decoding accuracy')
- axs[0].fill_between([-0.5,1.5],ymin,0.5, color='black',alpha=0.05)
- axs[0].axhline(0.5,color="black",linestyle='--',alpha=0.3,linewidth=1.5)
- nb_bins=25
- histA = axs[1].hist(scores_A[0],range=(ymin,ymax),bins=nb_bins,orientation='horizontal',color='blue',alpha=0.5,density=True)
- histB = axs[1].hist(scores_B[0],range=(ymin,ymax),bins=nb_bins,orientation='horizontal',color='red',alpha=0.5,density=True)
- axs[1].clear()
- axs[1].set_yticks([])
- axs[1].set_xticks([])
- axs[1].spines['bottom'].set_visible(False)
- axs[1].set_ylim((ymin,ymax+0.02))
- axs[1].set_xlim((0,70))
- # Interpolation
- spl_A = splrep(histA[1], list(histA[0]) + [0], s=0.01, per=False)
- spl_B = splrep(histB[1], list(histB[0]) + [0], s=0.01, per=False)
- x = np.linspace(ymin, ymax, 200)
- yA = splev(x, spl_A)
- yB = splev(x, spl_B)
- axs[1].fill_betweenx(x,x1=0,x2=yA,color='blue',alpha=0.5)
- axs[1].fill_betweenx(x,x1=0,x2=yB,color='red',alpha=0.5)
- #plt.savefig('Plots/SVG/decoding_2neuron_GNG.SVG', dpi = 300,bbox_inches='tight')
- #plt.savefig('Plots/PNG/decoding_2neuron_GNG.PNG', dpi = 300,bbox_inches='tight')
- print(mannwhitneyu(scores_A[0],scores_B[0]))
- # %%
- from scipy.interpolate import splev, splrep
- fig,ax = plt.subplots(1,1,figsize=(6.2,8))
- #fig.subplots_adjust(hspace=0)
- df_dict = {'Population\nGNG' : scores_A[0], 'Population\nLR' : scores_B[0], 'Population\n0' : scores_0[0]}
- test = pd.DataFrame(df_dict)
- sns.stripplot(test,alpha=0.7,palette=['blue','red','green'],jitter=0.2,size=12,ax=ax)
- ax.errorbar([0,1,2],[np.mean(scores_A[0]),np.mean(scores_B[0]),np.mean(scores_0[0])],yerr=[np.std(scores_A[0])/np.sqrt(nb_decoding),np.std(scores_B[0])/np.sqrt(nb_decoding),np.std(scores_0[0])/np.sqrt(nb_decoding)],\
- color='black',marker='_',markersize=85,elinewidth=3.5,capsize=7,zorder=10,linestyle='',markeredgewidth=3.5)
- ymin,ymax = 0.3,1
- ax.set_xlim((-0.5,2.5))
- ax.set_ylim((ymin,ymax+0.05))
- ax.set_yticks([0.5,0.75,1],['50%','75%','100%'])
- ax.set_ylabel('Population decoding accuracy')
- ax.fill_between([-0.5,2.5],ymin,0.5, color='black',alpha=0.05)
- ax.axhline(0.5,color="black",linestyle='--',alpha=0.3,linewidth=1.5)
- plt.savefig('Plots/SVG/decoding_2neuron_GNG.SVG', dpi = 300,bbox_inches='tight')
- plt.savefig('Plots/PNG/decoding_2neuron_GNG.PNG', dpi = 300,bbox_inches='tight')
- print(mannwhitneyu(scores_A[0],scores_B[0]))
- print(mannwhitneyu(scores_A[0],scores_0[0]))
- print(mannwhitneyu(scores_B[0],scores_0[0]))
- # %%
- export_plot_data('../../Source_Data.xlsx','Figure4g',scores_A = scores_A[0], scores_B = scores_B[0], scores_0 = scores_0[0])
- # %% [markdown]
- # ### LR - LR
- # %%
- from scipy.interpolate import splev, splrep
- fig,axs = plt.subplots(1,2,figsize=(6,8),gridspec_kw={'width_ratios': [3, 1]})
- #fig.subplots_adjust(hspace=0)
- df_dict = {'Population\nGNG' : scores_A[1], 'Population\nLR' : scores_B[1]}
- test = pd.DataFrame(df_dict)
- sns.stripplot(test,alpha=0.7,palette=['blue','red'],jitter=0.2,size=12,ax=axs[0])
- axs[0].errorbar([0,1],[np.mean(scores_A[1]),np.mean(scores_B[1])],yerr=[np.std(scores_A[1])/np.sqrt(nb_decoding),np.std(scores_B[1])/np.sqrt(nb_decoding)],\
- color='black',marker='_',markersize=85,elinewidth=3.5,capsize=7,zorder=10,linestyle='',markeredgewidth=3.5)
- ymin,ymax = 0.3,1
- axs[0].set_xlim((-0.5,1.5))
- axs[0].set_ylim((ymin,ymax+0.05))
- axs[0].set_yticks([0.5,0.75,1],['50%','75%','100%'])
- axs[0].set_ylabel('Population decoding accuracy')
- axs[0].fill_between([-0.5,1.5],ymin,0.5, color='black',alpha=0.05)
- axs[0].axhline(0.5,color="black",linestyle='--',alpha=0.3,linewidth=1.5)
- nb_bins=25
- histA = axs[1].hist(scores_A[1],range=(ymin,ymax),bins=nb_bins,orientation='horizontal',color='blue',alpha=0.5,density=True)
- histB = axs[1].hist(scores_B[1],range=(ymin,ymax),bins=nb_bins,orientation='horizontal',color='red',alpha=0.5,density=True)
- axs[1].clear()
- axs[1].set_yticks([])
- axs[1].set_xticks([])
- axs[1].spines['bottom'].set_visible(False)
- axs[1].set_ylim((ymin,ymax+0.02))
- axs[1].set_xlim((0,30))
- # Interpolation
- spl_A = splrep(histA[1], list(histA[0]) + [0], s=0.01, per=False)
- spl_B = splrep(histB[1], list(histB[0]) + [0], s=0.01, per=False)
- x = np.linspace(ymin, ymax, 200)
- yA = splev(x, spl_A)
- yB = splev(x, spl_B)
- axs[1].fill_betweenx(x,x1=0,x2=yA,color='blue',alpha=0.5)
- axs[1].fill_betweenx(x,x1=0,x2=yB,color='red',alpha=0.5)
- #plt.savefig('Plots/SVG/decoding_2neuron_LR.SVG', dpi = 300,bbox_inches='tight')
- #plt.savefig('Plots/PNG/decoding_2neuron_LR.PNG', dpi = 300,bbox_inches='tight')
- print(mannwhitneyu(scores_A[1],scores_B[1]))
- # %%
- from scipy.interpolate import splev, splrep
- fig,ax = plt.subplots(1,1,figsize=(6.2,8))
- df_dict = {'Population\nGNG' : scores_A[1], 'Population\nLR' : scores_B[1], 'Population\n0' : scores_0[1]}
- test = pd.DataFrame(df_dict)
- sns.stripplot(test,alpha=0.7,palette=['blue','red','green'],jitter=0.2,size=12,ax=ax)
- ax.errorbar([0,1,2],[np.mean(scores_A[1]),np.mean(scores_B[1]),np.mean(scores_0[1])],yerr=[np.std(scores_A[1])/np.sqrt(nb_decoding),np.std(scores_B[1])/np.sqrt(nb_decoding),np.std(scores_0[1])/np.sqrt(nb_decoding)],\
- color='black',marker='_',markersize=85,elinewidth=3.5,capsize=7,zorder=10,linestyle='',markeredgewidth=3.5)
- ymin,ymax = 0.3,1
- ax.set_xlim((-0.5,2.5))
- ax.set_ylim((ymin,ymax+0.05))
- ax.set_yticks([0.5,0.75,1],['50%','75%','100%'])
- ax.set_ylabel('Population decoding accuracy')
- ax.fill_between([-0.5,2.5],ymin,0.5, color='black',alpha=0.05)
- ax.axhline(0.5,color="black",linestyle='--',alpha=0.3,linewidth=1.5)
- plt.savefig('Plots/SVG/decoding_2neuron_LR.SVG', dpi = 300,bbox_inches='tight')
- plt.savefig('Plots/PNG/decoding_2neuron_LR.PNG', dpi = 300,bbox_inches='tight')
- print(mannwhitneyu(scores_A[1],scores_B[1]))
- print(mannwhitneyu(scores_A[1],scores_0[1]))
- print(mannwhitneyu(scores_B[1],scores_0[1]))
- # %%
- export_plot_data('../../Source_Data.xlsx','Figure4h',scores_A = scores_A[1], scores_B = scores_B[1], scores_0 = scores_0[1])
- # %% [markdown]
- # ### Go/NoGo - LR
- # %%
- from scipy.interpolate import splev, splrep
- fig,axs = plt.subplots(1,2,figsize=(10,8),gridspec_kw={'width_ratios': [3, 1]})
- #fig.subplots_adjust(hspace=0)
- df_dict = {'Population\nGNG' : scores_A_many[2], 'Population\nLR' : scores_B_many[2],'Population\n0' : scores_0_many[2]}
- df_plot = pd.DataFrame(df_dict)
- sns.stripplot(df_plot,alpha=0.7,palette=['blue','red','green'],jitter=0.2,size=12,ax=axs[0])
- axs[0].errorbar([0,1,2],[np.mean(scores_A_many[2]),np.mean(scores_B_many[2]),np.mean(scores_0_many[2])],\
- yerr=[np.std(scores_A_many[2])/np.sqrt(nb_decoding),np.std(scores_B_many[2])/np.sqrt(nb_decoding),np.std(scores_0_many[2])/np.sqrt(nb_decoding)],\
- color='black',marker='_',markersize=80,elinewidth=3.5,capsize=7,zorder=10,linestyle='',markeredgewidth=3.5)
- ymin,ymax = 0.3,1
- axs[0].set_xlim((-0.5,2.5))
- axs[0].set_ylim((ymin,ymax+0.05))
- axs[0].set_yticks([0.5,0.75,1],['50%','75%','100%'],fontsize=25)
- axs[0].set_ylabel('Population decoding accuracy')
- axs[0].fill_between([-0.5,2.5],ymin,0.5, color='black',alpha=0.05)
- axs[0].axhline(0.5,color="black",linestyle='--',alpha=0.3,linewidth=1.5)
- nb_bins=25
- histA = axs[1].hist(scores_A_many[2],range=(ymin,ymax),bins=nb_bins,orientation='horizontal',color='blue',alpha=0.5,density=True)
- histB = axs[1].hist(scores_B_many[2],range=(ymin,ymax),bins=nb_bins,orientation='horizontal',color='red',alpha=0.5,density=True)
- hist0 = axs[1].hist(scores_0_many[2],range=(ymin,ymax),bins=nb_bins,orientation='horizontal',color='green',alpha=0.5,density=True)
- axs[1].clear()
- axs[1].set_yticks([])
- axs[1].set_xticks([])
- axs[1].spines['bottom'].set_visible(False)
- axs[1].set_ylim((ymin,ymax+0.02))
- axs[1].set_xlim((0,30))
- # Interpolation
- spl_A = splrep(histA[1], list(histA[0]) + [0], s=0.01, per=False)
- spl_B = splrep(histB[1], list(histB[0]) + [0], s=0.01, per=False)
- spl_0 = splrep(hist0[1], list(hist0[0]) + [0], s=0.01, per=False)
- x = np.linspace(ymin, ymax, 200)
- yA = splev(x, spl_A)
- yB = splev(x, spl_B)
- y0 = splev(x, spl_0)
- axs[1].fill_betweenx(x,x1=0,x2=yA,color='blue',alpha=0.5)
- axs[1].fill_betweenx(x,x1=0,x2=yB,color='red',alpha=0.5)
- axs[1].fill_betweenx(x,x1=0,x2=y0,color='green',alpha=0.5)
- #plt.savefig('Plots/SVG/decoding_20neuron_GNG_to_LR.SVG', dpi = 300,bbox_inches='tight')
- #plt.savefig('Plots/PNG/decoding_20neuron_GNG_to_LR.PNG', dpi = 300,bbox_inches='tight')
- print(mannwhitneyu(scores_0_many[2],scores_A_many[2]))
- print(mannwhitneyu(scores_0_many[2],scores_B_many[2]))
- print(wilcoxon(scores_0_many[2]-0.5,alternative='greater'))
- # %%
- export_plot_data('../../Source_Data.xlsx','Figure4i',scores_A = scores_A_many[2], scores_B = scores_B_many[2], scores_0 = scores_0_many[2])
- # %% [markdown]
- # ### Information
- # %%
- ## Exploration on the nature of information each population provides
- nb_bins = 20
- def compute_prob(data) :
- hist = np.histogram(data,nb_bins,range=(-2,2))[0]
- return hist/np.sum(hist)
- eps = 0
- def MI(XY_mat,pX,pY) :
- # XY matrix must be the matrix of X conditionned on Y
- mi = 0
- for i in range(np.shape(XY_mat)[0]) :
- for j in range(np.shape(XY_mat)[1]) :
- if (pY[i] <= eps) or (pX[j] <= eps) or (XY_mat[i,j] <= eps) :
- mi+=0
- else :
- #print(pY[i])
- #print(pX[j])
- mi+=pY[i]*XY_mat[i,j]*np.log2(pY[i]*XY_mat[i,j]/(pY[i]*pX[j]))
- return mi
- def cond_MI(XYZ_mat,XZ_mat,pX,pY,pZ) :
- # We compute the MI between X and Y conditionned on Z
- # Here we suppose the independence between Y and Z. XYZ matrix is the matrix of X conditionned on Y and Z.
- mi = 0
- for i in range(np.shape(XYZ_mat)[0]) :
- for j in range(np.shape(XYZ_mat)[1]) :
- for k in range(np.shape(XYZ_mat)[2]) :
- if (XZ_mat[j,k] <= eps) or (XYZ_mat[i,j,k] <= eps):
- mi += 0
- else :
- mi += pY[i]*pZ[j]*XYZ_mat[i,j,k]*np.log2(XYZ_mat[i,j,k]/XZ_mat[j,k])
- return mi
- # %%
- from sklearn.mixture import GaussianMixture, BayesianGaussianMixture
- from tqdm.auto import tqdm
- def get_pop(prob,S) :
- pop1 = prob[:,0] >= 0.9
- pop2 = prob[:,1] >= 0.9
- pop3 = prob[:,2] >= 0.9
- pops = [pop1,pop2,pop3]
- S_pop = [S[:,pop1],S[:,pop2],S[:,pop3]]
- mean_ctx_s = [np.mean(S_pop[0][0,:]),np.mean(S_pop[1][0,:]),np.mean(S_pop[2][0,:])]
- popA = pops[np.argsort(mean_ctx_s)[0]]
- popB = pops[np.argsort(mean_ctx_s)[2]]
- pop0 = pops[np.argsort(mean_ctx_s)[1]]
- return popA, popB, pop0
- def compute_UI_SYN_pop(S_grid,A_data,s_data,c_data) :
- pC = np.array([0.5,0.5])
- pS = np.array([0.5,0.5])
- MI_AC_popA = []
- MI_AS_popA = []
- MI_AC_S_popA = []
- MI_AC_popB = []
- MI_AS_popB = []
- MI_AC_S_popB = []
- MI_AC_pop0 = []
- MI_AS_pop0 = []
- MI_AC_S_pop0 = []
- A_matrices = np.array([compute_prob(A_data[:,n]) for n in range(np.shape(A_data)[1])])
- AS_matrices = np.array([[compute_prob(A_data[s_data==-1,n]),compute_prob(A_data[s_data==1,n])] for n in range(np.shape(A_data)[1])])
- AC_matrices = np.array([[compute_prob(A_data[c_data==-1,n]),compute_prob(A_data[c_data==1,n])] for n in range(np.shape(A_data)[1])])
- ACS_matrices = np.array([[[compute_prob(A_data[(s_data==-1)&(c_data==-1),n]),compute_prob(A_data[(s_data==1)&(c_data==-1),n])],\
- [compute_prob(A_data[(s_data==-1)&(c_data==1),n]),compute_prob(A_data[(s_data==1)&(c_data==1),n])]] for n in range(np.shape(A_data)[1])])
- MI_AS_s = []
- MI_AC_s = []
- MI_AC_S_s = []
- for n in range(np.shape(A_matrices)[0]) :
- pA = A_matrices[n]
- AS_mat = AS_matrices[n]
- AC_mat = AC_matrices[n]
- ACS_mat = ACS_matrices[n]
- MI_AS_s.append(MI(AS_mat,pA,pS))
- MI_AC_s.append(MI(AC_mat,pA,pC))
- MI_AC_S_s.append(cond_MI(ACS_mat,AS_mat,pA,pC,pS))
- MI_AS_s = np.array(MI_AS_s)
- MI_AC_s = np.array(MI_AC_s)
- MI_AC_S_s = np.array(MI_AC_S_s)
- MI_AC_popA.append(MI_AC_s[popA])
- MI_AC_popB.append(MI_AC_s[popB])
- MI_AC_pop0.append(MI_AC_s[pop0])
- MI_AS_popA.append(MI_AS_s[popA])
- MI_AS_popB.append(MI_AS_s[popB])
- MI_AS_pop0.append(MI_AS_s[pop0])
- MI_AC_S_popA.append(MI_AC_S_s[popA])
- MI_AC_S_popB.append(MI_AC_S_s[popB])
- MI_AC_S_pop0.append(MI_AC_S_s[pop0])
- MI_AC_popA = np.array(MI_AC_popA)
- MI_AC_popB = np.array(MI_AC_popB)
- MI_AC_pop0 = np.array(MI_AC_pop0)
- MI_AS_popA = np.array(MI_AS_popA)
- MI_AS_popB = np.array(MI_AS_popB)
- MI_AS_pop0 = np.array(MI_AS_pop0)
- MI_AC_S_popA = np.array(MI_AC_S_popA)
- MI_AC_S_popB = np.array(MI_AC_S_popB)
- MI_AC_S_pop0 = np.array(MI_AC_S_pop0)
- return np.squeeze(np.array([MI_AC_popA, MI_AS_popA, MI_AC_S_popA])), \
- np.squeeze(np.array([MI_AC_popB, MI_AS_popB, MI_AC_S_popB])), \
- np.squeeze(np.array([MI_AC_pop0, MI_AS_pop0, MI_AC_S_pop0]))
- # %%
- M = np.stack(merged_data['Spike rate'])
- M = standardize(M)
- M = pca1.inverse_transform(pca1.transform(M))
- c_d = 2*merged_data['Context']-1
- s_d = 2*merged_data['Category ID']-3
- MI_popA, MI_popB, MI_pop0 = compute_UI_SYN_pop(S, M, s_d, c_d)
- # %%
- plt.figure(figsize=(10,7))
- colors=['blue','red','green']
- def set_color(bplot) :
- for patch, color in zip(bplot['boxes'], colors):
- patch.set_facecolor(color)
- patch.set_alpha(0.3)
- for median, color in zip(bplot['medians'], colors):
- median.set_color(color)
- median.set_linewidth(2)
- for mean, color in zip(bplot['means'], colors):
- mean.set_color(color)
- mean.set_alpha(0.5)
- mean.set_linewidth(1.5)
- plt.ylim(-0.05,1.05)
- bplot = plt.boxplot([MI_popA[0,:],MI_popB[0,:],MI_pop0[0,:]], positions=[0,0.3,0.6],\
- patch_artist=True,notch=True,bootstrap=1000,sym='',showmeans=True,meanline=True)
- set_color(bplot)
- bplot = plt.boxplot([MI_popA[1,:],MI_popB[1,:],MI_pop0[1,:]], positions=[1.5,1.8,2.1],\
- patch_artist=True,notch=True,bootstrap=1000,sym='',showmeans=True,meanline=True)
- set_color(bplot)
- bplot = plt.boxplot([MI_popA[2,:]-MI_popA[0,:],MI_popB[2,:]-MI_popB[0,:],MI_pop0[2,:]-MI_pop0[0,:]], positions=[3,3.3,3.6],\
- patch_artist=True,notch=True,bootstrap=1000,sym='',showmeans=True,meanline=True)
- set_color(bplot)
- plt.xticks([0.3,1.8,3.3],[r'$UI_{Category}$',r'$UI_{Task}$',r'$Syn_{\{Category,Task\}}$'])
- plt.ylabel('Information (bits)')
- plt.yticks([0,0.2,0.4,0.6,0.8,1])
- plt.savefig('Plots/SVG/MI_pop.SVG', dpi = 300,bbox_inches='tight')
- plt.savefig('Plots/PNG/MI_pop.PNG', dpi = 300,bbox_inches='tight')
- # %%
- from scipy.stats import mannwhitneyu, permutation_test
- print('------- UI category -------')
- print('A-B : ' + str(mannwhitneyu(MI_popA[0,:],MI_popB[0,:])))
- print('A-0 : ' + str(mannwhitneyu(MI_popA[0,:],MI_pop0[0,:])))
- print('B-0 : ' + str(mannwhitneyu(MI_popB[0,:],MI_pop0[0,:])))
- print('------- UI task -------')
- print('A-B : ' + str(mannwhitneyu(MI_popA[1,:],MI_popB[1,:])))
- print('A-0 : ' + str(mannwhitneyu(MI_popA[1,:],MI_pop0[1,:])))
- print('B-0 : ' + str(mannwhitneyu(MI_popB[1,:],MI_pop0[1,:])))
- print('------- Synergy -------')
- print('A-B : ' + str(mannwhitneyu(MI_popA[2,:]-MI_popA[0,:],MI_popB[2,:]-MI_popB[0,:])))
- print('A-0 : ' + str(mannwhitneyu(MI_popA[2,:]-MI_popA[0,:],MI_pop0[2,:]-MI_pop0[0,:])))
- print('B-0 : ' + str(mannwhitneyu(MI_popB[2,:]-MI_popB[0,:],MI_pop0[2,:]-MI_pop0[0,:])))
gaussian_mixture.ipynb at commit 852d401, no license · at the source
Overview
- Laboratoire des Systemes Perceptifs, Ecole Normale Superieure, PSL University, CNRS, Paris, France
- Ear Institute, University College London, London, UK
- Max Planck Institute for Biological Intelligence, Martinsried, Germany
- Present Address: Sainsbury Wellcome Centre, University College London, London, UK
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.
Repository
Its files are read in the Code ↔ Paper reader above, with 21 matches between paragraphs and lines of code.
HTissot42/Sensorimotor-remapping-drives-task-specialization
852d401f2c575a49377ae0e7b91e2b309db210a5, 23 June 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
59 files
- .ipynb_checkpoints/
export_source-checkpoint , Python, 149 lines.py - Response-switch/
DATA/ , Jupyter, 49 lines.ipynb_checkpoints/ data_export_tc-checkpoin t.ipynb - Response-switch/
DATA/ , Jupyter, 146 lines.ipynb_checkpoints/ edit_df_tc-checkpoint.ip ynb - Response-switch/
DATA/ , Jupyter, 49 linesdata_export_tc.ipynb - Response-switch/
DATA/ , Jupyter, 147 linesedit_df_tc.ipynb - Response-switch/
Individuals/ , Jupyter, 75 lines.ipynb_checkpoints/ PCA-checkpoint.ipynb - Response-switch/
Individuals/ , Jupyter, 675 lines.ipynb_checkpoints/ angles-checkpoint.ipynb - Response-switch/
Individuals/ , Jupyter, 436 lines.ipynb_checkpoints/ controls-checkpoint.ipyn b - Response-switch/
Individuals/ , Jupyter, 707 lines.ipynb_checkpoints/ decoding-checkpoint.ipyn b - Response-switch/
Individuals/ , Jupyter, 255 lines.ipynb_checkpoints/ regression-checkpoint.ip ynb - Response-switch/
Individuals/ , Jupyter, 640 lines.ipynb_checkpoints/ selectivity_analysis-che ckpoint.ipynb - Response-switch/
Individuals/ , Jupyter, 75 linesPCA.ipynb - Response-switch/
Individuals/ , Jupyter, 675 linesangles.ipynb - Response-switch/
Individuals/ , Jupyter, 436 linescontrols.ipynb - Response-switch/
Individuals/ , Jupyter, 707 linesdecoding.ipynb - Response-switch/
Individuals/ , Jupyter, 255 lines, 2 matchesregression.ipynb - Response-switch/
Individuals/ , Jupyter, 640 linesselectivity_analysis.ipy nb - Response-switch/
Pseudo_pop/ , Jupyter, 1,366 lines.ipynb_checkpoints/ gaussian_mixture-checkpo int.ipynb - Response-switch/
Pseudo_pop/ , Jupyter, 193 lines.ipynb_checkpoints/ pseudopop-checkpoint.ipy nb - Response-switch/
Pseudo_pop/ , Jupyter, 1,045 lines.ipynb_checkpoints/ regression-checkpoint.ip ynb - Response-switch/
Pseudo_pop/ , Jupyter, 1,022 lines.ipynb_checkpoints/ selectivity_analysis-che ckpoint.ipynb - Response-switch/
Pseudo_pop/ , Jupyter, 313 lines, 1 matchPCA.ipynb - Response-switch/
Pseudo_pop/ , Jupyter, 1,366 lines, 6 matchesgaussian_mixture.ipynb - Response-switch/
Pseudo_pop/ , Jupyter, 193 linespseudopop.ipynb - Response-switch/
Pseudo_pop/ , Jupyter, 1,045 lines, 5 matchesregression.ipynb - Response-switch/
Pseudo_pop/ , Jupyter, 1,022 linesselectivity_analysis.ipy nb - Rule-switch/
DATA/ , Jupyter, 69 lines.ipynb_checkpoints/ data_export_rs-checkpoin t.ipynb - Rule-switch/
DATA/ , Jupyter, 111 lines.ipynb_checkpoints/ edit_df_rs-checkpoint.ip ynb - Rule-switch/
DATA/ , Jupyter, 69 linesdata_export_rs.ipynb - Rule-switch/
DATA/ , Jupyter, 111 linesedit_df_rs.ipynb - Rule-switch/
Individuals/ , Jupyter, 77 lines.ipynb_checkpoints/ PCA_rs-checkpoint.ipynb - Rule-switch/
Individuals/ , Jupyter, 567 lines.ipynb_checkpoints/ controls-checkpoint.ipyn b - Rule-switch/
Individuals/ , Jupyter, 277 lines.ipynb_checkpoints/ regression_rs-checkpoint .ipynb - Rule-switch/
Individuals/ , Jupyter, 334 lines.ipynb_checkpoints/ selectivity_analysis_rs- checkpoint.ipynb - Rule-switch/
Individuals/ , Jupyter, 77 linesPCA_rs.ipynb - Rule-switch/
Individuals/ , Jupyter, 567 linescontrols.ipynb - Rule-switch/
Individuals/ , Jupyter, 277 linesregression_rs.ipynb - Rule-switch/
Individuals/ , Jupyter, 334 linesselectivity_analysis_rs. ipynb - Rule-switch/
Pseudo_pop/ , Jupyter, 75 lines.ipynb_checkpoints/ PCA_rs-checkpoint.ipynb - Rule-switch/
Pseudo_pop/ , Jupyter, 832 lines.ipynb_checkpoints/ gaussian_mixture_rs-chec kpoint.ipynb - Rule-switch/
Pseudo_pop/ , Jupyter, 170 lines.ipynb_checkpoints/ pseudopop_rs-checkpoint. ipynb - Rule-switch/
Pseudo_pop/ , Jupyter, 293 lines.ipynb_checkpoints/ regression_rs-checkpoint .ipynb - Rule-switch/
Pseudo_pop/ , Jupyter, 521 lines.ipynb_checkpoints/ selectivity_analysis_rs- checkpoint.ipynb - Rule-switch/
Pseudo_pop/ , Jupyter, 75 linesPCA_rs.ipynb - Rule-switch/
Pseudo_pop/ , Jupyter, 832 lines, 2 matchesgaussian_mixture_rs.ipyn b - Rule-switch/
Pseudo_pop/ , Jupyter, 163 linespseudopop_rs.ipynb - Rule-switch/
Pseudo_pop/ , Jupyter, 293 linesregression_rs.ipynb - Rule-switch/
Pseudo_pop/ , Jupyter, 521 linesselectivity_analysis_rs. ipynb - Simulation/
.ipynb_checkpoints/ , Jupyter, 1,907 linesmodel_response_switch-ch eckpoint.ipynb - Simulation/
.ipynb_checkpoints/ , Jupyter, 1,468 linesmodel_rule_switch-checkp oint.ipynb - Simulation/
.ipynb_checkpoints/ , Jupyter, 472 linesprediction_test-checkpoi nt.ipynb - Simulation/
model_response_switch.ip , Jupyter, 1,907 lines, 3 matchesynb - Simulation/
model_rule_switch.ipynb , Jupyter, 1,468 lines, 2 matches - Simulation/
prediction_test.ipynb , Jupyter, 472 lines - Stimulus-switch/
DATA/ , Jupyter, 55 lines.ipynb_checkpoints/ data_export_ss-checkpoin t.ipynb - Stimulus-switch/
DATA/ , Jupyter, 135 lines.ipynb_checkpoints/ edit_df_ss-checkpoint.ip ynb - Stimulus-switch/
DATA/ , Jupyter, 55 linesdata_export_ss.ipynb - Stimulus-switch/
DATA/ , Jupyter, 135 linesedit_df_ss.ipynb - repository limit reached (2,000 files or 30 MB): the rest is at the source (10 files)
- README.txt, Text, 21 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: HTissot42/
Sensorimotor-remapping-d rives-task-specializatio n
Read it in the paper: doi.org/10.1038/s41467-026-76104-3.
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:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 58 scripts, each with its path and the digest of its content;
- 21 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
- gin.g-node.org/
sreinert/ , at gin.g-node.org; found in “Data availability”category-learning_mpfc
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: gin.g-node.org/
sreinert/ category-learning_mpfc
Read it in the paper: doi.org/10.1038/s41467-026-76104-3.
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, 5 authors, 4 keywords, 7 MeSH terms, 2 funders, 49 references.
Cite
This paper
Tissot, H., Boucher, J., Reinert, S., Goltstein, P. M., & Boubenec, Y. (2026). Sensorimotor remapping drives task specialization in prefrontal cortex. Nature communications, 17(1), 9904. https://
BibTeX
@article{tissot2026senso
author = {Tissot, Hugo and Boucher, Jeff and Reinert, Sandra and Goltstein, Pieter M and Boubenec, Yves},
title = {{Sensorimotor remapping drives task specialization in prefrontal cortex}},
journal = {Nature communications},
year = {2026},
month = aug,
volume = {17},
number = {1},
pages = {9904},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/
url = {https://
pmid = {42749711},
pmcid = {PMC13582938}
}
RIS
TY - JOUR
AU - Tissot, Hugo
AU - Boucher, Jeff
AU - Reinert, Sandra
AU - Goltstein, Pieter M
AU - Boubenec, Yves
TI - Sensorimotor remapping drives task specialization in prefrontal cortex
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/
VL - 17
IS - 1
SP - 9904
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Sensorimotor remapping drives task specialization in prefrontal cortex",
"container-title": "Nature communications",
"author": [
{
"family": "Tissot",
"given": "Hugo"
},
{
"family": "Boucher",
"given": "Jeff"
},
{
"family": "Reinert",
"given": "Sandra"
},
{
"family": "Goltstein",
"given": "Pieter M"
},
{
"family": "Boubenec",
"given": "Yves"
}
],
"container-title-short":
"volume": "17",
"issue": "1",
"page": "9904",
"DOI": "10.1038/
"PMID": "42749711",
"PMCID": "PMC13582938",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
19
]
]
}
}
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-02333-w [code]
- Learning shapes neural geometry in the primate prefrontal cortex.Journal: Nature neuroscienceIn common: seaborn, scikit-learn, pandas, 3 other tools, non-human primate, 9 references
- [2] doi:10.1371/journal.pbio.3003831 [code]
- Disinhibitory signaling enables flexible coding of top-down information in cortical networks.Journal: PLoS biologyIn common: NetworkX, scikit-learn, pandas, 3 other tools, systems, 6 references
- [3] doi:10.1038/s41467-026-71725-0 [code]
- Interactions across hemispheres in prefrontal cortex reflect global cognitive processing.Journal: Nature communicationsIn common: scikit-learn, pandas, SciPy, 2 other tools, non-human primate, 6 references
- [4] doi:10.1016/j.isci.2026.117492 [code]
- Neural subspace reorganization reflects value-based decision-making.Journal: iScienceIn common: statsmodels, scikit-learn, pandas, 3 other tools, 5 references
- [5] doi:10.1038/s41467-026-74347-8 [code]
- Compositionality of social gaze in the prefrontal-amygdala circuits.Journal: Nature communicationsIn common: statsmodels, seaborn, scikit-learn, 4 other tools, non-human primate, systems, 3 references
- [6] doi:10.1371/journal.pcbi.1014162 [code]
- Exploring neural manifolds across a wide range of intrinsic dimensions.Journal: PLoS computational biologyIn common: scikit-learn, pandas, SciPy, 2 other tools, 5 references
- [7] doi:10.1016/j.celrep.2026.117420 [code]
- Neural population dynamics of direct electrical stimulation of neocortex.Journal: Cell reportsIn common: NetworkX, statsmodels, seaborn, 5 other tools, systems, 1 reference
- [8] doi:10.1038/s41540-026-00727-x [code]
- Association-sensory spatiotemporal hierarchy and functional gradient-regularised recurrent neural network with implications for schizophrenia.Journal: NPJ systems biology and applicationsIn common: statsmodels, seaborn, pandas, 3 other tools, 3 references
- [9] doi:10.7554/elife.109717 [code]
- Retrosplenial cortex enables context-dependent goal-directed sensorimotor transformation.Journal: eLifeIn common: statsmodels, seaborn, scikit-learn, 4 other tools, systems, 2 references
- [10] doi:10.1038/s41467-026-72057-9 [code]
- Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior.Journal: Nature communicationsIn common: statsmodels, seaborn, scikit-learn, 4 other tools, systems, 2 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: 1 repository of the authors' code, each at its verified commit and with its license, 58 scripts, and 21 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:427c9a9fbe5dc70e…
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.
