OSCR

Neuronal APOE4-induced early hippocampal network hyperexcitability in Alzheimer's disease pathogenesis.

Code ↔ Paper

The paper beside its authors' code: matches between them have not been computed for this paper yet.

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,554 lines · 62 KB · no license

  1. # %% [markdown]
  2. # <a href="https://colab.research.google.com/github/dtabuena/TabuenaJangGrone/blob/main/_Clustering.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
  3. # %%
  4. !pip install openpyxl
  5. !pip install XlsxWriter
  6. !pip install pingouin
  7. !pip install CMH
  8. !pip install svgutils
  9. from IPython.display import clear_output
  10. import pandas as pd
  11. import os
  12. import numpy as np
  13. from sklearn.decomposition import PCA
  14. from matplotlib import pyplot as plt
  15. import matplotlib as mpl
  16. import seaborn as sns
  17. import matplotlib
  18. clear_output()
  19. from sklearn.cluster import KMeans
  20. import statsmodels.api as sm
  21. from statsmodels.formula.api import ols
  22. from statsmodels.stats.multicomp import pairwise_tukeyhsd
  23. import pingouin as pg
  24. import scipy
  25. # from pandas.compat.numpy import np_array_datetime64_compat
  26. from random import sample
  27. from tqdm import tqdm
  28. import os
  29. import shutil
  30. from google import colab
  31. from svgutils.compose import *
  32. clear_output()
  33. def restore_order(arg_ord):
  34. return np.argsort(arg_ord)
  35. # %%
  36. import urllib
  37. response = urllib.request.urlretrieve('https://raw.githubusercontent.com/dtabuena/Resources/main/Matplotlib_Config/Load_FS6.py','Load_FS6.py')
  38. %run 'Load_FS6.py'
  39. # %%
  40. def get_files(link):
  41. my_drop_folder = 'my_drop_folder'
  42. zipped_file_path = "/content/"+my_drop_folder + ".zip"
  43. unzipped_file_path = "/content/"+my_drop_folder
  44. if not( os.path.exists(zipped_file_path)):
  45. !wget -O $zipped_file_path $link # download with new name
  46. !echo A | unzip $zipped_file_path -d $unzipped_file_path
  47. file_list = [f for r,d,f in os.walk("/content/"+my_drop_folder )][0]
  48. return file_list
  49. def read_data_file(xl_to_analyze,dir='./Prelim_2023/Cleaned_DataSets/'):
  50. my_df_dict = pd.read_excel(dir+xl_to_analyze, engine='openpyxl',index_col=None,sheet_name=None)
  51. NEW_my_df_dict = {}
  52. for k in my_df_dict.keys():
  53. new_k = k
  54. if 'cre' in new_k:
  55. new_k = k.replace('Syn1-cre','/Syn1-Cre')
  56. NEW_my_df_dict[new_k] = my_df_dict[k]
  57. return NEW_my_df_dict, xl_to_analyze
  58. def dict_to_df(my_df_dict,AP_cut=60):
  59. '''convert dict of categories into a DF'''
  60. for k in my_df_dict.keys():
  61. df = my_df_dict[k]
  62. df['type'] = k
  63. df_list = [my_df_dict[k] for k in my_df_dict.keys()]
  64. full_df = pd.concat(df_list,ignore_index=True)
  65. '''drop low AP amps'''
  66. for r in full_df.index:
  67. if full_df.loc[r,'AP amp'] <AP_cut:
  68. full_df.at[r,'AP amp'] = np.nan
  69. return full_df
  70. def clean_data(labeling,full_df):
  71. numericals = [c for c in full_df.columns if c not in labeling ]
  72. full_df_norm = full_df.copy()
  73. mean_dict = {}
  74. sd_dict = {}
  75. if 'extras' not in full_df.columns:
  76. exclude_extras = False
  77. plot_extras = False
  78. for c in full_df_norm.columns:
  79. if c not in labeling:
  80. col_mean = np.mean(full_df_norm[c])
  81. col_sd = np.std(full_df_norm[c])
  82. mean_dict[c] = col_mean
  83. sd_dict[c] = col_sd
  84. full_df_norm[c] = (full_df_norm[c] - col_mean)/col_sd
  85. null_check = pd.isna(full_df_norm)
  86. incomplete =[]
  87. for r in full_df_norm.index:
  88. vals = np.array(null_check.loc[r,numericals])
  89. bad = any(vals)
  90. if bad:
  91. incomplete.append(r)
  92. complete = [c for c in full_df_norm.index if c not in incomplete]
  93. full_df_no_null = full_df_norm.loc[complete].copy()
  94. if exclude_extras:
  95. is_extra = list(full_df_no_null['extra']==1)
  96. extra_ind = full_df_no_null.index[is_extra]
  97. full_df_no_null.drop( index =extra_ind, inplace=True)
  98. data_X = full_df_no_null[numericals].to_numpy()
  99. data_for_fit = data_X
  100. numericals = [n.replace('(1)', '') for n in numericals]
  101. return numericals, data_for_fit, full_df_no_null
  102. def get_types(full_df_no_null):
  103. type_list = list(full_df_no_null['type'])
  104. uniq_types = list(set(type_list))
  105. young_types = [t for t in uniq_types if '-9' in t and 'Cre' not in t]
  106. young_types.sort()
  107. cre_types = [t for t in uniq_types if 'Cre' in t]
  108. cre_types.sort()
  109. old_types = [t for t in uniq_types if '-19' in t and 'Cre' not in t]
  110. old_types.sort()
  111. uniq_types = young_types + cre_types + old_types
  112. # print(uniq_types)
  113. num_t = len(uniq_types)
  114. type_num = [ uniq_types.index(t) for t in type_list]
  115. return type_num, num_t, uniq_types,type_list
  116. def cluster_full(data_for_fit,xl_to_analyze,labels):
  117. random_state = 42
  118. if 'CA1' in xl_to_analyze:
  119. random_state = 47
  120. if 'Type I ' in xl_to_analyze:
  121. random_state = 43
  122. if 'Type II ' in xl_to_analyze:
  123. random_state = 42
  124. rheo_ind = [i for i in range(len(labels)) if 'Rheo' in labels[i]][0]
  125. # kmeans = KMeans(n_clusters=2, random_state=random_state).fit(data_for_fit)
  126. needs_flip = True
  127. while needs_flip:
  128. random_state+=1
  129. print('random_state',random_state)
  130. kmeans = KMeans(n_clusters=2, random_state=random_state,n_init=10).fit(data_for_fit)
  131. centers = kmeans.cluster_centers_.T
  132. needs_flip = np.diff(centers[rheo_ind])<0
  133. print(np.diff(centers[rheo_ind]))
  134. # centers = kmeans.cluster_centers_.T
  135. distances = kmeans.transform(data_for_fit)
  136. rel_centers = (centers.T - np.mean(centers.T,axis = 0)).T
  137. labels = kmeans.labels_
  138. rel_counts = [np.mean( l == labels ) for l in range(2)]
  139. # new_cent_order = np.argsort(centers[:,0]).tolist()
  140. new_cent_order = np.arange(len(centers)).tolist()
  141. total_D = np.tile(np.sum(distances,axis=1),[2,1]).T
  142. closeness = np.log(total_D/distances)
  143. closeness = (closeness - np.mean(closeness,axis=0)) / (np.std(closeness,axis=0))
  144. distances_norm = (distances - np.mean(distances,axis=0)) / (np.std(distances,axis=0))
  145. score = closeness[:,1] - closeness[:,0]
  146. packaged = {'features':numericals,
  147. 'kmeans':kmeans,
  148. 'centers':centers,
  149. 'distances':distances,
  150. 'rel_centers':rel_centers,
  151. 'rel_counts':rel_counts,
  152. 'total_D':total_D,
  153. 'closeness':closeness,
  154. 'distances_norm':distances_norm,
  155. 'score':-score,
  156. 'data_for_fit':data_for_fit,
  157. 'new_cent_order':new_cent_order
  158. }
  159. return packaged
  160. def do_stats(type_list,score):
  161. is_E3 = ['E3' in t for t in type_list ]
  162. is_9mo = ['-19' not in t for t in type_list ]
  163. age_list = ['7-9' if b else '17-19' for b in is_9mo]
  164. genotype = ['apoE3-KI' if b else 'apoE4-KI' for b in is_E3]
  165. is_crePos = ['cre+' in t for t in type_list ]
  166. is_creNeg = ['cre-' in t for t in type_list ]
  167. not_cree = [not any( [is_crePos[i], is_creNeg[i]] ) for i in range(len(is_crePos))]
  168. e34_genotype = [genotype[i] for i in range(len(not_cree)) if not_cree[i]]
  169. e34_score = [score[i] for i in range(len(not_cree)) if not_cree[i]]
  170. e34_age = [str(age_list[i])+'mo' for i in range(len(not_cree)) if not_cree[i]]
  171. genotype_list_str = [t.replace(' 7-9mo', '').replace(' 17-19mo', '') for t in type_list]
  172. age_list_str = [str(a)+'mo' for a in age_list]
  173. df_for_34_2W_anova = pd.DataFrame({'genotype': genotype_list_str, 'Age': age_list_str,'T1_Score': score})
  174. ANOVA2_results = pg.anova(dv='T1_Score', between=['genotype', 'Age'], data=df_for_34_2W_anova, detailed=True)
  175. # display(ANOVA2_results)
  176. ANOVA2_results.to_csv(f'{folder}/2W_results Age-Geno.csv')
  177. df_for_34_2W_anova['combination'] = df_for_34_2W_anova.genotype + " " + df_for_34_2W_anova.Age
  178. m_comp = pairwise_tukeyhsd(endog=df_for_34_2W_anova['T1_Score'], groups=df_for_34_2W_anova['combination'], alpha=0.05)
  179. tukey_data = pd.DataFrame(data=m_comp._results_table.data[1:], columns = m_comp._results_table.data[0])
  180. tukey_data = tukey_data.sort_values('p-adj',ascending=True)
  181. # display(tukey_data)
  182. tukey_data.to_csv(f'{folder}/pairwise Age-Genotype.csv')
  183. df_for_34_2W_anova.to_csv(f'{folder}/Scores.csv')
  184. return df_for_34_2W_anova, tukey_data
  185. def save_dl(folder):
  186. root_list = []
  187. f_list = []
  188. fold_name = folder
  189. fold_name = fold_name.replace(' ','_')
  190. zip_name= fold_name+'.zip'
  191. print(folder)
  192. !zip -r $zip_name $folder
  193. colab.files.download(zip_name)
  194. return None
  195. # %%
  196. def plot_summary(num_t,numericals,type_list,uniq_types,packaged,df_for_34_2W_anova,tukey_data,folder, clust_names = ['Hyper', 'Normal'],fig_opt = {'dpi': 300, 'format':'png','bbox_inches':None}):
  197. cmap = plt.cm.cool(np.linspace(0,1,2))*np.array([0.6]*3+[1])
  198. dark_cool = mpl.colors.ListedColormap(cmap)
  199. dpi=300
  200. kmeans = packaged['kmeans']
  201. centers = packaged['centers']
  202. distances = packaged['distances']
  203. rel_centers = packaged['rel_centers']
  204. rel_counts = packaged['rel_counts']
  205. total_D = packaged['total_D']
  206. closeness = packaged['closeness']
  207. distances_norm = packaged['distances_norm']
  208. score = packaged['score']
  209. data_for_fit = packaged['data_for_fit']
  210. cell_type = packaged['cell_type']
  211. type_num = packaged['type_num']
  212. colors = plt.cm.viridis(np.linspace(0,1,num_t))
  213. alpha_1 = 1
  214. alpha_2 = .8
  215. colors = [[4/255, 51/255, 255/255, alpha_1], [4/255, 51/255, 255/255, alpha_2],
  216. [255/255, 38/255, 0/255, alpha_1], [255/255, 38/255, 0/255, alpha_2],
  217. [103/255, 35/255, 2/255, alpha_1], [103/255, 35/255, 2/255, alpha_2],
  218. ]
  219. list_colors = [colors[i] for i in type_num ]
  220. color_dc={'apoE3-KI 7-9mo': colors[0],
  221. 'apoE3-KI 17-19mo': colors[1],
  222. 'apoE4-KI 7-9mo':colors[2],
  223. 'apoE4-KI 17-19mo': colors[3],
  224. 'fE4/Syn1-Cre+ 7-9mo': colors[4],
  225. 'fE4/Syn1-Cre- 7-9mo': colors[5],}
  226. ################# raw_data_map '''
  227. counts = [np.sum( [tt==t for tt in type_list]) for t in uniq_types ]
  228. counts[-1] = counts[-1] + np.sum(counts)*0.075
  229. fig_norm_vals, ax = plt.subplots(1,num_t+1,figsize = [7.25,1.6],gridspec_kw={'width_ratios': counts+[.75]},layout="constrained",dpi=dpi)
  230. data_X_min = np.min(data_for_fit)
  231. data_X_max = np.max(data_for_fit)
  232. extrema = np.array([-1,1]) * np.max(np.abs([data_X_min,data_X_max]))
  233. data_X_min = extrema[0]
  234. data_X_max = extrema[1]
  235. for ti in range(num_t):
  236. t = uniq_types[ti]
  237. t_colr = colors[ti]
  238. is_type = [ind for ind in range(len(type_num)) if type_num[ind]==ti]
  239. c = ax[ti].pcolor(data_for_fit.T[:,is_type], vmin=data_X_min*1.2, vmax=data_X_max*1.2)
  240. ax[ti].set_yticks(np.arange(len(numericals))+0.5)
  241. if ti ==0 : ax[ti].set_yticklabels(numericals,rotation=0)
  242. else: ax[ti].get_yaxis().set_visible(False)
  243. if ti==0: ax[ti].set_xlabel('Cell (#)')
  244. ax[ti].title.set_text(t.replace(' ','\n'))
  245. start = min(is_type)+3 / len(is_type)
  246. stop = max(is_type)-3 / len(is_type)
  247. fig_norm_vals.suptitle(cell_type+ ' Normalized Parameters',y=1.05,)# fontsize=6
  248. plt.colorbar(c, cax=ax[ti+1],label='z-scored'+'\n'+'values')
  249. plt.show()
  250. # plt.tight_layout()
  251. # plt.rcParams.update({'font.size': FS,'font.family': 'arial'}) #14
  252. ################# Centers
  253. new_cent_order = np.argsort(centers[:,1]).tolist()[::-1]
  254. fig_centers, ax = plt.subplots(1,1,figsize = [3,2],dpi=dpi,layout="constrained")
  255. ax.set_prop_cycle(color=[cmap[0], cmap[1]])
  256. ax.plot(centers[new_cent_order,:],'-o',linewidth=.5,markersize=2)
  257. ax.legend(clust_names,loc='lower center',frameon=False)
  258. sorted_numericals = [numericals[i] for i in new_cent_order]
  259. ax.set_xticks(np.arange(len(numericals)))
  260. ax.set_xticklabels(sorted_numericals,rotation=45,ha='right', rotation_mode='anchor')
  261. ax.set_ylabel('z-scored'+' '+'values')
  262. ax.axhline(0,color='k',linewidth=1)
  263. ax.title.set_text(cell_type +' '+'Cluster Centers')
  264. # plt.tight_layout()
  265. ################# Split Distance heat map
  266. counts = [np.sum( [tt==t for tt in type_list]) for t in uniq_types ]
  267. print(counts)
  268. b_dim = 2
  269. fig_distances_split, ax = plt.subplots(1,num_t+1,figsize = [7.25,.9],gridspec_kw={'width_ratios': counts+[.75]},layout="constrained",dpi=dpi) ## NEw
  270. dist_min = np.min(distances_norm)
  271. dist_max = np.max(distances_norm)
  272. extrema = np.array([-1,1]) * np.max(np.abs([dist_min,dist_max]))
  273. dist_min = extrema[0]
  274. dist_max = extrema[1]
  275. # plt.rcParams.update({'font.size': FS,'font.family': 'arial'}) #14
  276. for ti in range(num_t):
  277. t = uniq_types[ti]
  278. t_colr = colors[ti]
  279. is_type = [ind for ind in range(len(type_num)) if type_num[ind]==ti]
  280. c = ax[ti].pcolor(distances_norm.T[:,is_type], vmin=dist_min*1.2, vmax=dist_max*1.2)
  281. ax[ti].set_yticks(np.arange(len(clust_names))+0.5)
  282. clust_names_nl = [c.replace(' ',' ') for c in clust_names]
  283. if ti ==0 : ax[ti].set_yticklabels(clust_names_nl,rotation=0)
  284. else: ax[ti].get_yaxis().set_visible(False)
  285. if ti==0: ax[ti].set_xlabel('Cell (#)')
  286. # fig_distances_split.tight_layout()
  287. fig_distances_split.colorbar(c, cax=ax[ti+1],label='z-scored'+'\n'+'distance')
  288. for ti in range(num_t):
  289. t = uniq_types[ti]
  290. ax[ti].text(0.5, 1, t.replace(' ','\n'),transform=ax[ti].transAxes,ha='center',rotation=0,va='bottom') # ,fontsize=14
  291. # fig_distances_split.tight_layout()
  292. plt.show()
  293. ############# Combined Scatter
  294. # plt.rcParams.update({'font.size': FS,'font.family': 'arial'}) #14
  295. fig_comb_scatter, ax = plt.subplots(1,figsize = [7.25/5,7.25/5],dpi=dpi,layout="constrained")
  296. for ti in range(num_t):
  297. t = uniq_types[ti]
  298. t_colr = color_dc[t]
  299. print(t,t_colr)
  300. t_colr_edge = t_colr[:-1] + [alpha_1]
  301. t_colr_edge = None
  302. t_colr = 'k'
  303. is_type = [ind for ind in range(len(type_num)) if type_num[ind]==ti]
  304. ax.scatter(distances_norm[is_type,0],distances_norm[is_type,1],color=t_colr, label=t,linewidth=1,edgecolor=t_colr_edge,s=2,zorder=1)
  305. # ax.legend(bbox_to_anchor=(1.04, 1), loc="upper left")
  306. ax.set_xlabel('Dist. from '+clust_names[0]+' Center \n (z-scored)')
  307. ax.set_ylabel('Dist. from '+clust_names[1]+' Center \n (z-scored)')
  308. ax.plot([0, 1], [0, 1], transform=ax.transAxes,color='k',linewidth=1)
  309. ax.text(0.98, 0.02, ''+clust_names[1],transform=ax.transAxes,ha='right',va='bottom') # ,fontsize=12
  310. ax.text(0.02, 0.98, ''+clust_names[0],transform=ax.transAxes,ha='left',va='top') # ,fontsize=12
  311. comb_xlim = ax.get_xlim()
  312. comb_ylim = ax.get_ylim()
  313. common_lim = np.array([-1,1]) * np.max(np.abs(list(comb_xlim)+list(comb_ylim)))
  314. common_lim= [np.min([comb_xlim[0],comb_ylim[0]]), np.max([comb_xlim[1],comb_ylim[1]]) ]
  315. ax.set_xlim(common_lim)
  316. ax.set_ylim(common_lim)
  317. # residuals = np.diff(distances_norm,axis=1)
  318. x=distances_norm[:,0]
  319. y=distances_norm[:,1]
  320. for i in range(len(x)):
  321. if x[i]>y[i]:
  322. c=[.6, 0, .6]
  323. else :
  324. c=[0, .6, .6]
  325. plt.plot([x[i], x[i]], [y[i], x[i]], color=c, linewidth=0.5,zorder=-1)
  326. #### separate Scatter #####
  327. b_dim = 3
  328. print(common_lim)
  329. common_lim =common_lim+np.array([0,2])
  330. """ for compact scatter Layout """
  331. if 'CA3' in cell_type: fig_sep_scatter, axs = plt.subplots(3,int(num_t/3),figsize = [7.25/4,7.25/4*1.5*1.2],dpi=dpi,layout="constrained") #
  332. else: fig_sep_scatter, axs = plt.subplots(2,int(num_t/2),figsize =[7.25/4,7.25/4*1.2],dpi=dpi,layout="constrained") # [int(num_t/2)*b_dim,b_dim*2]
  333. axs = axs.T.flatten()
  334. if 'CA3' in cell_type:
  335. axs = axs[np.arange(6)[[0,1,2,5,3,4]]]
  336. for ti in np.arange(num_t):
  337. t = uniq_types[ti]
  338. t_colr = color_dc[t]
  339. t_colr_edge = t_colr[:-1] + [alpha_1]
  340. t_colr_edge = None
  341. is_type = [ind for ind in range(len(type_num)) if type_num[ind]==ti]
  342. axs[ti].scatter(distances_norm[is_type,0],distances_norm[is_type,1],color=t_colr, label=t,linewidth=1,edgecolor=t_colr_edge,s=2)
  343. axs[ti].plot([0, 1], [0, 1], transform=axs[ti].transAxes,color=[0,0,0,1],linewidth=1)
  344. axs[ti].title.set_text(t.replace(' ','\n').replace('apo',""))
  345. axs[ti].set_xlim(common_lim)
  346. axs[ti].set_ylim(common_lim)
  347. axs[ti].text(0.98, 0.02, ''+clust_names[1],transform=axs[ti].transAxes,ha='right',va='bottom')
  348. axs[ti].text(0.02, 0.98, ''+clust_names[0],transform=axs[ti].transAxes,ha='left',va='top')
  349. fig_sep_scatter.subplots_adjust(wspace=0.3, hspace=0.3)
  350. fig_sep_scatter.supxlabel(f'{clust_names[0]} Distance', x=0.60,y=.05)
  351. fig_sep_scatter.supylabel(f'{clust_names[1]} Distance', x=0.050)
  352. plt.tight_layout()
  353. ######## ''' Violins of Residual Plots '''
  354. if 'CA3' in cell_type: fig_violin_residuals, ax = plt.subplots(1,figsize=np.array([7.25*.3,2.25]),dpi=dpi,layout="constrained")
  355. else: fig_violin_residuals, ax = plt.subplots(1,figsize=np.array([7.25*.7/3,2.25]),dpi=dpi,layout="constrained")
  356. comb_list = list(set(df_for_34_2W_anova['combination']))
  357. young_types = [t for t in comb_list if '-9' in t and 'Cre' not in t]
  358. young_types.sort()
  359. cre_types = [t for t in comb_list if 'Cre' in t]
  360. cre_types.sort()
  361. old_types = [t for t in comb_list if '-19' in t and 'Cre' not in t]
  362. old_types.sort()
  363. comb_list = young_types + cre_types + old_types
  364. sns_dict={}
  365. for ti in range(len(comb_list)):
  366. type_i = comb_list[ti]
  367. match_type = df_for_34_2W_anova['combination']==type_i
  368. y_vals = list(np.array(df_for_34_2W_anova[match_type]['T1_Score']))
  369. x_vals = np.ones_like(y_vals)*ti + int(np.floor(ti/2))
  370. pos = list(np.ones_like(y_vals)*ti)
  371. t_colr = colors[ti]
  372. t_colr = color_dc[type_i]
  373. sns_dict[type_i] =y_vals
  374. sns_df = pd.DataFrame.from_dict(sns_dict, orient='index').T
  375. colors_sns = [color_dc[t] for t in sns_df.columns]
  376. colors_sns = [ list(np.array(c[:-1]) + (1 - np.array(c[:-1]))* (1-c[-1])) + list(c[-1:]) for c in colors_sns]
  377. sns.swarmplot(ax=ax,data=sns_df,palette=colors_sns, label=t,linewidth=.05,edgecolor='k',size=2) # color='k'
  378. sns.violinplot(ax=ax,data=sns_df,palette=colors_sns,scale ='area', label=t, linewidth=1,inner=None)
  379. # Manually adjust the violins
  380. violins = [v for v in ax.collections if isinstance(v, matplotlib.collections.PolyCollection)] # Get all violins
  381. for i, violin in enumerate(violins):
  382. violin.set_facecolor([0.9, 0.9, 0.9]) # Set the fill color to light gray
  383. violin.set_edgecolor(colors_sns[i % len(colors_sns)]) # Set edge color using colors_sns
  384. violin.set_linewidth(1) # Set the linewidth to make the edges more visible
  385. ax.text(0, 1, clust_names[0],transform=ax.transAxes,ha='right',va='bottom',color=[0, .6, .6])
  386. ax.text(0, 0, clust_names[1],transform=ax.transAxes,ha='right',va='top',color=[.6, 0, .6])
  387. ax.set_ylabel('Residual Value')
  388. ax.set_ylim(-7.5,12.5)
  389. ax.axhline(0,color='k',linewidth=.5)
  390. ax_labels = sns_df.columns
  391. ax.set_xticks(ticks=range(len(ax_labels)))
  392. ax_labels_r = [c.replace(' ','\n') for c in ax_labels]
  393. ax_labels_r = [c.replace('\nsyn',' syn') for c in ax_labels_r]
  394. ax_labels_r = [c.split('\n')[0] for c in ax_labels_r]
  395. ax_labels_r = [c.replace('fE4/Syn1-Cre', 'Cre') for c in ax_labels_r]
  396. ax_labels_r = [c.replace('apo', '') for c in ax_labels_r]
  397. # ax_labels_r = ['\n'+ax_labels_r[i] if i%2 == 0 else ax_labels_r[i] for i in range(len(ax_labels_r)) ]
  398. # ax.set_xticklabels(ax_labels_r,rotation=60,ha='right', va='top', rotation_mode='anchor')
  399. ax.set_xticklabels(ax_labels_r,rotation=0,ha='center', va='top', rotation_mode='anchor')
  400. ax.title.set_text(cell_type) #+' Cluster Membership'
  401. div_x = np.sum( ['7-9' in l for l in ax_labels])-0.5
  402. segment_x = [div_x, div_x]
  403. segment_y = ax.get_ylim()*np.array([1,1])
  404. ll = plt.plot(segment_x,segment_y,':k',linewidth=.5) # returns a sequence of line objects
  405. ll[0].set_clip_on(False)
  406. month_offset = 0 # 10
  407. plt.text(div_x/2-0.25, segment_y[0]-month_offset, '7-9mo',ha='center',va='bottom')
  408. plt.text(div_x+1, segment_y[0]-month_offset, '17-19mo', ha='center',va='bottom')
  409. print('sns_dict',sns_dict)
  410. cre_ind = [i for i,k in enumerate(sns_dict.keys()) if 'Cre' in k]
  411. if len(cre_ind)>0:
  412. x = np.mean(cre_ind)
  413. plt.text(x, segment_y[0]-4, 'fE4/Syn-Cre',ha='center',va='bottom')
  414. else:
  415. plt.text(1, segment_y[0]-4, 'PLACE HOLDER' ,ha='center',va='bottom', color = 'white')
  416. base_offset = 1.5
  417. y0 = np.max(df_for_34_2W_anova['T1_Score'])*base_offset
  418. print('y0',y0)
  419. labels = comb_list
  420. true_tuckey = tukey_data[tukey_data['reject']==True]
  421. astk_range = [0.05,0.01,0.001,0.0001]
  422. for r in true_tuckey.index:
  423. comps = ['apoE3-KI 7-9mo']
  424. if true_tuckey.loc[r,'group1'] in comps or true_tuckey.loc[r,'group2'] in comps:
  425. start_l = true_tuckey.loc[r,'group1']
  426. stop_l = true_tuckey.loc[r,'group2']
  427. start_x = labels.index(start_l)
  428. stop_x = labels.index(stop_l)
  429. y0 = np.max(df_for_34_2W_anova['T1_Score'])*base_offset - 1
  430. y_offset = abs((stop_x-start_x))
  431. y = y0+ y_offset * 1.25 + np.min([stop_x,start_x])*.25
  432. half_dx = abs(stop_x-start_x)/2
  433. ax.errorbar( np.mean([stop_x,start_x]), y,yerr=0, xerr=half_dx,color='k',capsize=.75,linewidth=0.75,capthick=0.75)
  434. cent =np.mean([start_x,stop_x])
  435. num_ask = np.sum([true_tuckey.loc[r,'p-adj'] < t for t in astk_range] )
  436. plt.text(cent,y, '*' * num_ask,ha='center',va='center',fontsize = 2+plt.rcParams['font.size'])
  437. if 'Syn' in true_tuckey.loc[r,'group1'] and 'Syn' in true_tuckey.loc[r,'group2']:
  438. start_l = true_tuckey.loc[r,'group1']
  439. stop_l = true_tuckey.loc[r,'group2']
  440. start_x = labels.index(start_l)
  441. stop_x = labels.index(stop_l)
  442. y0 = np.max(df_for_34_2W_anova['T1_Score'])*base_offset
  443. y_offset = abs((stop_x-start_x))
  444. y = y0+ y_offset * .75
  445. half_dx = abs(stop_x-start_x)/2
  446. ax.errorbar( np.mean([stop_x,start_x]), y,yerr=0, xerr=half_dx,color='k',capsize=.75,linewidth=0.75,capthick=0.75)
  447. cent =np.mean([start_x,stop_x])
  448. num_ask = np.sum([true_tuckey.loc[r,'p-adj'] < t for t in astk_range] )
  449. print(true_tuckey.loc[r,'p-adj'],num_ask)
  450. plt.text(cent,y, '*' * num_ask,ha='center',va='center',fontsize = 2+plt.rcParams['font.size'])
  451. plt.tight_layout()
  452. fig_norm_vals.savefig(f'{folder}/Normalized_Vals_split.'+fig_opt['format'],**fig_opt)
  453. fig_violin_residuals.savefig(f'{folder}/fig_violin_residuals.'+fig_opt['format'],**fig_opt)
  454. fig_distances_split.savefig(f'{folder}/fig_distances_split.'+fig_opt['format'],**fig_opt)
  455. fig_sep_scatter.savefig(f'{folder}/GenoType_dist_Separate.'+fig_opt['format'],**fig_opt)
  456. fig_comb_scatter.savefig(f'{folder}/GenoType_dist.'+fig_opt['format'],**fig_opt)
  457. return None
  458. # %%
  459. import matplotlib.pyplot as plt
  460. import numpy as np
  461. from matplotlib.patches import FancyArrow
  462. def fig_cartoon_x(fig_opt={'dpi': 300, 'format': 'svg', 'bbox_inches': None}):
  463. FS = 8
  464. A = np.random.randn(16, 80)
  465. B = np.random.randn(2, 80)
  466. B_t = np.random.randn(16, 2)
  467. fig_cartoon, ax = plt.subplots(3, 4, figsize=[7.25 * .85, .5], width_ratios=[6, 3, .2, 6], height_ratios=[4, 2, 4], dpi=300, layout="constrained")
  468. # Hide the original axes
  469. for a in ax.reshape(-1):
  470. a.set_visible(False)
  471. # Get the gridspec to modify the layout
  472. gs = ax[0, 0].get_gridspec()
  473. # Create the large axis for the heatmap A
  474. big_ax_1 = fig_cartoon.add_subplot(gs[0:, 0])
  475. ch = big_ax_1.pcolorfast(A)
  476. cbar = plt.colorbar(ch)
  477. cbar.set_ticks([])
  478. cbar.set_label('z-score')
  479. big_ax_1.set_xlabel('Pooled CA3 Cells', fontsize=FS)
  480. big_ax_1.set_ylabel('16 Parameters', rotation=0, ha='right', va='center', fontsize=FS)
  481. big_ax_1.xaxis.set_label_position('top')
  482. big_ax_1.set_xticks([])
  483. big_ax_1.set_yticks([])
  484. # Create an axis for B (second row, fourth column)
  485. ax[1, 3].set_visible(True)
  486. ax[1, 3].pcolorfast(B)
  487. ax[1, 3].set_ylabel('k', rotation=0, ha='right', va='center', fontsize=FS)
  488. ax[1, 3].set_title('Cell Distances', rotation=0, ha='center', va='center', fontsize=FS)
  489. ax[1, 3].set_xticks([])
  490. ax[1, 3].set_yticks([])
  491. # Create the third large axis for B_t
  492. big_ax_2 = fig_cartoon.add_subplot(gs[0:, 2])
  493. big_ax_2.pcolorfast(B_t)
  494. big_ax_2.set_xlabel('k', rotation=0, ha='center', va='center', fontsize=FS)
  495. big_ax_2.set_ylabel('Centroid\n Parameters (16)', rotation=90, ha='center', va='bottom', fontsize=FS)
  496. big_ax_2.set_xticks([])
  497. big_ax_2.set_yticks([])
  498. # Create a new axis for the blocky arrow, spanning only the second column
  499. arrow_ax = fig_cartoon.add_subplot(gs[:, 1]) # this spans the whole second column
  500. arrow_ax.set_xticks([])
  501. arrow_ax.set_yticks([])
  502. arrow_ax.set_frame_on(False) # Hide the axis frame
  503. arrow_ax.set_xlim(-.0,1.2)
  504. # Add the blocky arrow on the new axis
  505. x_start, y_start = 0, 0.5 # Arrow starting position in axes coordinates
  506. dx, dy = 0.6, 0 # Arrow length (horizontal)
  507. arrow = FancyArrow(x_start, y_start, dx, dy, width=.35, head_width=0.75, head_length=0.15, color='k', transform=arrow_ax.transAxes)
  508. arrow_ax.add_patch(arrow)
  509. # Add label for the arrow
  510. arrow_ax.annotate('k-means', xy=(x_start + dx / 2, y_start), ha='center', va='center', xycoords='axes fraction', color='w', fontsize=FS)
  511. # Save the figure
  512. fig_cartoon.savefig('./fig_cartoon_x.' + fig_opt['format'], **fig_opt)
  513. return None
  514. # %%
  515. def cross_val(data_for_fit,numericals,new_cent_order,folder,k=2,
  516. num_iter = 1000,SubSampleFrac = .8,cell_type='',fig_opt = {'dpi': 300, 'format':'svg','bbox_inches':None},ds_iter=10):
  517. cval_res = {}
  518. center_list = []
  519. label_list = []
  520. sub_sample_ind_list = []
  521. full_label_list = []
  522. pred_all = True
  523. # sorted_numericals = [numericals[i] for i in new_cent_order]
  524. FS=6
  525. dpi=300
  526. # set_font_all(FS)
  527. cmap = plt.cm.cool(np.linspace(0,1,k))*np.array([0.6]*3+[1])
  528. dark_cool = mpl.colors.ListedColormap(cmap)
  529. for ni in tqdm( range(num_iter) ):
  530. sub_sample_ind = []
  531. for t in uniq_types:
  532. t_inds = [i for i in range(len(type_list)) if t in type_list[i]]
  533. # print(t_inds)
  534. sub_sample_ind.extend( sample(t_inds, int(len(t_inds)*SubSampleFrac) ) )
  535. kmeans_iter = KMeans(n_clusters=k, random_state=42,n_init=10).fit(data_for_fit[sub_sample_ind][:,new_cent_order])
  536. centers_iter = kmeans_iter.cluster_centers_.T
  537. centers_iter = centers_iter - np.mean(centers_iter,axis=0)
  538. labels_iter = kmeans_iter.labels_
  539. if pred_all:
  540. labels_iter = kmeans_iter.predict(data_for_fit)
  541. if ni>0:
  542. corco = np.corrcoef(centers_iter.T,center_list[0].T)
  543. corco = corco[0:k,k:]
  544. ind = np.argmax(corco,axis=0)
  545. centers_iter = centers_iter[:,ind]
  546. labels_iter = [ind[l] for l in labels_iter]
  547. if not pred_all:
  548. full_label_iter = np.empty((data_for_fit.shape[0],))
  549. full_label_iter[:] = np.nan
  550. full_label_iter[sub_sample_ind] = labels_iter
  551. else:
  552. full_label_iter = labels_iter
  553. center_list.append(centers_iter)
  554. label_list.append(labels_iter)
  555. sub_sample_ind_list.append(sub_sample_ind_list)
  556. full_label_list.append(full_label_iter)
  557. center_stack = np.stack(center_list)
  558. mean_center = np.mean(center_stack,0)
  559. nintey_p_center = np.percentile(center_stack, [.5, 99.5], axis=0)
  560. cval_res['mean_center'] = mean_center
  561. cval_res['confidence'] = nintey_p_center
  562. plot_order = np.argsort(np.diff(mean_center,1).T)[0]
  563. numericals=np.array(numericals)
  564. ####### PLOT CENTERS
  565. plt.rcParams.update({'font.size': FS,'font.family': 'arial'}) #14
  566. fig_cv_cent, ax = plt.subplots(1,figsize = (7.25/4,1.8),dpi=dpi,layout="constrained")
  567. handle_list = []
  568. if k == 2:
  569. clust_names = [ 'Hyper', 'Normal']
  570. rheo_index_in_center = [i for i in range(len(numericals)) if 'Rheo' in numericals[i]]
  571. rheo_dif = np.diff(mean_center[rheo_index_in_center,:])[0]
  572. if rheo_dif<0:
  573. clust_names=clust_names[::-1]
  574. cmap=np.flipud(cmap)
  575. for ki in range(k):
  576. if k==2: ax.plot(mean_center[plot_order,ki].T,'-o',color=cmap[ki],linewidth=1,label = clust_names[ki],markersize=2)
  577. else: ax.plot(mean_center[plot_order,ki].T,'-o',color=cmap[ki],linewidth=.5,markersize=2)
  578. ax.plot(nintey_p_center[0,plot_order,ki].T,':',color=cmap[ki],linewidth=.5)
  579. ax.plot(nintey_p_center[1,plot_order,ki].T,':',color=cmap[ki],linewidth=.5)
  580. ax.set_xticks(np.arange(len(numericals)))
  581. ax.set_xticklabels(numericals[plot_order],rotation=45,ha='right',va='center', rotation_mode='anchor',fontsize=plt.rcParams['font.size']) # ,fontsize=14
  582. ax.axhline(0, color='k',linewidth=.5)
  583. if k == 2: ax.legend(loc='upper center',frameon=False,fontsize=plt.rcParams['font.size'])
  584. ax.set_ylabel('z-scored'+' '+'values')
  585. full_label_stack = np.stack(full_label_list)
  586. pseudo_truth = scipy.stats.mode(full_label_stack,axis=0, nan_policy='omit')[0]
  587. is_match = (full_label_stack == pseudo_truth)*1. # convert to float so can accept nans
  588. is_match[np.isnan(full_label_stack)]=np.nan
  589. consistency_iter = np.nanmean(is_match, axis = 1)
  590. mean_consistency_iter = np.nanmean(consistency_iter)
  591. std_consistency_iter = np.nanstd(consistency_iter)
  592. consistency_cell = np.nanmean(is_match, axis = 0)
  593. mean_consistency_cell = np.nanmean(consistency_cell)
  594. sd_consistency_cell = np.nanstd(consistency_cell)
  595. consistency_report = 'label consistency: ' + str(round(mean_consistency_cell*100,1)) + '% +- ' + str(round(sd_consistency_cell*100,2)) + ' (chance: ' + str(round(1/k*100,1))+')'
  596. ax.title.set_text(cell_type)
  597. cval_res['mean_consistency_iter'] = mean_consistency_iter
  598. cval_res['std_consistency_iter'] = std_consistency_iter
  599. cval_res['consistency_cell'] = consistency_cell
  600. cval_res['mean_consistency_cell'] = mean_consistency_cell
  601. plt.tight_layout()
  602. fig_cv_cent.tight_layout()
  603. fig_cv_cent.patch.set_facecolor('white')
  604. plt.show()
  605. fig_cv_cent.savefig(f'{folder}/CrossVal_Data k={k} Centers.'+fig_opt['format'],**fig_opt)
  606. ################## Plot iteration results
  607. scale_factor= 1
  608. plt.rcParams.update({'font.size': FS*scale_factor, 'font.family': 'arial'})
  609. fig_cv_prob, ax = plt.subplots(2,2,figsize=np.array([7.25/4,1.8]),
  610. gridspec_kw={'height_ratios': [3, 1],'width_ratios': [4, 1.1]} ,dpi=dpi,layout="constrained")
  611. ax = ax.T.flatten()
  612. iters_ds = np.arange(0, num_iter + 1, ds_iter)
  613. cell_ids = np.arange(0, full_label_stack.shape[1] + 1) # **Use shape[1] for cells**
  614. pcol = ax[0].pcolorfast(cell_ids, iters_ds, full_label_stack[::ds_iter,:], cmap=dark_cool) # **Swap arguments**
  615. ax[0].set_xlabel('Cell (#)\n',va='bottom')
  616. ax[0].set_ylabel('Iteration (#)\n',va='bottom',rotation = 90)
  617. ax[0].xaxis.set_label_position('top')
  618. # ax[0].set_xticks([])
  619. ax[0].xaxis.tick_top()
  620. ax[1].scatter(np.arange(len(consistency_cell)),consistency_cell*100,color='k',s=1,edgecolor='k',marker='.')
  621. ax[1].axhline(1/k*100,color='k',linestyle=':',linewidth=.25)
  622. ax[1].set_ylabel('Cell\nConsistency (%)',va='bottom',rotation = 90)
  623. ax[1].set_ylim((0,102))
  624. ax[1].set_xlim((0,is_match.shape[1]))
  625. ax[1].set_xlabel('Cell (#)')
  626. ax[2].scatter(consistency_iter*100,np.arange(num_iter),color='k',s=1,edgecolor='k',marker='.')
  627. ax[2].axvline(1/k*100,color='k',linestyle=':',linewidth=.25)
  628. ax[2].set_xlabel('Iteration\nConsistency (%)',va='bottom')
  629. ax[2].set_xlim((102,0))
  630. ax[2].set_ylim((0,num_iter))
  631. ax[2].yaxis.tick_right()
  632. ax[2].xaxis.set_label_position('top')
  633. ax[2].xaxis.tick_top()
  634. # fig.delaxes(ax[3])
  635. ax[3].errorbar(mean_consistency_iter*100, mean_consistency_cell*100, yerr=sd_consistency_cell*100, xerr=std_consistency_iter*100,color='k',capsize=.75,linewidth=0.5,capthick=0.5)
  636. ax[3].axvline(1/k*100,color='k',linestyle=':',linewidth=.25)
  637. ax[3].axhline(1/k*100,color='k',linestyle=':',linewidth=.25)
  638. ax[3].set_xlim((100,0))
  639. ax[3].set_ylim((0,100))
  640. ax[3].yaxis.tick_right()
  641. # fig_cv_prob.suptitle(f'k = {k} clusters', y=1.3,fontsize=plt.rcParams['font.size'])# y=1.075
  642. # fig_cv_prob.tight_layout(pad=.1)
  643. # fig_cv_prob.set_figwidth(1.8)
  644. # fig_cv_prob.set_figheight(1.6)
  645. ax[0].set_xticks([])
  646. fig_cv_prob.patch.set_facecolor('white')
  647. plt.show()
  648. plt.tight_layout()
  649. fig_cv_prob.savefig(f'{folder}/CrossVal_Data k={k} Labeling.'+fig_opt['format'],**fig_opt)
  650. return cval_res
  651. # %%
  652. ### Git clone data tables for sorting ###
  653. try: shutil.rmtree('./'+git_rep)
  654. except: None
  655. git_rep = 'Prelim_2023'
  656. git_link = 'https://github.com/dtabuena/'+git_rep+'/'
  657. !git clone $git_link
  658. file_list = [f for f in [f for r,d,f in os.walk('./'+git_rep+'/Cleaned_DataSets/' )][0] if '.xlsx' in f]
  659. print(file_list)
  660. mpl.font_manager.fontManager.addfont('./'+git_rep+'/Cleaned_DataSets/arial.ttf')
  661. # %%
  662. fig_cartoon_x(fig_opt={'dpi': 300, 'format':'svg','bbox_inches':None})
  663. # %%
  664. ### Cycle throught Data tables, cluster, and plot. ###
  665. multipack={}
  666. fig_opt = {'dpi': 300, 'format':'svg','bbox_inches':None}
  667. for f in file_list: # [f for f in file_list if 'CA3' in f]:#
  668. my_df_dict, xl_to_analyze = read_data_file(f,dir='./'+git_rep+'/Cleaned_DataSets/')
  669. folder = xl_to_analyze.split('.')[0].replace(' ','_')
  670. try: os.makedirs(folder)
  671. except: None
  672. full_df = dict_to_df(my_df_dict,AP_cut=60)
  673. labeling = ['cell','Cell','type','extra']
  674. numericals, data_for_fit, full_df_no_null = clean_data(labeling, full_df)
  675. type_num, num_t, uniq_types,type_list = get_types(full_df_no_null)
  676. packaged = cluster_full(data_for_fit,xl_to_analyze,numericals)
  677. packaged['type_num'] = type_num
  678. if 'CA1' in f: packaged['cell_type'] ='CA1 PC'
  679. if 'CA3' in f: packaged['cell_type'] ='CA3 PC'
  680. if 'II' in f: packaged['cell_type'] ='DGC II'
  681. if ' I ' in f: packaged['cell_type'] ='DGC I'
  682. df_for_34_2W_anova, tukey_data = do_stats(type_list,packaged['score'])
  683. plot_summary(num_t,numericals,type_list,uniq_types,packaged,df_for_34_2W_anova,tukey_data,folder,fig_opt =fig_opt)
  684. k2_cval = cross_val(data_for_fit,numericals,packaged['new_cent_order'],folder,k=2,num_iter = 1000,SubSampleFrac = .8,
  685. cell_type=packaged['cell_type'],fig_opt =fig_opt,ds_iter=1)
  686. k3_cval = cross_val(data_for_fit,numericals,packaged['new_cent_order'],folder,k=3,num_iter = 1000,SubSampleFrac = .8,
  687. cell_type=packaged['cell_type'],fig_opt =fig_opt,ds_iter=1)
  688. packaged['k2_cval'] = k2_cval
  689. packaged['k3_cval'] = k3_cval
  690. multipack[packaged['cell_type']] = packaged
  691. save_dl(folder)
  692. # %%
  693. # %%
  694. ########### Write K-Means Values ################
  695. all_dfs = dict()
  696. for celltype, data in multipack.items():
  697. # print(celltype)
  698. # print(data.keys())
  699. # print(data['features'])
  700. # print(data['k2_cval'])
  701. # Create dictionary with features column
  702. df_dict = {'features': data['features']}
  703. # Add a column for each cluster center
  704. for i in range(data['centers'].shape[1]):
  705. df_dict[f'full_centroid_K{i}'] = data['centers'][:, i]
  706. means = data['k2_cval']['mean_center']
  707. for i in range(means.shape[1]):
  708. df_dict[f'mean_centroid_K{i}'] = means[:, i]
  709. confidence = data['k2_cval']['confidence']
  710. for i in range(confidence.shape[2]): # Iterate over clusters (last dimension)
  711. df_dict[f'confidence_low_K{i}'] = confidence[0, :, i] # Low bound
  712. df_dict[f'confidence_high_K{i}'] = confidence[1, :, i] # High bound
  713. feat_df = pd.DataFrame(df_dict)
  714. feat_df = feat_df.sort_values('full_centroid_K0', ascending=False)
  715. feat_df = feat_df.rename(columns=lambda x: x.replace('K0', 'Hyper').replace('K1', 'Normal'))
  716. all_dfs[celltype] = feat_df
  717. # Write to multi-tabbed Excel file
  718. with pd.ExcelWriter('kmeans_results.xlsx', engine='openpyxl') as writer:
  719. for celltype, df in all_dfs.items():
  720. df.to_excel(writer, sheet_name=celltype, index=False)
  721. print("Excel file 'kmeans_results.xlsx' created successfully!")
  722. colab.files.download('kmeans_results.xlsx')
  723. # %%
  724. ############### Combined Centers Plot ####################
  725. FS=6
  726. # set_font_all(FS)
  727. dpi=300
  728. num_types = len(multipack)
  729. fig_combined_centers, ax = plt.subplots(1,1,figsize=np.array([7.25,1.6])*1,dpi=dpi,layout="constrained")
  730. cmap = plt.cm.cool(np.linspace(0,1,2))*np.array([0.6]*3+[1])
  731. dark_cool = mpl.colors.ListedColormap(cmap)
  732. # cent_ord = multipack['CA3 PC']['new_cent_order']
  733. features = np.array(multipack['CA3 PC']['features'])
  734. mean_center = multipack['CA3 PC']['k2_cval']['mean_center']
  735. plot_order = np.argsort(np.diff(mean_center,1).T)[0]
  736. keys = multipack.keys()
  737. keys = ['CA3 PC', 'DGC II', 'DGC I', 'CA1 PC' ]
  738. for ci in range(num_types):
  739. # ci_type = list(multipack.keys())[ci]
  740. ci_type = keys[ci]
  741. centers = multipack[ci_type]['centers']
  742. bar_wid = 1/(num_types*2+2)
  743. x = np.arange(len(features))
  744. m_color = cmap[0,:]*[1,1,1,(ci+1)/4]
  745. m_ys = centers[:,0]
  746. m_label = ci_type + ' - Hyper'
  747. m_x = x + (ci+1)*bar_wid - 0.5
  748. ax.bar(m_x,m_ys[plot_order],color=m_color,edgecolor='k',width=bar_wid,label=m_label,linewidth=.5)
  749. l_color = cmap[1,:]*[1,1,1,(ci+1)/4]
  750. l_ys = centers[:,1]
  751. l_label = ci_type + ' - Normal'
  752. l_x = m_x + (num_types)*bar_wid
  753. ax.bar(l_x,l_ys[plot_order],color=l_color,edgecolor='k',width=bar_wid,label=l_label,linewidth=.5)
  754. ax.set_xticks(x)
  755. ax.set_ylim(bottom=-1, top=1.3)
  756. ax.set_xlim(-0.5,x[-1]+.5)
  757. ax.set_xticklabels(features[plot_order],rotation=25,ha='right', rotation_mode='anchor')
  758. ax.axhline(0,color='k',linewidth=.5)
  759. # ax.axvline(0,color='k')
  760. ax.set_ylabel('z-scored'+' '+'values')
  761. # ax.title.set_text('Cluster Centers')
  762. ax.text(np.mean(ax.get_xlim()),ax.get_ylim()[-1]*.95,'Cluster Centers',ha='center',va='top')
  763. handles, bar_labels = plt.gca().get_legend_handles_labels()
  764. order = np.arange(8)
  765. order = np.append(np.arange(num_types)*2,np.arange(num_types)*2+1)
  766. print(order)
  767. ax.legend([handles[i] for i in order],[bar_labels[i] for i in order],ncol=1,loc='center left',frameon=False,bbox_to_anchor=(1, .5)) ## ,bbox_to_anchor=(1, 0), loc='upper center'
  768. # plt.()
  769. plt.show()
  770. fig_combined_centers.savefig('combined_centers.'+fig_opt['format'],**fig_opt)
  771. colab.files.download('combined_centers.'+fig_opt['format'])
  772. # %%
  773. from cmh import CMH
  774. print(type_list)
  775. ##### DG TypeR ######
  776. type_list =['DG Type II', 'DG Type I']
  777. cmh_df = pd.DataFrame(columns=['Geno','Age','Type'])
  778. counts = {}
  779. e4_table =pd.DataFrame(columns=type_list,index=['7-9mo','17-19mo'])
  780. e3_table = e4_table.copy()
  781. for f in file_list:
  782. if 'DG' in f:
  783. c_type = [t for t in type_list if t+' ' in f][0]
  784. my_df_dict,_ = read_data_file(f)
  785. for k,v in my_df_dict.items():
  786. if '19' in k: age = '17-19mo'
  787. else: age = '7-9mo'
  788. if 'E4' in k: e4_table.at[age,c_type]=v.shape[0]
  789. else: e3_table.at[age,c_type]=v.shape[0]
  790. a = np.array([[k.split(' ')[0], k.split(' ')[1], c_type]]*v.shape[0])
  791. df2 = pd.DataFrame(data=a, columns=['Geno','Age','Type'])
  792. # cmh_df = cmh_df.append(df2, ignore_index=True) # DEPRICATED
  793. cmh_df = pd.concat([cmh_df,df2], ignore_index=True) # REPLACED 2024.06.13
  794. print('E3')
  795. display(e3_table)
  796. print('E4')
  797. display(e4_table)
  798. result = CMH(cmh_df, 'Type', 'Age', stratifier='Geno')
  799. display(result)
  800. # %%
  801. def plot_summary_DG_comb(num_t,numericals,type_list,uniq_types,packaged,df_for_34_2W_anova,tukey_data,folder, clust_names = ['Hyper', 'Normal'],fig_opt = {'dpi': 300, 'format':'png',
  802. 'bbox_inches':None}):
  803. # fig_opt = {'dpi': 300, 'format':'svg','bbox_inches':None}
  804. cmap = plt.cm.cool(np.linspace(0,1,2))*np.array([0.6]*3+[1])
  805. dark_cool = mpl.colors.ListedColormap(cmap)
  806. kmeans = packaged['kmeans']
  807. centers = packaged['centers']
  808. distances = packaged['distances']
  809. rel_centers = packaged['rel_centers']
  810. rel_counts = packaged['rel_counts']
  811. total_D = packaged['total_D']
  812. closeness = packaged['closeness']
  813. distances_norm = packaged['distances_norm']
  814. score = packaged['score']
  815. data_for_fit = packaged['data_for_fit']
  816. cell_type = packaged['cell_type']
  817. FS=rcParams['font.size']
  818. colors = plt.cm.viridis(np.linspace(0,1,num_t))
  819. alpha_1 = 1.0
  820. alpha_2 = 0.6
  821. colors = [[4/255, 51/255, 255/255, alpha_1], [4/255, 51/255, 255/255, alpha_2],
  822. [255/255, 38/255, 0/255, alpha_1], [255/255, 38/255, 0/255, alpha_2],
  823. [0, 0.75, 0.75, alpha_1], [0, 0.75, 0.75, alpha_2],
  824. [1, 0, 0.65, alpha_1], [1, 0, 0.65, alpha_2],
  825. ]
  826. list_colors = [colors[i] for i in type_num ]
  827. color_dc={'apoE3-KI Type_I 7-9mo': colors[0],#[:-1]+[alpha_1],
  828. 'apoE3-KI Type_II 7-9mo': colors[4],#[:-1]+[alpha_1],
  829. 'apoE4-KI Type_I 7-9mo': colors[1],#[:-1]+[alpha_2],
  830. 'apoE4-KI Type_II 7-9mo': colors[5],#[:-1]+[alpha_2],
  831. 'apoE3-KI Type_I 17-19mo': colors[2],#[:-1]+[alpha_1],
  832. 'apoE3-KI Type_II 17-19mo': colors[6],#[:-1]+[alpha_1],
  833. 'apoE4-KI Type_I 17-19mo': colors[3],#[:-1]+[alpha_2],
  834. 'apoE4-KI Type_II 17-19mo': colors[7],#[:-1]+[alpha_2],
  835. }
  836. '''
  837. apoE3-KI Type_I 7-9mo
  838. apoE3-KI Type_II 7-9mo
  839. apoE4-KI Type_I 7-9mo
  840. apoE4-KI Type_II 7-9mo
  841. apoE3-KI Type_I 17-19mo
  842. apoE3-KI Type_II 17-19mo
  843. apoE4-KI Type_I 17-19mo
  844. apoE4-KI Type_II 17-19mo
  845. '''
  846. '''raw_data_map '''
  847. counts = [np.sum( [tt==t for tt in type_list]) for t in uniq_types ]
  848. fig_norm_vals, ax = plt.subplots(1,num_t,figsize = [4.3,1],gridspec_kw={'width_ratios': counts},layout='constrained')
  849. data_X_min = np.min(data_for_fit)
  850. data_X_max = np.max(data_for_fit)
  851. for ti in range(num_t):
  852. t = uniq_types[ti]
  853. t_colr = colors[ti]
  854. is_type = [ind for ind in range(len(type_num)) if type_num[ind]==ti]
  855. c = ax[ti].pcolor(data_for_fit.T[:,is_type], vmin=data_X_min*1.2, vmax=data_X_max*1.2)
  856. ax[ti].set_yticks(np.arange(len(numericals))+0.5)
  857. if ti ==0 : ax[ti].set_yticklabels(numericals,rotation=0)
  858. else: ax[ti].get_yaxis().set_visible(False)
  859. ax[ti].set_xlabel('Cell (#)')
  860. ax[ti].title.set_text(t.replace(' ','\n'))
  861. start = min(is_type)+3 / len(is_type)
  862. stop = max(is_type)-3 / len(is_type)
  863. fig_norm_vals.suptitle(cell_type+ ' Normalized Parameters',y=1.05, fontsize=FS)
  864. # plt.tight_layout()
  865. plt.colorbar(c, ax=ax[ti],label='z-scored'+'\n'+'values')
  866. plt.show()
  867. ################# Centers
  868. new_cent_order = np.argsort(centers[:,1]).tolist()[::-1]
  869. # fig_clust_pie, ax = plt.subplots(1,2,figsize = [12,5], gridspec_kw={'width_ratios': [2, 1]} )
  870. fig_centers, ax = plt.subplots(1,1,figsize = [3,2],layout='constrained')
  871. ax.set_prop_cycle(color=[cmap[0], cmap[1]])
  872. ax.plot(centers[new_cent_order,:],'-o',linewidth=.5)
  873. ax.legend(clust_names,loc='lower center',frameon=False)
  874. sorted_numericals = [numericals[i] for i in new_cent_order]
  875. ax.set_xticks(np.arange(len(numericals)))
  876. ax.set_xticklabels(sorted_numericals,rotation=45,ha='right', rotation_mode='anchor',fontsize=FS)
  877. ax.set_ylabel('z-scored'+' '+'values')
  878. ax.axhline(0,color='k')
  879. ax.title.set_text(cell_type +' '+'Cluster Centers')
  880. # plt.tight_layout()
  881. ################# Split Distance heat map
  882. counts = [np.sum( [tt==t for tt in type_list]) for t in uniq_types ]
  883. print(counts)
  884. b_dim = 2
  885. # fig_distances_split, ax = plt.subplots(1,num_t,figsize = [8,3],gridspec_kw={'width_ratios': counts},constrained_layout=True) ## OG
  886. fig_distances_split, ax = plt.subplots(1,num_t,figsize = [7.25,.5],gridspec_kw={'width_ratios': counts},layout='constrained') ## NEw
  887. dist_min = np.min(distances_norm)
  888. dist_max = np.max(distances_norm)
  889. for ti in range(num_t):
  890. t = uniq_types[ti]
  891. t_colr = colors[ti]
  892. is_type = [ind for ind in range(len(type_num)) if type_num[ind]==ti]
  893. c = ax[ti].pcolor(distances_norm.T[:,is_type], vmin=dist_min*1.2, vmax=dist_max*1.2)
  894. ax[ti].set_yticks(np.arange(len(clust_names))+0.5)
  895. clust_names_nl = [c.replace(' ',' ') for c in clust_names]
  896. if ti ==0 : ax[ti].set_yticklabels(clust_names_nl,rotation=0)
  897. else: ax[ti].get_yaxis().set_visible(False)
  898. # ax[ti].title.set_text(t.replace(' ','\n'))
  899. # ax[ti].title.set_fontsize(16)
  900. # ax[ti].title.set_rotation(60)
  901. ax[ti].set_xlabel('Cell (#)')
  902. # fig_distances_split.tight_layout()
  903. fig_distances_split.colorbar(c, ax=ax[ti],label='z-scored'+'\n'+'distance')
  904. for ti in range(num_t):
  905. t = uniq_types[ti]
  906. ax[ti].text(0.5, 1, t.replace(' ','\n'),transform=ax[ti].transAxes,ha='center',rotation=0,va='bottom',fontsize=FS)
  907. plt.show()
  908. # xxxxxxx
  909. ############# Combined Scatter
  910. fig_comb_scatter, ax = plt.subplots(1,figsize = [4,2],layout='constrained')
  911. for ti in range(num_t):
  912. t = uniq_types[ti]
  913. t_colr = color_dc[t]
  914. print(t,t_colr)
  915. t_colr_edge = t_colr[:-1] + [alpha_1]
  916. is_type = [ind for ind in range(len(type_num)) if type_num[ind]==ti]
  917. ax.scatter(distances_norm[is_type,0],distances_norm[is_type,1],color=t_colr, label=t,linewidth=1,edgecolor=t_colr_edge,s=4)
  918. ax.legend(bbox_to_anchor=(1.04, 1), loc="upper left")
  919. # ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
  920. ax.set_xlabel('Dist. from '+clust_names[0]+' Center \n (z-scored)')
  921. ax.set_ylabel('Dist. from '+clust_names[1]+' Center \n (z-scored)')
  922. ax.plot([0, 1], [0, 1], transform=ax.transAxes,color='k')
  923. ax.text(0.98, 0.02, ''+clust_names[1],transform=ax.transAxes,ha='right',va='bottom',fontsize=FS)
  924. ax.text(0.02, 0.98, ''+clust_names[0],transform=ax.transAxes,ha='left',va='top',fontsize=FS)
  925. comb_xlim = ax.get_xlim()
  926. comb_ylim = ax.get_ylim()
  927. # print(comb_xlim)
  928. # print(comb_ylim)
  929. common_lim = np.array([-1,1]) * np.max(np.abs(list(comb_xlim)+list(comb_ylim)))
  930. common_lim= [np.min([comb_xlim[0],comb_ylim[0]]), np.max([comb_xlim[1],comb_ylim[1]]) ]
  931. # print(common_lim)
  932. ax.set_xlim(common_lim)
  933. ax.set_ylim(common_lim)
  934. #### separate Scatter #####
  935. b_dim = 1.5
  936. if 'CA3' in cell_type: fig_sep_scatter, axs = plt.subplots(1,num_t,figsize = [num_t*b_dim,b_dim*1.2],layout='constrained')
  937. else: fig_sep_scatter, axs = plt.subplots(2,int(num_t/2),figsize = [int(num_t/2)*b_dim,b_dim*2*1.2],layout='constrained') # [int(num_t/2)*b_dim,b_dim*2]
  938. axs = axs.T.flatten()
  939. for ti in range(num_t):
  940. t = uniq_types[ti]
  941. t_colr = color_dc[t]
  942. t_colr_edge = t_colr[:-1] + [alpha_1]
  943. is_type = [ind for ind in range(len(type_num)) if type_num[ind]==ti]
  944. axs[ti].scatter(distances_norm[is_type,0],distances_norm[is_type,1],color=t_colr, label=t,linewidth=1,edgecolor=t_colr_edge,s=36)
  945. axs[ti].plot([0, 1], [0, 1], transform=axs[ti].transAxes,color=[0,0,0,1])
  946. axs[ti].title.set_text(t.replace(' ','\n'))
  947. axs[ti].set_xlim(common_lim)
  948. axs[ti].set_ylim(common_lim)
  949. axs[ti].set_xlabel(clust_names[0]+' Dist.',fontsize=FS)
  950. axs[ti].set_ylabel(clust_names[1]+' Dist.',fontsize=FS)
  951. axs[ti].text(0.98, 0.02, ''+clust_names[1],transform=axs[ti].transAxes,ha='right',va='bottom',fontsize=FS)
  952. axs[ti].text(0.02, 0.98, ''+clust_names[0],transform=axs[ti].transAxes,ha='left',va='top',fontsize=FS)
  953. x=distances_norm[is_type,0]
  954. y=distances_norm[is_type,1]
  955. for i in range(len(x)):
  956. plt.plot([x[i], x[i]], [y[i], x[i]], 'k:')
  957. # plt.tight_layout()
  958. ######## ''' Violins of Residual Plots '''
  959. fig_violin_residuals, ax = plt.subplots(1,figsize=[7.25*.5,1.4],layout='constrained')
  960. comb_list = list(set(df_for_34_2W_anova['combination']))
  961. young_types = [t for t in comb_list if '-9' in t and 'Cre' not in t]
  962. young_types.sort()
  963. cre_types = [t for t in comb_list if 'Cre' in t]
  964. cre_types.sort()
  965. old_types = [t for t in comb_list if '-19' in t and 'Cre' not in t]
  966. old_types.sort()
  967. comb_list = young_types + cre_types + old_types
  968. sns_dict={}
  969. print('comb_list',comb_list)
  970. for ti in range(len(comb_list)):
  971. type_i = comb_list[ti]
  972. match_type = df_for_34_2W_anova['combination']==type_i
  973. y_vals = list(np.array(df_for_34_2W_anova[match_type]['T1_Score']))
  974. x_vals = np.ones_like(y_vals)*ti + int(np.floor(ti/2))
  975. pos = list(np.ones_like(y_vals)*ti)
  976. t_colr = colors[ti]
  977. t_colr = color_dc[type_i]
  978. sns_dict[type_i] =y_vals
  979. print(sns_dict.keys())
  980. sns_df = pd.DataFrame.from_dict(sns_dict, orient='index').T
  981. display(sns_df.head())
  982. colors_sns = [color_dc[t] for t in sns_df.columns]
  983. colors_sns = [ list(np.array(c[:-1]) + (1 - np.array(c[:-1]))* (1-c[-1])) + list(c[-1:]) for c in colors_sns]
  984. print("colors_sns",colors_sns,"colors_sns")
  985. # Plot the swarm plot
  986. sns.swarmplot(ax=ax, data=sns_df, palette=colors_sns, label=t, linewidth=.05, edgecolor='k', size=2)
  987. # Plot the violin plot
  988. sns.violinplot(ax=ax, data=sns_df, palette=colors_sns, scale='area', label=t, inner=None, linewidth=0)
  989. # Adjust the violins to set the face and edge colors
  990. violins = [v for v in ax.collections if isinstance(v, matplotlib.collections.PolyCollection)] # Get all violins
  991. # Iterate over violins and apply fill and edge color
  992. for i, violin in enumerate(violins):
  993. violin.set_facecolor([0.9, 0.9, 0.9]) # Set fill color to light gray
  994. violin.set_edgecolor(colors_sns[i % len(colors_sns)]) # Set edge color from colors_sns
  995. violin.set_linewidth(1) # Set the linewidth to ensure the edges are visible
  996. ax.text(0, 1, clust_names[0],transform=ax.transAxes,ha='right',va='center',fontsize = FS,color=[.6, 0, .6])
  997. ax.text(0, 0, clust_names[1],transform=ax.transAxes,ha='right',va='center',fontsize = FS,color=[0, .6, .6]) # =
  998. ax.set_ylabel('Residual Value')
  999. ax.set_ylim(-7.5,9.5)
  1000. ax.axhline(0,color='k')
  1001. ax_labels = sns_df.columns
  1002. ax.set_xticks(ticks=range(len(ax_labels)))
  1003. ax_labels_r = [c.replace(' ','\n') for c in ax_labels]
  1004. ax_labels_r = [c.replace('\nsyn',' syn') for c in ax_labels_r]
  1005. ax_labels_r = [c.replace('\n7-9mo','') for c in ax_labels_r]
  1006. ax_labels_r = [c.replace('\n17-19mo','') for c in ax_labels_r]
  1007. ax_labels_r = [c.replace('-Cre', '\nCre') for c in ax_labels_r]
  1008. ax_labels_r = [c.replace('_', ' ') for c in ax_labels_r]
  1009. ax_labels_r = [c.replace('apo', '') for c in ax_labels_r]
  1010. print('ax_labels',ax_labels)
  1011. print('ax_labels_r',ax_labels_r)
  1012. ax.set_xticklabels(ax_labels_r,rotation=0,ha='center',va='top', rotation_mode='anchor',fontsize=FS)
  1013. ax.title.set_text(cell_type+' Cluster Membership')
  1014. div_x = np.sum( ['7-9' in l for l in ax_labels])-0.5
  1015. segment_x = [div_x, div_x]
  1016. # segment_y = ax.get_ylim()*np.array([1.8,1])
  1017. segment_y = ax.get_ylim()*np.array([1,1])
  1018. print('segments',segment_x,segment_y)
  1019. ll = plt.plot(segment_x,segment_y,':k') # returns a sequence of line objects
  1020. ll[0].set_clip_on(False)
  1021. month_offset = 0
  1022. plt.text(div_x/2-0.25, segment_y[0]-month_offset, '7-9mo', fontsize=FS,ha='center',va='bottom')
  1023. plt.text(div_x+div_x/2+0.25, segment_y[0]-month_offset, '17-19mo', fontsize=FS,ha='center',va='bottom')
  1024. labels = comb_list
  1025. true_tuckey = tukey_data[tukey_data['reject']==True]
  1026. astk_range = [0.05,0.01,0.001,0.0001]
  1027. for r in true_tuckey.index:
  1028. g1 = true_tuckey.loc[r,'group1']
  1029. g2 = true_tuckey.loc[r,'group2']
  1030. g1 = g1.replace('_II','X')
  1031. g1 = g1.replace('_I','X')
  1032. g2 = g2.replace('_II','X')
  1033. g2 = g2.replace('_I','X')
  1034. is_match = g1 in g2
  1035. if is_match:
  1036. start_l = true_tuckey.loc[r,'group1']
  1037. stop_l = true_tuckey.loc[r,'group2']
  1038. start_x = labels.index(start_l)
  1039. stop_x = labels.index(stop_l)
  1040. y0 = np.max(df_for_34_2W_anova['T1_Score'])*1.25
  1041. y_offset = abs((stop_x-start_x))
  1042. y = y0+ y_offset * .75 + np.min([stop_x,start_x])*.25
  1043. half_dx = abs(stop_x-start_x)/2
  1044. ax.errorbar( np.mean([stop_x,start_x]), y,yerr=0, xerr=half_dx,color='k',capsize=.75,linewidth=0.75,capthick=0.75)
  1045. cent =np.mean([start_x,stop_x])
  1046. num_ask = np.sum([true_tuckey.loc[r,'p-adj'] < t for t in astk_range] )
  1047. plt.text(cent,y, '*' * num_ask,fontsize=FS+10,ha='center',va='center')
  1048. fig_size = fig_violin_residuals.get_size_inches() # Get the figure size
  1049. fig_norm_vals.savefig(f'{folder}/Normalized_Vals_split.'+fig_opt['format'],**fig_opt)
  1050. fig_violin_residuals.savefig(f'{folder}/fig_violin_residuals.'+fig_opt['format'],**fig_opt)
  1051. fig_distances_split.savefig(f'{folder}/fig_distances_split.'+fig_opt['format'],**fig_opt)
  1052. fig_sep_scatter.savefig(f'{folder}/GenoType_dist_Separate.'+fig_opt['format'],**fig_opt)
  1053. fig_comb_scatter.savefig(f'{folder}/GenoType_dist.'+fig_opt['format'],**fig_opt)
  1054. return None
  1055. ### testing
  1056. # plot_summary_DG_comb(num_t,numericals,type_list,uniq_types,packaged,df_for_34_2W_anova,tukey_data,folder,fig_opt =fig_opt)
  1057. # %%
  1058. ##### DG Combined ####
  1059. file_list_DG = [f for f in file_list if 'DG' in f]
  1060. print(file_list_DG)
  1061. dg_dict={}
  1062. for f in file_list_DG:
  1063. f_dict,_=read_data_file(f)
  1064. for k,v in f_dict.items():
  1065. if 'II' in f: k=k.replace('KI','KI Type_II')
  1066. if ' I ' in f: k=k.replace('KI','KI Type_I')
  1067. dg_dict[k]=v
  1068. folder ='DG_Comb'
  1069. try: os.makedirs(folder)
  1070. except: None
  1071. full_df = dict_to_df(dg_dict,AP_cut=60)
  1072. labeling = ['cell','Cell','type','extra']
  1073. numericals, data_for_fit, full_df_no_null = clean_data(labeling, full_df)
  1074. type_num, num_t, uniq_types,type_list = get_types(full_df_no_null)
  1075. packaged = cluster_full(data_for_fit,folder,numericals)
  1076. packaged['cell_type']='Combined DGC'
  1077. df_for_34_2W_anova, tukey_data = do_stats(type_list,packaged['score'])
  1078. _ = [print(t)for t in uniq_types]
  1079. plot_summary_DG_comb(num_t,numericals,type_list,uniq_types,packaged,df_for_34_2W_anova,tukey_data,folder,fig_opt =fig_opt)
  1080. packaged['k2_cval'] = cross_val(data_for_fit,numericals,packaged['new_cent_order'],folder,k=2,num_iter = 1000,SubSampleFrac = .8,
  1081. cell_type=packaged['cell_type'],fig_opt =fig_opt,ds_iter=5)
  1082. # _ = cross_val(data_for_fit,numericals,packaged['new_cent_order'],folder,k=3,num_iter = 1000,SubSampleFrac = .8,
  1083. # cell_type=packaged['cell_type'],fig_opt =fig_opt,ds_iter=5)
  1084. # _ = cross_val(data_for_fit,numericals,packaged['new_cent_order'],folder,k=4,num_iter = 1000,SubSampleFrac = .8,
  1085. # cell_type=packaged['cell_type'],fig_opt =fig_opt,ds_iter=5)
  1086. # _ = cross_val(data_for_fit,numericals,packaged['new_cent_order'],folder,k=5,num_iter = 1000,SubSampleFrac = .8,
  1087. # cell_type=packaged['cell_type'],fig_opt =fig_opt,ds_iter=5)
  1088. save_dl(folder)
  1089. # %%
  1090. data=packaged
  1091. # Create dictionary with features column
  1092. df_dict = {'features': data['features']}
  1093. # Add a column for each cluster center
  1094. for i in range(data['centers'].shape[1]):
  1095. df_dict[f'full_centroid_K{i}'] = data['centers'][:, i]
  1096. means = data['k2_cval']['mean_center']
  1097. for i in range(means.shape[1]):
  1098. df_dict[f'mean_centroid_K{i}'] = means[:, i]
  1099. confidence = data['k2_cval']['confidence']
  1100. for i in range(confidence.shape[2]): # Iterate over clusters (last dimension)
  1101. df_dict[f'confidence_low_K{i}'] = confidence[0, :, i] # Low bound
  1102. df_dict[f'confidence_high_K{i}'] = confidence[1, :, i] # High bound
  1103. feat_df = pd.DataFrame(df_dict)
  1104. feat_df = feat_df.sort_values('full_centroid_K0', ascending=False)
  1105. feat_df = feat_df.rename(columns=lambda x: x.replace('K0', 'Hyper').replace('K1', 'Normal'))
  1106. feat_df = feat_df.set_index('features')
  1107. display(feat_df)
  1108. feat_df.to_csv('combined_dgcs.csv')
  1109. colab.files.download('combined_dgcs.csv')
  1110. # %%
  1111. ## NatAging Resubmission
  1112. """
  1113. New Supplemental Figure
  1114. """
  1115. row_0 =0
  1116. row_1 =row_0+160
  1117. row_2 =row_1+170
  1118. row_3 = row_2+190
  1119. row_4 = row_3+215
  1120. dpi = 96
  1121. width = 7.25*dpi
  1122. length = 9.4*dpi
  1123. scale_factor= 1.33
  1124. weight='bold'
  1125. label_size = 12
  1126. sup_layout_1 = Figure(str(width), str(length),
  1127. Panel(SVG("/content/CA3_parameters_for_PCA_Clean/CrossVal_Data k=2 Centers.svg"),
  1128. Text("a", 10, 20, size=label_size,weight=weight,font='arial')
  1129. ).scale(scale_factor).move(width*0, row_0),
  1130. Panel(SVG("/content/DG_Type_II_GC_main_parameters_Clean/CrossVal_Data k=2 Centers.svg"),
  1131. Text("b", 10, 20, size=label_size,weight=weight)
  1132. ).scale(scale_factor).move(width*.25, row_0),
  1133. Panel(SVG("/content/DG_Type_I_GC_main_parameters_Clean/CrossVal_Data k=2 Centers.svg"),
  1134. Text("c", 10, 20, size=label_size,weight=weight)
  1135. ).scale(scale_factor).move(width*.50, row_0),
  1136. Panel(SVG("/content/CA1_main_parameters_Clean/CrossVal_Data k=2 Centers.svg"),
  1137. Text("d", 10, 20, size=label_size,weight=weight)
  1138. ).scale(scale_factor).move(width*.75, row_0),
  1139. Panel(SVG("/content/CA3_parameters_for_PCA_Clean/CrossVal_Data k=2 Labeling.svg"),
  1140. Text("e", 10, 20, size=label_size, weight=weight)
  1141. ).scale(scale_factor).move(width*0, row_1),
  1142. Panel(SVG("/content/DG_Type_II_GC_main_parameters_Clean/CrossVal_Data k=2 Labeling.svg"),
  1143. Text("f", 10, 20, size=label_size, weight=weight)
  1144. ).scale(scale_factor).move(width*.25, row_1),
  1145. Panel(SVG("/content/DG_Type_I_GC_main_parameters_Clean/CrossVal_Data k=2 Labeling.svg"),
  1146. Text("g", 10, 20, size=label_size, weight=weight)
  1147. ).scale(scale_factor).move(width*.50, row_1),
  1148. Panel(SVG("/content/CA1_main_parameters_Clean/CrossVal_Data k=2 Labeling.svg"),
  1149. Text("h", 10, 20, size=label_size, weight=weight)
  1150. ).scale(scale_factor).move(width*.75, row_1),
  1151. Panel(SVG("/content/CA3_parameters_for_PCA_Clean/CrossVal_Data k=3 Labeling.svg"),
  1152. Text("i", 10, 20, size=label_size, weight=weight)
  1153. ).scale(scale_factor).move(width*.0, row_2),
  1154. Panel(SVG("/content/DG_Type_II_GC_main_parameters_Clean/CrossVal_Data k=3 Labeling.svg"),
  1155. Text("j", 10, 20, size=label_size, weight=weight)
  1156. ).scale(scale_factor).move(width*.25, row_2),
  1157. Panel(SVG("/content/DG_Type_I_GC_main_parameters_Clean/CrossVal_Data k=3 Labeling.svg"),
  1158. Text("k", 10, 20, size=label_size, weight=weight)
  1159. ).scale(scale_factor).move(width*.50, row_2),
  1160. Panel(SVG("/content/CA1_main_parameters_Clean/CrossVal_Data k=3 Labeling.svg"),
  1161. Text("l", 10, 20, size=label_size, weight=weight)
  1162. ).scale(scale_factor).move(width*.75, row_2),
  1163. Panel(SVG("/content/CA3_parameters_for_PCA_Clean/GenoType_dist_Separate.svg"),
  1164. Text("m", 10, 20, size=label_size, weight=weight)
  1165. ).scale(scale_factor).move(width*0-0, row_3),
  1166. Panel(SVG("/content/DG_Type_II_GC_main_parameters_Clean/GenoType_dist_Separate.svg"),
  1167. Text("n", 10, 20, size=label_size, weight=weight)
  1168. ).scale(scale_factor).move(width*.25, row_3),
  1169. Panel(SVG("/content/DG_Type_I_GC_main_parameters_Clean/GenoType_dist_Separate.svg"),
  1170. Text("o", 10, 20, size=label_size, weight=weight)
  1171. ).scale(scale_factor).move(width*.5, row_3),
  1172. Panel(SVG("/content/CA1_main_parameters_Clean/GenoType_dist_Separate.svg"),
  1173. Text("p", 10, 20, size=label_size, weight=weight)
  1174. ).scale(scale_factor).move(width*.75, row_3),
  1175. Panel(SVG("//content/DG_Comb/CrossVal_Data k=2 Centers.svg"),
  1176. Text("q", 10, 0, size=label_size,weight=weight)
  1177. ).scale(scale_factor).move(width*.25, row_4),
  1178. Panel(SVG("/content/DG_Comb/fig_violin_residuals.svg"),
  1179. Text("r", 10, 0, size=label_size,weight=weight)
  1180. ).scale(scale_factor).move(width*.495, row_4),
  1181. # Add vertical dividing lines
  1182. Line(points=[(width * 0.25+4.5, 15), (width * 0.25+4.5, length*.9)], width=1, color='grey'),
  1183. Line(points=[(width * 0.50+4.5, 15), (width * 0.5+4.5, length*.8)], width=1, color='grey'),
  1184. Line(points=[(width * 0.75+4.5, 15), (width * 0.75+4.5, length*.8)], width=1, color='grey'),
  1185. )
  1186. sup_layout_1.save(f"SupFig4_v2.svg")
  1187. display(sup_layout_1)
  1188. colab.files.download(f"SupFig4_v2.svg")
  1189. # %%
  1190. ######### Alt fig 5
  1191. left_margin = 0
  1192. row_0 = 0
  1193. row_0_5 =row_0+20
  1194. row_1 =row_0+140
  1195. row_2 =row_1+180
  1196. dpi = 96
  1197. width = 7.25*dpi
  1198. length = 9.25*dpi
  1199. scale_factor= 1.333
  1200. weight='bold'
  1201. label_size = 12
  1202. weight = 'bold'
  1203. # label_size = label_size*scale
  1204. #### from svgutils.compose import *
  1205. CONFIG = {
  1206. "svg.file_path": ".",
  1207. "figure.save_path": ".",
  1208. "image.file_path": ".",
  1209. "text.position": (0, 0),
  1210. "text.weight": "bold",
  1211. "text.font": "Arial",
  1212. }
  1213. layout_1 = Figure(width,length,
  1214. Panel(SVG("/content/fig_cartoon_x.svg"),
  1215. Text("a", 0, 12-20, size=label_size, weight=weight)
  1216. ).move(width*.0, row_0_5).scale(scale_factor),
  1217. Panel(SVG("/content/CA1_main_parameters_Clean/GenoType_dist.svg"),
  1218. Text("b", -10, 12, size=label_size, weight=weight)
  1219. ).scale(scale_factor).move(width*.78, row_0),
  1220. Panel(SVG("/content/CA3_parameters_for_PCA_Clean/fig_violin_residuals.svg"),
  1221. Text("c", 0, 10, size=label_size, weight=weight)
  1222. ).scale(scale_factor).move(width*0, row_1),
  1223. Panel(SVG("/content/DG_Type_II_GC_main_parameters_Clean/fig_violin_residuals.svg"),
  1224. Text("d", 0, 10, size=label_size, weight=weight)
  1225. ).scale(scale_factor).move(width*.3+width*(.7/3*0), row_1),
  1226. Panel(SVG("/content/DG_Type_I_GC_main_parameters_Clean/fig_violin_residuals.svg"),
  1227. Text("e", 0, 10, size=label_size, weight=weight)
  1228. ).scale(scale_factor).move(width*.3+width*(.7/3*1), row_1),
  1229. Panel(SVG("/content/CA1_main_parameters_Clean/fig_violin_residuals.svg"),
  1230. Text("f", 0, 10, size=label_size, weight=weight)
  1231. ).scale(scale_factor).move(width*.3+width*(.7/3*2), row_1),
  1232. Panel(SVG("/content/combined_centers.svg"),
  1233. Text("g", 0, 0, size=label_size, weight=weight)
  1234. ).scale(scale_factor).move(left_margin+0, row_2),
  1235. )
  1236. display(layout_1)
  1237. layout_1.save("Figure_5_v2.svg")
  1238. colab.files.download('Figure_5_v2.svg')

