OSCR

Cell death analysis of inducible, titratable neurodegenerative disease models in zebrafish and human stem cell-derived retinal organoids.

Code ↔ Paper

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

The 3 matches
  1. [1] § MATERIALS AND METHODS › Transcriptomic data processing ↔ preprocessing_basics.ipynb, lines 208–237 · score 0.54 · highly variable genes, filtered, apoptosis, necroptosis, parthanatos
  2. [2] § MATERIALS AND METHODS › Cell death pathway scoring ↔ time_course.ipynb, lines 234–346 · score 0.53 · sham signatures, baseline, Scanpy, score, gene
  3. [3] § RESULTS › Cell death signatures in transcriptomic datasets of NTR 2.0/MTZ-based ablation ↔ time_course.ipynb, lines 234–346 · score 0.51 · gene signature, sham signatures, ablated, signaling, score, apoptosis

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 · 647 lines · 20 KB · no license · 2 matches

  1. # %%
  2. import scanpy as sc
  3. import numpy as np
  4. import matplotlib.pyplot as plt
  5. import pandas as pd
  6. import scipy
  7. from pydeseq2.dds import DeseqDataSet
  8. from pydeseq2.default_inference import DefaultInference
  9. from pydeseq2.ds import DeseqStats
  10. from matplotlib.colors import LinearSegmentedColormap
  11. import matplotlib.cm as cm
  12. from misc_utils import score_colors
  13. # %%
  14. # data_annotated_path = "/home/bmb/haxx/working/ceisel_mumm/data/"
  15. data_annotated_path = "/Users/bbrener1/haxx/ceisel_mumm/data/"
  16. data = sc.read_h5ad(data_annotated_path + "full_annotations_leiden.h5ad")
  17. figure_path = "/Users/bbrener1/haxx/ceisel_mumm/figures/"
  18. data.shape
  19. # %%
  20. data.obs['time']=data.obs['time'].astype(int)
  21. # %% [markdown]
  22. # # Building A Timeline
  23. # %%
  24. plt.figure()
  25. plt.title("Log total counts vs time")
  26. plt.boxplot(
  27. [data.obs['log_total_counts'][data.obs['time'] == t] for t in [12,24,48,72]],
  28. showfliers=False,
  29. )
  30. plt.xticks(
  31. [1,2,3,4],
  32. ["12h","24h","48h","72h"]
  33. )
  34. plt.ylabel("Log total counts")
  35. plt.show()
  36. # %% [markdown]
  37. # # PCA IQR Timeline, Big Sheet
  38. # %% [markdown]
  39. # We can briefly look at the timecourse of each individual PC across the entire dataset with no filtering to sanity check our data and see the overall level of stability. We also want to observe whether or not there are obvious outliers
  40. # %%
  41. def extract_iqrs(data,low_percentile=25,high_percentile=75):
  42. return {
  43. 'low':np.percentile(data,low_percentile),
  44. 'center':np.median(data),
  45. 'high':np.percentile(data,high_percentile),
  46. }
  47. def extract_sems(data,sem_multiple=2):
  48. sem = scipy.stats.sem(data)
  49. mean = np.mean(data)
  50. return {
  51. 'low':mean - (sem*sem_multiple),
  52. 'center':mean,
  53. 'high':mean + (sem*sem_multiple),
  54. }
  55. import matplotlib.patheffects as pe
  56. def shadow_segments_to_ax(
  57. ax,
  58. shadowed_segments,
  59. label=None,ax_label=None,suppress_legend=False,
  60. plot_shadow=True,fill_label=None,
  61. color=None,outline=None,
  62. **kwargs
  63. ):
  64. times = sorted(shadowed_segments.keys())
  65. ln, = ax.plot(
  66. times,
  67. [shadowed_segments[t]['center'] for t in times],
  68. label=label,color=color,
  69. **kwargs
  70. )
  71. # plot outline
  72. if outline:
  73. ln.set_path_effects([
  74. pe.Stroke(linewidth=ln.get_linewidth()+outline, foreground='black'),
  75. pe.Normal(),
  76. ])
  77. if plot_shadow:
  78. ax.fill_between(
  79. times,
  80. [shadowed_segments[t]['low'] for t in times],
  81. [shadowed_segments[t]['high'] for t in times],
  82. label=fill_label,alpha=.2,
  83. color=ln.get_color()
  84. )
  85. ax.set_xlabel("Time (h)")
  86. ax.set_ylabel(ax_label)
  87. ax.set_title(ax_label)
  88. if label and not suppress_legend:
  89. ax.legend()
  90. return ax
  91. def get_compound_masks(
  92. data
  93. ):
  94. times = sorted(set(data.obs['time']))
  95. time_masks = [
  96. data.obs['time'] == t
  97. for t in times
  98. ]
  99. control_mask = data.obs['exp_condition'] == "Cntr"
  100. injury_mask = data.obs['exp_condition'] == "Mtz"
  101. combined_masks = {
  102. 'control':{
  103. time:control_mask & time_mask for time,time_mask in zip(times,time_masks)
  104. },
  105. 'ablated':{
  106. time: injury_mask & time_mask for time,time_mask in zip(times,time_masks)
  107. }
  108. }
  109. return combined_masks
  110. def map_over_leaves(compound,fn):
  111. return {
  112. outer_key: {inner_key: fn(v) for inner_key, v in inner.items()}
  113. for outer_key, inner in compound.items()
  114. }
  115. def get_compound_scores(
  116. compound_masks,target
  117. ):
  118. return map_over_leaves(compound_masks,lambda mask: target[mask])
  119. def get_compound_iqrs(
  120. compound_scores
  121. ):
  122. return map_over_leaves(compound_scores, extract_iqrs)
  123. def get_compound_means(
  124. compound_scores
  125. ):
  126. return map_over_leaves(compound_scores, extract_sems)
  127. def plot_timecourse(
  128. target,compound_masks,ax,
  129. label=None,fill_label=None,ax_label=None,central_tendency="median",
  130. plot_shadow=True,suppress_legend=False,alpha=1,color=None,outline=None,linewidth=None,
  131. ):
  132. compound_scores = get_compound_scores(compound_masks,target)
  133. compound_shadow = None
  134. shadow_label = None
  135. match central_tendency:
  136. case "median":
  137. compound_shadow = get_compound_iqrs(compound_scores)
  138. shadow_label = "IQR"
  139. case "mean":
  140. compound_shadow = get_compound_means(compound_scores)
  141. shadow_label = "SEM"
  142. case _: raise ValueError(f"Invalid central tendency: {central_tendency}")
  143. if fill_label is not None:
  144. shadow_label = fill_label
  145. for condition in compound_shadow:
  146. shadow_segments_to_ax(
  147. ax,compound_shadow[condition],
  148. label=f"{label} : {condition}",
  149. fill_label=shadow_label,
  150. ax_label=ax_label,plot_shadow=plot_shadow,suppress_legend=suppress_legend,alpha=alpha,
  151. color=color,outline=outline, **({"linestyle":"--"} if "control" in condition else {}),linewidth=linewidth
  152. )
  153. # %%
  154. compound_masks = get_compound_masks(data)
  155. fig,axes = plt.subplots(5,10,figsize=(30,15))
  156. for i in range(50):
  157. ax = axes[i//10,i%10]
  158. target = data.obsm['X_pca'][:,i]
  159. plot_timecourse(
  160. target,compound_masks,ax,
  161. label=f"PC{i}",
  162. ax_label=f'PC{i}',central_tendency="median",
  163. )
  164. fig.tight_layout()
  165. fig.show()
  166. # %% [markdown]
  167. # # Cell Death Scoring
  168. # %% [markdown]
  169. # The particulars of producing these scores are in the sister file "annotations", but this is the set of gene sets used by the TBI guys. They can be updated with alternatives on demand. (Swap in the gene sets and map the homologs to zebrafish genome)
  170. #
  171. # We want to plot the timecourse of the score, as well as the scores over the UMAP to see if there are any obvious patterns.
  172. #
  173. # Brief note: representing an uncertainty score here is a little fraught, I'm not a huge fan of either the IQR or the SEM, the former is too vague and the latter is too conservative (as written it is treating each cell as a unique observation)
  174. #
  175. # I want to check if there are biological replicate annotations, if so we can re-calculate the SEM on a per-sample basis, which will better represent the actual variation certainty. Please stand by for this.
  176. # %%
  177. parthanatos_mask = np.array(data.var['parthanatos'])
  178. apoptosis_mask = np.array(data.var['apoptosis'])
  179. necroptosis_mask = np.array(data.var['necroptosis'])
  180. parthanatos_signature = data.var_names[parthanatos_mask]
  181. apoptosis_signature = data.var_names[apoptosis_mask]
  182. necroptosis_signature = data.var_names[necroptosis_mask]
  183. print(f"Parthanatos: {parthanatos_signature}")
  184. print(f"Necroptosis: {necroptosis_signature}")
  185. print(f"Apoptosis: {apoptosis_signature}")
  186. sc.tl.score_genes(data,parthanatos_signature,score_name="parthanatos")
  187. sc.tl.score_genes(data,necroptosis_signature,score_name="necroptosis")
  188. sc.tl.score_genes(data,apoptosis_signature,score_name="apoptosis")
  189. # %% [markdown]
  190. # ### Sham Signature
  191. # %%
  192. # Let's also construct a sham gene signature to get a sense for "baseline" signal
  193. # First we want to establish the overall number of genes in ext signatures:
  194. print((
  195. f'Parthanatos: {np.sum(data.var['parthanatos'])}\n'
  196. f'Necroptosis: {np.sum(data.var['necroptosis'])}\n'
  197. f'Apoptosis: {np.sum(data.var['apoptosis'])}\n'
  198. ))
  199. def get_sham_percentiles(
  200. data,
  201. n_shams=1000,sham_size=17,
  202. verbose=False,
  203. central_tendency="median"
  204. ):
  205. compound_masks = get_compound_masks(data)
  206. n_features = len(data.var_names)
  207. times = sorted(data.obs['time'].unique())
  208. lotta_sham = [np.random.randint(0,n_features,size=sham_size) for _ in range(n_shams)]
  209. sham_timecourses = []
  210. for i,sham in enumerate(lotta_sham):
  211. if verbose:
  212. print(f"sham {i}",end="\r")
  213. sc.tl.score_genes(data,data.var_names[sham],score_name=f'sham_tmp')
  214. scores = get_compound_scores(compound_masks,data.obs['sham_tmp'])
  215. ucl = {}
  216. match central_tendency:
  217. case "median":
  218. ucl = get_compound_iqrs(scores)
  219. case "mean":
  220. ucl = get_compound_means(scores)
  221. case _: raise ValueError(f"Invalid central tendency: {central_tendency}")
  222. sham_timecourses.append(ucl)
  223. time_deltas = []
  224. for timecourse in sham_timecourses:
  225. deltas = [timecourse['ablated'][t]['center'] - timecourse['control'][t]['center'] for t in times]
  226. time_deltas.append(deltas)
  227. time_deltas = np.array(time_deltas)
  228. time_deltas
  229. delta_quantiles = {
  230. t:np.percentile(time_deltas[:,i],[np.arange(100)])[0] for i,t in enumerate(times)
  231. }
  232. delta_quantiles
  233. return sham_timecourses,time_deltas,delta_quantiles
  234. def plot_quantiles_to_ax(
  235. quantiles,ax,
  236. offsets=None,
  237. bound_ranges=None,
  238. alpha_multiple=.05,
  239. color='blue',
  240. ):
  241. if offsets is None:
  242. offsets = np.array([0] * len(quantiles))
  243. if bound_ranges is None:
  244. bound_ranges = [
  245. (1,99),
  246. (5,95),
  247. (15,85),
  248. (25,75),
  249. ]
  250. times = sorted(quantiles.keys())
  251. for lower,upper in bound_ranges:
  252. lower_bound = np.array([quantiles[t][lower] for t in times]) + offsets
  253. upper_bound = np.array([quantiles[t][upper] for t in times]) + offsets
  254. ax.fill_between(
  255. times,upper_bound,lower_bound,
  256. alpha=alpha_multiple,color=color,
  257. label=f"_quant_{lower}_{upper}"
  258. )
  259. return ax
  260. from matplotlib.patches import Patch as mpl_Patch
  261. def label_quantiles(ax,labels=None):
  262. quantile_artists = [
  263. a for a in ax.get_children()
  264. if str(a.get_label()).startswith("_quant")
  265. ]
  266. if labels is None:
  267. bounds = [qa.get_label().split("_quant_")[1].split("_") for qa in quantile_artists]
  268. labels = [f"{lower}-{upper} %" for lower,upper in bounds]
  269. for qa,label in zip(quantile_artists,labels):
  270. qa.set_label(label)
  271. legend_entries = {
  272. mpl_Patch(color=qa.get_facecolor(),alpha=qa.get_alpha()*(i+1)) : qa.get_label()
  273. for i,qa in enumerate(quantile_artists)
  274. }
  275. return legend_entries
  276. # %% [markdown]
  277. # ### RGC Shams
  278. # %%
  279. rgc_subset = data[data.obs['cell_type'] == "RGCs"]
  280. _,_,rgc_delta_quantiles = get_sham_percentiles(rgc_subset,verbose=True)
  281. _,_,global_delta_quantiles = get_sham_percentiles(data,verbose=True)
  282. # %%
  283. plt.figure()
  284. plt.title("RGC-Specific Null Distribution of Signature Deltas\n Ablation vs Control")
  285. plot_quantiles_to_ax(rgc_delta_quantiles,plt.gca())
  286. plt.xlabel("Time (h)")
  287. plt.ylabel("Signature Delta (ablation - control, arb units)")
  288. plt.show()
  289. # %% [markdown]
  290. # # Timecourse Plot
  291. # %%
  292. # Let's plot a timecourse for ablation vs control globally (all cell types)
  293. # Reminder: masks by condition and by time
  294. compound_masks = get_compound_masks(data)
  295. times = sorted(data.obs['time'].unique())
  296. fig,axes = plt.subplots(1,3,figsize=(15,5))
  297. for ax,score in zip(axes,['parthanatos','necroptosis','apoptosis']):
  298. score_values = data.obs[score]
  299. plot_timecourse(
  300. score_values,compound_masks,ax,
  301. label=score,ax_label=score,central_tendency="median",
  302. plot_shadow=False,suppress_legend=True,alpha=1,
  303. color=score_colors[score],outline=.5
  304. )
  305. score_iqrs = get_compound_iqrs(get_compound_scores(compound_masks,score_values))
  306. legend_spec = {handle:label for handle,label in zip(*(ax.get_legend_handles_labels()))}
  307. plot_quantiles_to_ax(
  308. global_delta_quantiles,ax,color=score_colors[score],
  309. offsets=[score_iqrs['control'][t]['center'] for t in times],
  310. alpha_multiple=.1,
  311. )
  312. legend_spec |= label_quantiles(ax)
  313. ax.legend(legend_spec.keys(),legend_spec.values())
  314. fig.tight_layout()
  315. fig.show()
  316. # %% [markdown]
  317. # ### UMAP Score Plots
  318. # %% [markdown]
  319. # We can see that the distribution of the scores is similar but not fully overlapping. We can probably confirm this intuition with a bunch of pairwise scatters, but this basically tracks with the lit anyway afaik.
  320. # %%
  321. fig,axes = plt.subplots(1,3,figsize=(25,5))
  322. axes = axes.flatten()
  323. sc.pl.umap(data,color='parthanatos',ax=axes[0],show=False)
  324. sc.pl.umap(data,color='necroptosis',ax=axes[1],show=False)
  325. sc.pl.umap(data,color='apoptosis',ax=axes[2],show=False)
  326. fig.show()
  327. # %% [markdown]
  328. # # Subset to Retinal Ganglion Cells
  329. # %% [markdown]
  330. # We want to check if the behavior of RGCs in particular is different than the rest of the pop with respect to the scores of interest.
  331. #
  332. # We will isolate just the RGCs using leiden mapping, and then re-plot some of our previous analysis
  333. # %%
  334. score_legend_spec = {}
  335. quantile_legend_spec = {}
  336. rgc_compound_masks = get_compound_masks(rgc_subset)
  337. fig,axes = plt.subplots(1,3,figsize=(15,5))
  338. fig.suptitle("RGC-Specific Death Score Timecourse")
  339. for ax,score in zip(axes,['parthanatos','necroptosis','apoptosis']):
  340. score_values = rgc_subset.obs[score]
  341. plot_timecourse(
  342. score_values,rgc_compound_masks,ax,
  343. label=score,ax_label=score,central_tendency="median",
  344. plot_shadow=False,suppress_legend=True,alpha=1,
  345. color=score_colors[score],outline=.5,linewidth=2,
  346. )
  347. score_iqrs = get_compound_iqrs(get_compound_scores(rgc_compound_masks,score_values))
  348. legend_spec = {handle:label for handle,label in zip(*(ax.get_legend_handles_labels()))}
  349. score_legend_spec |= legend_spec
  350. plot_quantiles_to_ax(
  351. rgc_delta_quantiles,ax,color=score_colors[score],
  352. offsets=[score_iqrs['control'][t]['center'] for t in times],
  353. alpha_multiple=.1,
  354. )
  355. quantile_legend_spec |= label_quantiles(ax)
  356. # Per axis legend
  357. # ax.legend(legend_spec.keys(),legend_spec.values())
  358. # unified_legend_spec = {**score_legend_spec,**dict(list(quantile_legend_spec.items())[-4:])}
  359. # ax.legend(unified_legend_spec.keys(),unified_legend_spec.values(),framealpha=1)
  360. # Unify the axes
  361. y_min = min([ax.get_ylim()[0] for ax in axes])
  362. y_max = max([ax.get_ylim()[1] for ax in axes])
  363. for ax in axes:
  364. ax.set_ylim(y_min,y_max)
  365. fig.tight_layout()
  366. fig.savefig(f"{figure_path}/rgc_vs_sham_timecourse.png",dpi=300)
  367. fig.show()
  368. # %% [markdown]
  369. # # RGC Vs Rest Comparison
  370. # %% [markdown]
  371. # Let's compare the RGCs to the rest of the population to observe any differences for our scores of interest
  372. # %%
  373. rgc_mask = data.obs['cell_type'] == "RGCs"
  374. rgc = data[rgc_mask]
  375. non_rgc = data[~rgc_mask]
  376. times = [12,24,48,72]
  377. rgc_time_masks = [
  378. rgc.obs['time'] == t
  379. for t in times
  380. ]
  381. non_rgc_time_masks = [
  382. non_rgc.obs['time'] == t
  383. for t in times
  384. ]
  385. rgc_control_mask = rgc.obs['exp_condition'] == "Cntr"
  386. rgc_injury_mask = rgc.obs['exp_condition'] == "Mtz"
  387. non_rgc_control_mask = non_rgc.obs['exp_condition'] == "Cntr"
  388. non_rgc_injury_mask = non_rgc.obs['exp_condition'] == "Mtz"
  389. fig,axes = plt.subplots(1,3,figsize=(15,5))
  390. for i,score in enumerate(['parthanatos','necroptosis','apoptosis']):
  391. time_rgc_control_iqrs = {}
  392. time_rgc_injury_iqrs = {}
  393. time_non_rgc_control_iqrs = {}
  394. time_non_rgc_injury_iqrs = {}
  395. for t,(rgc_time_mask,non_rgc_time_mask) in zip(times,zip(rgc_time_masks,non_rgc_time_masks)):
  396. combined_rgc_control_mask = rgc_control_mask & rgc_time_mask
  397. combined_rgc_injury_mask = rgc_injury_mask & rgc_time_mask
  398. combined_non_rgc_control_mask = non_rgc_control_mask & non_rgc_time_mask
  399. combined_non_rgc_injury_mask = non_rgc_injury_mask & non_rgc_time_mask
  400. time_rgc_control_iqrs[t] = extract_iqrs(rgc.obs[score][combined_rgc_control_mask])
  401. time_rgc_injury_iqrs[t] = extract_iqrs(rgc.obs[score][combined_rgc_injury_mask])
  402. time_non_rgc_control_iqrs[t] = extract_iqrs(non_rgc.obs[score][combined_non_rgc_control_mask])
  403. time_non_rgc_injury_iqrs[t] = extract_iqrs(non_rgc.obs[score][combined_non_rgc_injury_mask])
  404. ax = axes[i]
  405. # We're going to discard all the IQR stuff here because the shadows make this more or less unreadable.
  406. # But I do think it's important to visualize this in overlay to convey the idea
  407. ax.plot(times, [time_rgc_control_iqrs[t]['center'] for t in times], label="RGC Control", color='r')
  408. ax.plot(times, [time_rgc_injury_iqrs[t]['center'] for t in times], label="RGC injury", color='b')
  409. ax.plot(times, [time_non_rgc_control_iqrs[t]['center'] for t in times], label="Non-RGC Control", color='r', linestyle='--',alpha=.3)
  410. ax.plot(times, [time_non_rgc_injury_iqrs[t]['center'] for t in times], label="Non-RGC injury", color='b', linestyle='--',alpha=.3)
  411. ax.set_xlabel("Time (h)")
  412. ax.set_ylabel(f"{score}")
  413. ax.set_title(f"{score} (RGC vs Non-RGC)")
  414. ax.legend()
  415. fig.tight_layout()
  416. fig.show()
  417. # %% [markdown]
  418. # # PCA sanity check
  419. # %% [markdown]
  420. # We should check if any computed PCs meaningfully correspond to the computed cell death scores, as this would be a simple way to verify whether or not this is a relatively orthogonal signal
  421. # %%
  422. corr = np.corrcoef(data.obsm['X_pca'].T,data.obs[['parthanatos','necroptosis','apoptosis']].T)[50:,:50]
  423. plt.figure()
  424. plt.imshow(
  425. corr.T,
  426. cmap='bwr',vmin=-1,vmax=1,
  427. aspect='auto'
  428. )
  429. plt.ylabel("PCs")
  430. plt.xticks(np.arange(3),labels=['parthanatos','necroptosis','apoptosis'])
  431. plt.colorbar(label="Pearson Correlation")
  432. plt.title("Cell Death Scores vs PCA Scores")
  433. plt.show()
  434. # %%
  435. parthanatos_min,parthanatos_max = np.min(corr[0]),np.max(corr[0])
  436. necroptosis_min,necroptosis_max = np.min(corr[1]),np.max(corr[1])
  437. apoptosis_min,apoptosis_max = np.min(corr[2]),np.max(corr[2])
  438. parthanatos_min,parthanatos_max = np.around(parthanatos_min,3),np.around(parthanatos_max,3)
  439. necroptosis_min,necroptosis_max = np.around(necroptosis_min,3),np.around(necroptosis_max,3)
  440. apoptosis_min,apoptosis_max = np.around(apoptosis_min,3),np.around(apoptosis_max,3)
  441. print(f"Correlations: \t\tMin\t\tMax")
  442. print("========================================================")
  443. print(f"parthanatos \t\t{parthanatos_min},\t\t{parthanatos_max}")
  444. print(f"necroptosis \t\t{necroptosis_min},\t\t{necroptosis_max}")
  445. print(f"apoptosis \t\t{apoptosis_min},\t\t{apoptosis_max}")
  446. # %% [markdown]
  447. # Apoptosis seems to have popped out as the strongest but not by a massive amount. It has (negative) 33% correlation to PC 2. PC orientation is arbitrary, so they should be considered absolute.
  448. # %% [markdown]
  449. # # Population Plot
  450. # %%
  451. from misc_utils import auto_split_range
  452. reduced = data[data.obs['cell_type'] != "injury"]
  453. def get_pairwise_proportions(data,all_cell_types):
  454. sizes = np.array([np.sum(data.obs['cell_type'] == t) for t in all_cell_types])
  455. pairwise = np.outer((1+sizes),1/(1+sizes))
  456. np
  457. return pairwise
  458. compound_ratios = {'ablated':{},'control':{}}
  459. all_cell_types = sorted(reduced.obs['cell_type'].unique())
  460. n_cell_types = len(all_cell_types)
  461. compound_masks = get_compound_masks(reduced)
  462. fig,axes = plt.subplots(2,4,figsize=(20,10))
  463. for i,condition in enumerate(compound_masks):
  464. for j,time in enumerate(compound_masks[condition]):
  465. ax = axes[i][j]
  466. subset = reduced[compound_masks[condition][time]]
  467. pairwise = get_pairwise_proportions(subset,all_cell_types)
  468. compound_ratios[condition][time] = pairwise
  469. im = ax.imshow(
  470. np.log(pairwise),
  471. **auto_split_range(np.log(pairwise),force_range=10)
  472. )
  473. plt.colorbar(im,ax=ax)
  474. ax.set_xticks(np.arange(n_cell_types),labels=all_cell_types,rotation=90)
  475. ax.set_yticks(np.arange(n_cell_types),labels=all_cell_types)
  476. ax.set_title(f"{condition} {time}")
  477. fig.tight_layout()
  478. fig.show()
  479. # %%
  480. fig,axes = plt.subplots(1,4,figsize=(20,5))
  481. fig.suptitle("Control / Ablated Ratio of Ratios")
  482. for time,ax in zip(compound_masks['control'],axes):
  483. ror = (compound_ratios['control'][time])/(compound_ratios['ablated'][time])
  484. im = ax.imshow(
  485. np.log(ror),
  486. **auto_split_range(np.log(ror))
  487. )
  488. ax.set_xticks(np.arange(n_cell_types),labels=all_cell_types,rotation=90)
  489. ax.set_yticks(np.arange(n_cell_types),labels=all_cell_types)
  490. plt.colorbar(im,ax=ax)
  491. fig.tight_layout()
  492. fig.show()
  493. # %%

time_course.ipynb at commit 900c21f, no license · at the source

Overview

Authors: Anneliese Ceisel1, Gianna Graziano1, Kevin Emmerich1,2, Xiangqian Shi1,3, Tae-In Kam4,5, Miguel Flores-Bellver6, M. Natalia Vergara6, Silvia Aparicio-Domingo6, Boris M. Brenerman7, Anne Vielle6, Elsie M. Williams8,9, Abigail V. Sharrock8,9, Uche Onuchuwku1, Georgina S. Martinez1, Lydia G. Sanders1, Uzoamaka Nwagbo10, Beichen Wang10, Huanhuan Xiao1, Grant Kroeschell1, Yiqi Gao1
and 16 other authorsDaniel J. Choe1, Caroline E. Clouatre1, Diego Alfaro Carcoba1, Barak Reibman1, Catalina Rodriguez1, Kevin Yang1, Shreya Banerjee1, Frazer Matthews1, James H. Thierer1, Genevieve Stein-O'Brien7, Ted M. Dawson4,5,7,11, Valina L. Dawson4,5,7,11, David F. Ackerley8,9, M. Valeria Canto-Soler2, Liyun Zhang1,10, Jeff S. Mumm1,7
  1. Wilmer Eye Institute, Johns Hopkins School of Medicine, Baltimore, MD 21231, USA
  2. Translational Vascular Medicine Branch, National Heart, Lung and Blood Institute, National Institutes of Health, Bethesda, MD 20814, USA
  3. Department of Pharmaceutical Sciences, University of Illinois-Chicago, Chicago, IL 60612, USA
  4. Neuroregeneration and Stem Cell Programs, Institute for Cell Engineering, Johns Hopkins University School of Medicine, Baltimore, MD 21205, USA
  5. Department of Neurology, Johns Hopkins University School of Medicine, Baltimore, MD 21205, USA
  6. CellSight Ocular Stem Cell and Regeneration Research Program, Department of Ophthalmology, Sue Anschutz-Rodgers Eye Center, University of Colorado Anschutz, Aurora, CO 80045, USA
  7. Solomon H. Snyder Department of Neuroscience, Johns Hopkins University School of Medicine, Baltimore, MD 21205, USA
  8. School of Biological Sciences, Victoria University of Wellington. Wellington 6140, New Zealand
  9. Te Matapihipihi - The Centre for Biodiscovery and Maurice Wilkins Centre for Molecular Biodiscovery, Victoria University of Wellington, Wellington 6140, New Zealand
  10. Department of Ophthalmology, University of Pittsburgh, Pittsburgh, PA 15219, USA
  11. Department of Physiology, Pharmacology and Therapeutics, Johns Hopkins University School of Medicine, Baltimore, MD 21205, USA
Journal: Disease models & mechanisms, volume 19, issue 7, article dmm052747
Dates: received 6 November 2025; accepted 12 June 2026; published online 24 July 2026; in print July 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1242/dmm.052747 · PMID 42497345 · PMCID PMC13474575 · OpenAlex W7170874272
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), zebrafish (organism), other condition (population), cellular / molecular (subfield)
Methods: Statistics, Smoothing, state filtering, decompositions, fMRI & imaging
Keywords: Cell death, Nitroreductase, Parthanatos, Inducible disease model, Neurodegeneration, Retinal organoids
MeSH: Neurodegenerative Diseases*, Organoids*, Retina*, Stem Cells*, Zebrafish*, Animals, Apoptosis, Cell Death, Disease Models, Animal, Humans, Metronidazole, Nitroreductases (* major topic)
Topic: Retinal Development and Disorders (Molecular Biology, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Funding: U.S. Department of Health & Human Services | NIH | National Eye Institute (NEI) (F31:EY032790, P30:EY1765, R01:EY033009, R01:EY032533, R01:OD020376, T32:EY7143-22, F31:EY037171); Maryland Department of Commerce; Dr. Ralph and Marian Falk Medical Research Trust; Marsden Fund (VUW2402, VUW1902); CellSight Development Fund; Research to Prevent Blindness, Inc; Doni Solich Family Chair in Ocular Stem Cell Research; BrightFocus Foundation (BrightFocus) (M2024008N); Foundation Fighting Blindness; Johns Hopkins University; School of Medicine, Johns Hopkins University
Citations: not cited yet (Europe PMC); 119 references in the paper

Abstract

Inducible disease models enable large-scale screening by providing control over pathology onset, such as cell death in neurodegenerative disease. The nitroreductase (NTR)/prodrug system of cell ablation has facilitated investigations of cell function and regeneration but has not been widely adopted as a disease modeling platform, perhaps owing to assumptions that the cell death mechanism(s) elicited is artificial in nature. Prior reports suggested that NTR/prodrug-mediated death occurred through apoptosis, necroptosis and/or parthanatos, which have all been implicated in neurodegenerative disease. To clarify this issue, we investigated the cell death pathway(s) elicited by the prodrug metronidazole (MTZ) with improved nitroreductase enzyme variants. We assessed the cell death pathway(s) elicited by NTR 2.0-expressing zebrafish retinal neurons using a transcriptomic analysis, chemical inhibitors and gene-targeting assays. NTR-H, a novel NTR variant, was tested – and found to be effective – in human stem cell-derived retinal organoids. Parthanatos was implicated across all conditions tested, while evidence of apoptosis was variable. As parthanatos is associated with neurodegeneration, our results support the use of the NTR/MTZ system to create inducible neurodegenerative models targeted to specific disease-relevant neuronal cell types.

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

Repository

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

mummlab/Cell-Death-in-NTR2.0-mediated-RGC-Ablation

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 900c21f8408c85500dc5d06df62160866ba0c181, 12 May 2026
Languages: Jupyter (4), Python (1)
Size: 10 files, 5 scripts
Software Heritage: not archived
Found in: the end of the paper
Holds: README, environment (envs/env.yml), 4 notebooks
Not found: license file, CITATION.cff, tests, continuous integration, documentation
Tools: Matplotlib (5 files), NumPy (5 files), pandas (4 files), Scanpy (4 files), SciPy (4 files), anndata (2 files), scikit-learn (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
6 files

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;
  • 5 scripts, each with its path and the digest of its content;
  • 3 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

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, 36 authors, 6 keywords, 12 MeSH terms, 11 funders, 119 references.

Cite

This paper

Ceisel, A., Graziano, G., Emmerich, K., Shi, X., Kam, T.-I., Flores-Bellver, M., Vergara, M. N., Aparicio-Domingo, S., Brenerman, B. M., Vielle, A., Williams, E. M., Sharrock, A. V., Onuchuwku, U., Martinez, G. S., Sanders, L. G., Nwagbo, U., Wang, B., Xiao, H., Kroeschell, G., . . . Mumm, J. S. (2026). Cell death analysis of inducible, titratable neurodegenerative disease models in zebrafish and human stem cell-derived retinal organoids. Disease models & mechanisms, 19(7), dmm052747. https://doi.org/10.1242/dmm.052747

BibTeX

@article{ceisel2026cell,
author = {Ceisel, Anneliese and Graziano, Gianna and Emmerich, Kevin and Shi, Xiangqian and Kam, Tae-In and Flores-Bellver, Miguel and Vergara, M. Natalia and Aparicio-Domingo, Silvia and Brenerman, Boris M. and Vielle, Anne and Williams, Elsie M. and Sharrock, Abigail V. and Onuchuwku, Uche and Martinez, Georgina S. and Sanders, Lydia G. and Nwagbo, Uzoamaka and Wang, Beichen and Xiao, Huanhuan and Kroeschell, Grant and Gao, Yiqi and Choe, Daniel J. and Clouatre, Caroline E. and Carcoba, Diego Alfaro and Reibman, Barak and Rodriguez, Catalina and Yang, Kevin and Banerjee, Shreya and Matthews, Frazer and Thierer, James H. and Stein-O'Brien, Genevieve and Dawson, Ted M. and Dawson, Valina L. and Ackerley, David F. and Canto-Soler, M. Valeria and Zhang, Liyun and Mumm, Jeff S.},
title = {{Cell death analysis of inducible, titratable neurodegenerative disease models in zebrafish and human stem cell-derived retinal organoids}},
journal = {Disease models \& mechanisms},
year = {2026},
month = jul,
volume = {19},
number = {7},
pages = {dmm052747},
publisher = {Company of Biologists},
issn = {1754-8403},
doi = {10.1242/dmm.052747},
url = {https://doi.org/10.1242/dmm.052747},
pmid = {42497345},
pmcid = {PMC13474575}
}

RIS

TY - JOUR
AU - Ceisel, Anneliese
AU - Graziano, Gianna
AU - Emmerich, Kevin
AU - Shi, Xiangqian
AU - Kam, Tae-In
AU - Flores-Bellver, Miguel
AU - Vergara, M. Natalia
AU - Aparicio-Domingo, Silvia
AU - Brenerman, Boris M.
AU - Vielle, Anne
AU - Williams, Elsie M.
AU - Sharrock, Abigail V.
AU - Onuchuwku, Uche
AU - Martinez, Georgina S.
AU - Sanders, Lydia G.
AU - Nwagbo, Uzoamaka
AU - Wang, Beichen
AU - Xiao, Huanhuan
AU - Kroeschell, Grant
AU - Gao, Yiqi
AU - Choe, Daniel J.
AU - Clouatre, Caroline E.
AU - Carcoba, Diego Alfaro
AU - Reibman, Barak
AU - Rodriguez, Catalina
AU - Yang, Kevin
AU - Banerjee, Shreya
AU - Matthews, Frazer
AU - Thierer, James H.
AU - Stein-O'Brien, Genevieve
AU - Dawson, Ted M.
AU - Dawson, Valina L.
AU - Ackerley, David F.
AU - Canto-Soler, M. Valeria
AU - Zhang, Liyun
AU - Mumm, Jeff S.
TI - Cell death analysis of inducible, titratable neurodegenerative disease models in zebrafish and human stem cell-derived retinal organoids
T2 - Disease models & mechanisms
J2 - Dis Model Mech
PY - 2026
DA - 2026/07/24
VL - 19
IS - 7
SP - dmm052747
SN - 1754-8403
PB - Company of Biologists
DO - 10.1242/dmm.052747
UR - https://doi.org/10.1242/dmm.052747
LA - en
ER -

CSL-JSON

{
"id": "10.1242/dmm.052747",
"type": "article-journal",
"title": "Cell death analysis of inducible, titratable neurodegenerative disease models in zebrafish and human stem cell-derived retinal organoids",
"container-title": "Disease models & mechanisms",
"author": [
{
"family": "Ceisel",
"given": "Anneliese"
},
{
"family": "Graziano",
"given": "Gianna"
},
{
"family": "Emmerich",
"given": "Kevin"
},
{
"family": "Shi",
"given": "Xiangqian"
},
{
"family": "Kam",
"given": "Tae-In"
},
{
"family": "Flores-Bellver",
"given": "Miguel"
},
{
"family": "Vergara",
"given": "M. Natalia"
},
{
"family": "Aparicio-Domingo",
"given": "Silvia"
},
{
"family": "Brenerman",
"given": "Boris M."
},
{
"family": "Vielle",
"given": "Anne"
},
{
"family": "Williams",
"given": "Elsie M."
},
{
"family": "Sharrock",
"given": "Abigail V."
},
{
"family": "Onuchuwku",
"given": "Uche"
},
{
"family": "Martinez",
"given": "Georgina S."
},
{
"family": "Sanders",
"given": "Lydia G."
},
{
"family": "Nwagbo",
"given": "Uzoamaka"
},
{
"family": "Wang",
"given": "Beichen"
},
{
"family": "Xiao",
"given": "Huanhuan"
},
{
"family": "Kroeschell",
"given": "Grant"
},
{
"family": "Gao",
"given": "Yiqi"
},
{
"family": "Choe",
"given": "Daniel J."
},
{
"family": "Clouatre",
"given": "Caroline E."
},
{
"family": "Carcoba",
"given": "Diego Alfaro"
},
{
"family": "Reibman",
"given": "Barak"
},
{
"family": "Rodriguez",
"given": "Catalina"
},
{
"family": "Yang",
"given": "Kevin"
},
{
"family": "Banerjee",
"given": "Shreya"
},
{
"family": "Matthews",
"given": "Frazer"
},
{
"family": "Thierer",
"given": "James H."
},
{
"family": "Stein-O'Brien",
"given": "Genevieve"
},
{
"family": "Dawson",
"given": "Ted M."
},
{
"family": "Dawson",
"given": "Valina L."
},
{
"family": "Ackerley",
"given": "David F."
},
{
"family": "Canto-Soler",
"given": "M. Valeria"
},
{
"family": "Zhang",
"given": "Liyun"
},
{
"family": "Mumm",
"given": "Jeff S."
}
],
"container-title-short": "Dis Model Mech",
"volume": "19",
"issue": "7",
"page": "dmm052747",
"DOI": "10.1242/dmm.052747",
"PMID": "42497345",
"PMCID": "PMC13474575",
"ISSN": "1754-8403",
"publisher": "Company of Biologists",
"URL": "https://doi.org/10.1242/dmm.052747",
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
24
]
]
}
}

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.stemcr.2026.102966
Optimized chemogenetic ablation and regeneration of enteric nervous system neurons in zebrafish.
Journal: Stem cell reports
In common: zebrafish, 5 references, author David F. Ackerley
[2] doi:10.1126/sciadv.aeg3223 [code]
The extreme diversity of retinal amacrine cells has deep evolutionary roots.
Journal: Science advances
In common: anndata, Scanpy, scikit-learn, 4 other tools, cellular / molecular, 1 reference
[3] doi:10.1016/j.celrep.2026.117270 [code]
Blocking apoptosis promotes survival and alters developmental dynamics of human retinal ganglion cells in retinal organoids.
Journal: Cell reports
In common: anndata, Scanpy, scikit-learn, 4 other tools, 1 reference
[4] doi:10.1016/j.xcrm.2026.102766 [code]
A longitudinal single-cell and spatial multiomic atlas of pediatric high-grade glioma.
Journal: Cell reports. Medicine
In common: anndata, Scanpy, scikit-learn, 4 other tools, other condition, cellular / molecular, 1 reference
[5] doi:10.1038/s41586-026-10658-6 [code]
An AI system to help scientists write expert-level empirical software.
Journal: Nature
In common: anndata, Scanpy, scikit-learn, 4 other tools, zebrafish, other condition
[6] doi:10.1038/s41586-026-10629-x [code]
Whole-genome duplication shaped cell-type evolution in the vertebrate brain.
Journal: Nature
In common: anndata, Scanpy, scikit-learn, 4 other tools, cellular / molecular, 1 reference
[7] doi:10.1038/s41592-026-03057-2 [code]
CREsted: modeling genomic and synthetic cell-type-specific enhancers across tissues and species.
Journal: Nature methods
In common: anndata, Scanpy, scikit-learn, 4 other tools, zebrafish
[8] doi:10.1371/journal.pcbi.1014327 [code]
Supervised deep learning with gene functional annotation for cell classification.
Journal: PLoS computational biology
In common: anndata, Scanpy, scikit-learn, 4 other tools, other condition, cellular / molecular
[9] doi:10.1038/s41593-026-02300-5 [code]
Integrated single-cell and spatial transcriptomic profiling in ALS uncovers peripheral-to-central immune infiltration and reprogramming.
Journal: Nature neuroscience
In common: anndata, Scanpy, scikit-learn, 4 other tools, other condition, cellular / molecular
[10] doi:10.1093/bib/bbag404 [code]
Navigating cell maps by deep learning integration of single-cell and spatially resolved transcriptomics.
Journal: Briefings in bioinformatics
In common: anndata, Scanpy, scikit-learn, 4 other tools, other condition, cellular / molecular

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.