_Clustering.ipynb at commit 3fa24de, no license · at the source

Overview

Authors: Dennis R. Tabuena1, Sung-Soo Jang1, Brian Grone1,2, Oscar Yip1,3, Emily A. Aery Jones1, Jessica Blumenfeld1,4, Zherui Liang1,4, Rajkamalpreet S. Mann1, Yaqiao Li1,2, Deanna Necula1,4, Nicole Koutsodendris1,5, Antara Rao1,5, Leonardo Ding1, Alex R. Zhang1, Yanxia Hao1,2, Qin Xu1,2, Seo Yeon Yoon1, Samuel De Leon1, Yadong Huang1,2,3,4,5,6,7, Misha Zilberter1
  1. Gladstone Institute of Neurological Disease, Gladstone Institutes,San Francisco, CA USA
  2. Gladstone Center for Translational Advancement, Gladstone Institutes,San Francisco, CA USA
  3. Biomedical Sciences Program, University of California, San Francisco,San Francisco, CA USA
  4. Neuroscience Program, University of California, San Francisco,San Francisco, CA USA
  5. Developmental and Stem Cell Biology Program, University of California, San Francisco,San Francisco, CA USA
  6. Department of Neurology, University of California, San Francisco,San Francisco, CA USA
  7. Department of Pathology, University of California, San Francisco,San Francisco, CA USA
Journal: Nature aging, volume 6, issue 4, pages 886-904
Dates: received 10 July 2024; accepted 18 February 2026; published online 3 April 2026; in print 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1038/s43587-026-01096-0 · PMID 41933197 · PMCID PMC13099648 · OpenAlex W7148741231
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: mouse (organism), Alzheimer's / dementia (population)
Methods: Connectivity, Statistics, Smoothing, state filtering, decompositions, Machine learning, Preprocessing, fMRI & imaging
Keywords: Alzheimer's disease
MeSH: Alzheimer Disease*, Apolipoprotein E4*, Hippocampus*, Nerve Net*, Neurons*, Aging, Animals, Dentate Gyrus, Disease Models, Animal, Male, Mice, Mice, Transgenic (* major topic)
Topic: Alzheimer's disease research and treatments (Physiology, Medicine), according to OpenAlex
Funding: National Institute on Aging (R01AG061150, R01AG087323, R01AG092390, F32AG085961, R01AG055682, R01AG071697, P01AG073082)
Citations: cited by 7 papers (Europe PMC); 110 references in the paper
Research resources: Primary antibodies included anti-NeuN RRID:AB_11205592, RRID:AB_2340470, 1:100) and anti-mCherry RRID:AB_2571870, RRID:AB_2782993

Abstract

The full impact of APOE4 (apolipoprotein E4), the strongest genetic risk factor for Alzheimer’s disease (AD), on neuronal and network function remains unclear, particularly during early preclinical stages of disease. Here we show that young APOE4 knockin (E4-KI) mice exhibit hippocampal region-specific network hyperexcitability that predicts later cognitive deficits. This early phenotype arises from cell-type-specific subpopulations of smaller, hyperexcitable neurons and is eliminated by selective removal of neuronal APOE4. With aging, E4-KI mice develop granule cell hyperexcitability, progressive inhibitory dysfunction and excitation–inhibition imbalance in the dentate gyrus. Single-nucleus RNA sequencing with multilevel gene filtering reveals age-dependent and cell-type-specific transcriptional changes and identifies candidate mediators of early neuronal hyperexcitability, including Nell2. Targeted CRISPR interference knockdown of Nell2 rescues abnormal excitability, implicating Nell2 as a contributor to APOE4-driven dysfunction. Together, these findings define molecular and circuit mechanisms linking neuronal APOE4-induced early network impairment to AD pathogenesis with aging.

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

Repository

Its files are read in the Code ↔ Paper reader above.

dtabuena/TabuenaJangGrone

License: none: the authors keep all their rights
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 3fa24de246b4d0adbc772091812095e48b2c3929, 17 December 2025
Languages: Jupyter (1)
Size: 5 files, 1 script
Software Heritage: not archived
Found in: “Code availability”
Holds: 1 notebook
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Matplotlib (1 file), NumPy (1 file), pandas (1 file), Pingouin (1 file), scikit-learn (1 file), SciPy (1 file), seaborn (1 file), statsmodels (1 file)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
1 file

Code availability

All code generated during this study is accessible upon reasonable request to the corresponding authors. k-means clustering code is available as a GitHub repository (https://github.com/dtabuena/TabuenaJangGrone).

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

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;
  • 1 script, each with its path and the digest of its content;
  • no match between paragraphs and code yet;
  • neither the text of the paper nor the code itself.

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

Data

Datasets cited

Data availability

All data associated with this study and the information of used materials are available in the main text, Methods or Supplementary Information. The snRNA-seq datasets of E3-KI and E4-KI mice at different ages are used from our previous publication61 (GEO accession ID: GSE167497 (http://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE167497)). The snRNA-seq datasets of fE-KI/Syn1-Cre mice generated during the study are available at the GEO (accession ID: GSE279550 (http://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE279550)). Source data are provided with this paper.

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

Versions

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

Version 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, 20 authors, 1 keyword, 12 MeSH terms, 1 funder, 108 references, 4 RRIDs.

Cite

This paper

Tabuena, D. R., Jang, S.-S., Grone, B., Yip, O., Aery Jones, E. A., Blumenfeld, J., Liang, Z., Mann, R. S., Li, Y., Necula, D., Koutsodendris, N., Rao, A., Ding, L., Zhang, A. R., Hao, Y., Xu, Q., Yoon, S. Y., De Leon, S., Huang, Y., & Zilberter, M. (2026). Neuronal APOE4-induced early hippocampal network hyperexcitability in Alzheimer's disease pathogenesis. Nature aging, 6(4), 886-904. https://doi.org/10.1038/s43587-026-01096-0

BibTeX

@article{tabuena2026neuronal,
author = {Tabuena, Dennis R. and Jang, Sung-Soo and Grone, Brian and Yip, Oscar and Aery Jones, Emily A. and Blumenfeld, Jessica and Liang, Zherui and Mann, Rajkamalpreet S. and Li, Yaqiao and Necula, Deanna and Koutsodendris, Nicole and Rao, Antara and Ding, Leonardo and Zhang, Alex R. and Hao, Yanxia and Xu, Qin and Yoon, Seo Yeon and De Leon, Samuel and Huang, Yadong and Zilberter, Misha},
title = {{Neuronal APOE4-induced early hippocampal network hyperexcitability in Alzheimer's disease pathogenesis}},
journal = {Nature aging},
year = {2026},
month = apr,
volume = {6},
number = {4},
pages = {886--904},
publisher = {Nature Portfolio},
issn = {2662-8465},
doi = {10.1038/s43587-026-01096-0},
url = {https://doi.org/10.1038/s43587-026-01096-0},
pmid = {41933197},
pmcid = {PMC13099648}
}

RIS

TY - JOUR
AU - Tabuena, Dennis R.
AU - Jang, Sung-Soo
AU - Grone, Brian
AU - Yip, Oscar
AU - Aery Jones, Emily A.
AU - Blumenfeld, Jessica
AU - Liang, Zherui
AU - Mann, Rajkamalpreet S.
AU - Li, Yaqiao
AU - Necula, Deanna
AU - Koutsodendris, Nicole
AU - Rao, Antara
AU - Ding, Leonardo
AU - Zhang, Alex R.
AU - Hao, Yanxia
AU - Xu, Qin
AU - Yoon, Seo Yeon
AU - De Leon, Samuel
AU - Huang, Yadong
AU - Zilberter, Misha
TI - Neuronal APOE4-induced early hippocampal network hyperexcitability in Alzheimer's disease pathogenesis
T2 - Nature aging
J2 - Nat Aging
PY - 2026
DA - 2026/04/03
VL - 6
IS - 4
SP - 886
EP - 904
SN - 2662-8465
PB - Nature Portfolio
DO - 10.1038/s43587-026-01096-0
UR - https://doi.org/10.1038/s43587-026-01096-0
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s43587-026-01096-0",
"type": "article-journal",
"title": "Neuronal APOE4-induced early hippocampal network hyperexcitability in Alzheimer's disease pathogenesis",
"container-title": "Nature aging",
"author": [
{
"family": "Tabuena",
"given": "Dennis R."
},
{
"family": "Jang",
"given": "Sung-Soo"
},
{
"family": "Grone",
"given": "Brian"
},
{
"family": "Yip",
"given": "Oscar"
},
{
"family": "Aery Jones",
"given": "Emily A."
},
{
"family": "Blumenfeld",
"given": "Jessica"
},
{
"family": "Liang",
"given": "Zherui"
},
{
"family": "Mann",
"given": "Rajkamalpreet S."
},
{
"family": "Li",
"given": "Yaqiao"
},
{
"family": "Necula",
"given": "Deanna"
},
{
"family": "Koutsodendris",
"given": "Nicole"
},
{
"family": "Rao",
"given": "Antara"
},
{
"family": "Ding",
"given": "Leonardo"
},
{
"family": "Zhang",
"given": "Alex R."
},
{
"family": "Hao",
"given": "Yanxia"
},
{
"family": "Xu",
"given": "Qin"
},
{
"family": "Yoon",
"given": "Seo Yeon"
},
{
"family": "De Leon",
"given": "Samuel"
},
{
"family": "Huang",
"given": "Yadong"
},
{
"family": "Zilberter",
"given": "Misha"
}
],
"container-title-short": "Nat Aging",
"volume": "6",
"issue": "4",
"page": "886-904",
"DOI": "10.1038/s43587-026-01096-0",
"PMID": "41933197",
"PMCID": "PMC13099648",
"ISSN": "2662-8465",
"publisher": "Nature Portfolio",
"URL": "https://doi.org/10.1038/s43587-026-01096-0",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
3
]
]
}
}

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.1016/j.celrep.2026.117505 [code]
Impaired spatial coding and neuronal hyperactivity in the medial entorhinal cortex of aged APP knock-in mice.
Journal: Cell reports
In common: pandas, Matplotlib, NumPy, Alzheimer's / dementia, mouse, 10 references
[2] doi:10.1038/s41467-026-69866-3 [code]
Selective weakening of population-coupled synaptic activity in vivo in a mouse model of amyloid-beta pathology.
Journal: Nature communications
In common: Alzheimer's / dementia, mouse, 10 references
[3] doi:10.1016/j.celrep.2026.117646 [code]
Medial entorhinal-hippocampal desynchronization parallels the emergence of memory impairment in a mouse model of Alzheimer's disease pathology.
Journal: Cell reports
In common: pandas, SciPy, Matplotlib, 1 other tool, Alzheimer's / dementia, mouse, 7 references
[4] doi:10.1038/s41593-026-02232-0 [code]
Entorhinal cortex represents task-relevant remote locations independently of CA1.
Journal: Nature neuroscience
In common: Pingouin, statsmodels, seaborn, 5 other tools, mouse, author Emily A. Aery Jones
[5] doi:10.1111/ejn.70480 [code]
Astrocyte Proximity Protects Synapses From Human Amyloid-Beta Induced Degeneration in a Mouse Ex Vivo Model of Early Alzheimer's Disease.
Journal: The European journal of neuroscience
In common: statsmodels, seaborn, scikit-learn, 4 other tools, Alzheimer's / dementia, mouse, 2 references
[6] doi:10.1038/s41467-026-74227-1 [code]
Age-related changes in behavioural and neural variability in a decision-making task.
Journal: Nature communications
In common: Pingouin, statsmodels, seaborn, 5 other tools, mouse, 1 reference
[7] doi:10.1111/acel.70494
Exceptional Longevity Modifying Allele APOE2 Promotes DNA Signaling Pathways Resisting Cellular Senescence in Human Neurons.
Journal: Aging cell
In common: Alzheimer's / dementia, 5 references
[8] doi:10.1371/journal.pcbi.1014571 [code]
SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification.
Journal: PLoS computational biology
In common: Pingouin, statsmodels, seaborn, 5 other tools, 1 reference
[9] doi:10.1126/sciadv.adz6517 [code]
Corticosterone-linked microglial activity underpins sexually dimorphic neuroplasticity after ketamine anesthesia.
Journal: Science advances
In common: statsmodels, seaborn, scikit-learn, 4 other tools, mouse, 2 references
[10] doi:10.3389/fnagi.2026.1847611 [code]
APOE ε4-associated hippocampal atrophy trajectories across the Alzheimer's disease continuum: a systematic review, meta-analysis, and longitudinal validation.
Journal: Frontiers in aging neuroscience
In common: statsmodels, seaborn, pandas, 3 other tools, Alzheimer's / dementia, 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.

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.