Cell2Fate analysis of mouse dentate gyrus dataset

[1]:
import cell2fate as c2f
import scanpy as sc
import numpy as np
import matplotlib.pyplot as plt
import os
data_name = 'DentateGyrus'
Global seed set to 0

You need to change this to suitable directories on your system:

[2]:
#  where to download data and where to save results
data_path = '/home/jupyter/data/benchmarking_datasets/DentateGyrus/'
results_path = '/home/jupyter/data/cell2fate_paper_results/DentateGyrus/'
[3]:
# Downloading data into specified directory:
os.system('cd ' + data_path + ' && wget -q https://cell2fate.cog.sanger.ac.uk/' + data_name + '/' + data_name + '_anndata.h5ad')
[3]:
0

Load the data and extract most variable genes (and optionally remove some clusters).

[4]:
adata = sc.read_h5ad(data_path + data_name + '_anndata.h5ad')
background = adata.var_names
clusters_to_remove = []
adata = c2f.utils.get_training_data(adata, cells_per_cluster = 10**5, cluster_column = 'clusters',
                                    remove_clusters = clusters_to_remove,
                                min_shared_counts = 20, n_var_genes= 3000)
Keeping at most 100000 cells per cluster
Filtered out 10340 genes that are detected 20 counts (shared).
Extracted 3000 highly variable genes.

Overview of the dataset on a UMAP, coloured by cluster assingment.

[5]:
fig, ax = plt.subplots(1,1, figsize = (6, 4))
sc.pl.umap(adata, color = ['clusters'], s = 200, legend_loc='on data', show = False, ax = ax)
plt.savefig(results_path + data_name + 'UMAP_clusters.pdf')
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_8_0.png

As usual in the scvi-tools workflow we register the anndata object …

[6]:
c2f.Cell2fate_DynamicalModel.setup_anndata(adata, spliced_label='spliced', unspliced_label='unspliced')

… and initialize the model:

[7]:
max_modules = c2f.utils.get_max_modules(adata)
mod = c2f.Cell2fate_DynamicalModel(adata, n_modules = max_modules)
Leiden clustering ...
WARNING: You’re trying to run this on 507 dimensions of `.X`, if you really want this, set `use_rep='X'`.
         Falling back to preprocessing with `sc.pp.pca` and default params.
Number of Leiden Clusters: 14
Maximal Number of Modules: 16

Let’s have a look at the anndata setup:

[8]:
mod.view_anndata_setup()
Anndata setup with scvi-tools version 0.16.1.
Setup via `Cell2fate_DynamicalModel.setup_anndata` with arguments:
{
'layer': None,
'batch_key': None,
'labels_key': None,
'unspliced_label': 'unspliced',
'spliced_label': 'spliced',
'cluster_label': None
}
     Summary Statistics     
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━┓
┃ Summary Stat Key  Value ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━┩
│     n_cells       2930  │
│      n_vars       3000  │
│     n_batch         1   │
└──────────────────┴───────┘
               Data Registry                
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Registry Key     scvi-tools Location    ┃
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│  unspliced    adata.layers['unspliced'] │
│   spliced      adata.layers['spliced']  │
│    batch      adata.obs['_scvi_batch']  │
│    ind_x        adata.obs['_indices']   │
└──────────────┴───────────────────────────┘
                     batch State Registry                      
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃     Source Location       Categories  scvi-tools Encoding ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩
│ adata.obs['_scvi_batch']      0                0          │
└──────────────────────────┴────────────┴─────────────────────┘

Training the model:

[9]:
mod.train()
GPU available: True, used: True
TPU available: False, using: 0 TPU cores
IPU available: False, using: 0 IPUs
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]
Epoch 500/500: 100%|██████████| 500/500 [15:52<00:00,  1.91s/it, v_num=1, elbo_train=6.75e+6]

We plot training history over multiple windows to effectively assess convergence (which is not reached here but it is close.)

[10]:
mod.view_history()
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_18_0.png

Here we export the model posterior to the anndata object:

[11]:
adata = mod.export_posterior(adata)
sample_kwargs['batch_size'] 2930
Sampling local variables, batch: 100%|██████████| 1/1 [00:21<00:00, 21.38s/it]
Sampling global variables, sample: 100%|██████████| 29/29 [00:17<00:00,  1.62it/s]
Warning: Saving ALL posterior samples. Specify "return_samples: False" to save just summary statistics.

One of the interesting parameter posteriors that was saved to the anndata object is the differentiation time:

[12]:
fig, ax = plt.subplots(1,2, figsize = (15, 5))
sc.pl.umap(adata, color = ['Time (hours)'], legend_loc = 'right margin',
                size = 200, color_map = 'inferno', ncols = 2, show = False, ax = ax[0])
sc.pl.umap(adata, color = ['Time Uncertainty (sd)'], legend_loc = 'right margin',
                size = 200, color_map = 'inferno', ncols = 2, show = False, ax = ax[1])
plt.savefig(results_path + data_name + 'UMAP_Time.pdf')
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_22_0.png
[13]:
mod.compare_module_activation(adata, chosen_modules = [0,1,2,4,5,6,7,8,9], time_min = 11, time_max = 69,
                         save = results_path + data_name + 'module_activation_comparison.pdf', ncol = 3)
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_23_0.png
[14]:
# import scipy
# import torch
# from cell2fate.utils import mu_mRNA_continousAlpha_globalTime_twoStates

# def compute_module_summary_statistics(self, adata):
#     '''Computes the contribution of each module to mRNA molecules in each cell'''
#     if scipy.sparse.issparse(self.adata_manager.get_from_registry('unspliced')):
#         observed_total = torch.sum(torch.sum(torch.stack([torch.tensor(self.adata_manager.get_from_registry('unspliced').toarray()),
#                       torch.tensor(self.adata_manager.get_from_registry('spliced').toarray())], axis = -1), axis = -1), axis = -1)
#     else:
#         observed_total = torch.sum(torch.sum(torch.stack([torch.tensor(self.adata_manager.get_from_registry('unspliced')),
#                       torch.tensor(self.adata_manager.get_from_registry('spliced'))], axis = -1), axis = -1), axis = -1)
#     inferred_total = torch.sum(torch.sum(torch.tensor(self.samples['post_sample_means']['mu_expression']), axis = -1), axis = -1)
#     for m in range(self.module.model.n_modules):
#         mu_m = mu_mRNA_continousAlpha_globalTime_twoStates(
#             torch.tensor(self.samples['post_sample_means']['A_mgON'][m,:]),
#             torch.tensor(0.),
#             torch.tensor(self.samples['post_sample_means']['beta_g']),
#             torch.tensor(self.samples['post_sample_means']['gamma_g']),
#             torch.tensor(self.samples['post_sample_means']['lam_mi'][m,:]),
#             torch.tensor(self.samples['post_sample_means']['T_c'][:,:,0]),
#             torch.tensor(self.samples['post_sample_means']['T_mON'][:,:,m]),
#             torch.tensor(self.samples['post_sample_means']['T_mOFF'][:,:,m]),
#             torch.zeros((self.module.model.n_obs, self.module.model.n_vars)))
#         ss_total = torch.sum(torch.tensor(self.samples['post_sample_means']['A_mgON'][m,:])/torch.tensor(self.samples['post_sample_means']['gamma_g']) + \
#     torch.tensor(self.samples['post_sample_means']['A_mgON'][m,:])/torch.tensor(self.samples['post_sample_means']['beta_g']), axis = 1)
#         adata.obs['Module ' + str(m) + ' Activation'] = torch.sum(torch.sum(mu_m, axis = -1), axis = -1)/ss_total
#         adata.obs['Module ' + str(m) + ' State'] = 'OFF'
#         adata.obs['Module ' + str(m) + ' State'
#          ][self.samples['post_sample_means']['T_c'][:,0,0] > self.samples['post_sample_means']['T_mON'][0,0,m]
#           ] = 'Induction'
#         adata.obs['Module ' + str(m) + ' State'
#          ][self.samples['post_sample_means']['T_c'][:,0,0] > self.samples['post_sample_means']['T_mOFF'][0,0,m]
#           ] = 'Repression'
#         adata.obs['Module ' + str(m) + ' State'
#          ][adata.obs['Module ' + str(m) + ' Activation'] > 0.95
#           ] = 'ON'
#         adata.obs['Module ' + str(m) + ' State'
#          ][adata.obs['Module ' + str(m) + ' Activation'] < 0.05
#           ] = 'OFF'
#         adata.obs['Module ' + str(m) + ' Activation'] = torch.sum(torch.sum(mu_m, axis = -1), axis = -1)
#     return adata
[15]:
# def plot_module_summary_statistics(self, adata, save = None):
#     'Weight, Activation, Velocity, Switch ON/OFF time (histogram)'
#     limit = np.max([np.sort(adata.obs['Module ' + str(i) + ' Activation'])[int(np.round(0.99*len(adata.obs['Module ' + str(i) + ' Activation'])))] for i in range(self.module.model.n_modules)])
#     fig, ax = plt.subplots(self.module.model.n_modules, 2, figsize = (10, 4*self.module.model.n_modules))
#     for i in range(self.module.model.n_modules):
#         sc.pl.umap(adata, color = ['Module ' + str(i) + ' Activation'], legend_loc = None,
#                     size = 200, color_map = 'viridis', ax = ax[i,0], show = False, vmin = 0, vmax = limit)
#         sc.pl.umap(adata, color = ['Module ' + str(i) + ' State'], legend_loc = 'on data',
#                     size = 200, ax = ax[i,1], show = False,
#                    palette =  {'ON': 'lime', 'OFF': 'grey', 'Induction': 'lightgreen', 'Repression': 'orange'})
#         plt.tight_layout()
#     if save:
#         plt.savefig(save)
[16]:
# adata = compute_module_summary_statistics(mod, adata)
[17]:
# plot_module_summary_statistics(mod, adata)

We can compute some module statistics to visualize the activity of the underlying modules:

[18]:
adata = mod.compute_module_summary_statistics(adata)
mod.plot_module_summary_statistics(adata, save = results_path + data_name + 'module_summary_stats_plot.pdf')
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_29_0.png
[ ]:

This plot explains how we get module states from the module activation (see figure 3b in the paper):

[19]:
mod.example_module_activation(adata, chosen_module = 5, time_min = 35, time_max = 50,
                         save = results_path + data_name + 'module_activation_example.pdf')
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_32_0.png

It is also possible to visualize “module-specific” velocity although this does not always work well:

[20]:
# mod.compute_and_plot_module_velocity(adata, save = results_path + data_name + 'module_velocity_plots.png',
#                                     plotting_kwargs = {"color": 'clusters', 'legend_fontsize': 10,
#                                                         'legend_loc': 'right_column', 'min_mass': 4.4})

And of course we can make the usual visualization of total RNAvelocity on a UMAP:

[21]:
mod.compute_and_plot_total_velocity(adata, save = results_path + data_name + 'total_velocity_plots.png')
Computing total RNAvelocity ...
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_36_1.png

This visualization is more similar to the scvelo plots:

[22]:
import scvelo as scv
fix, ax = plt.subplots(1, 1, figsize = (8, 6))
scv.pl.velocity_embedding_stream(adata, basis='umap', save = False, vkey='Velocity',
                                 show = False, ax = ax,legend_fontsize = 8)
plt.savefig(results_path + data_name + 'total_velocity_plots.png')
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_38_0.png

Technical variables usually show lower detection efficiency and higher noise (= lower overdispersion parameter) for unspliced counts:

[23]:
mod.plot_technical_variables(adata, save = results_path + data_name + 'technical_variables_overview_plot.pdf')
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_40_0.png

This is how to have a look at the various rate parameters the optimization converged to:

[24]:
print('A_mgON mean:', np.mean(mod.samples['post_sample_means']['A_mgON']))
print('gamma_g mean:', np.mean(mod.samples['post_sample_means']['gamma_g']))
print('beta_g mean:', np.mean(mod.samples['post_sample_means']['beta_g']))
print('lam_mi, all modules: \n \n', np.round(mod.samples['post_sample_means']['lam_mi'],2))
A_mgON mean: 0.070882834
gamma_g mean: 0.800978
beta_g mean: 1.2001703
lam_mi, all modules:

 [[[3.88 3.19]]

 [[4.24 3.49]]

 [[3.2  3.39]]

 [[4.53 3.49]]

 [[2.15 2.45]]

 [[3.93 3.58]]

 [[3.85 3.32]]

 [[3.7  3.23]]

 [[5.   3.11]]

 [[4.47 3.73]]

 [[4.67 4.74]]

 [[5.17 2.86]]

 [[4.65 4.49]]

 [[4.18 4.65]]

 [[2.56 3.07]]

 [[2.5  3.17]]]

This method returns orders the genes and TFs in each module from most to least enriched. And it also performs gene set enrichment analysis:

[25]:
tab, all_results = mod.get_module_top_features(adata, background, p_adj_cutoff=0.05, n_top_genes = 10)
tab.to_csv(results_path + data_name + 'module_top_features_table.csv')

“tab” contains a concise summary and “all_results” a list with detailed enrichment scores for all modules:

[26]:
tab
[26]:
Module Number Genes Ranked TFs Ranked Terms Ranked
0 0 Gal, Rgs6, Fbl, Pcdh15, Npm3, Rpl39, Cct8, Tri... Nono, Rcor2, Smarcc1, Ezh2, Zfp410, Fos, Klf7,... positive regulation of protein localization to...
1 1 Rwdd1, Psma4, Ppa1, Skp1a, Ndufb6, Tm2d1, Emc4... Nr4a2, Camta1, Zfp397, Cebpz, Zfp704, Ybx1, Ml...
2 2 Npy, Gad2, Npas1, Gad1, Psmb10, Gria4, Gap43, ... Npas1, Arnt2, Klf7, Deaf1, Zfp706, Mllt11, Tax... gamma-aminobutyric acid metabolic process (GO:...
3 3 Gng4, B3gat3, Brinp2, Megf8, Tshz2, Satb1, Mgr... Tshz2, Satb1, Kat5, Nr2f2, Thrb, Deaf1, Zmat4,... heparan sulfate proteoglycan metabolic process...
4 4 Pds5b, Rbm24, Aagab, Foxo1, Sptb, Fam19a1, Fam... Foxo1, Atf6, Atf6b, Nfx1, Arid5b, Zc3h13, Zfp1... cellular response to DNA damage stimulus (GO:0...
5 5 Dgkh, Palm2, Ptprj, Larp1b, Pdzd2, Usp19, Ppp6... Parp1, Rbm22, Rfx3, Zfp106, Zbtb37, Dnajc21, B... negative regulation of cell motility (GO:20001...
6 6 Fst, Rapgef5, Moxd1, Nup88, Stard13, Adarb2, S... Zbtb16, Zmat4, Prdm5, Parp1, Ncoa2, Hmbox1, Ph... regulation of cellular response to heat (GO:19...
7 7 Kdm5d, Prr16, Akr1e1, Swt1, Rpa3, Figf, Dpagt1... Kdm5d, Tfam, Zfp511, Prdm5, Zfp12, Mynn, Zfp9,... axo-dendritic transport (GO:0008088), dolichyl...
8 8 Nudt13, Rgs2, Hscb, Numa1, 1700048O20Rik, Tnnt... Nr4a2, Irf9, Mynn, Tfam, Kdm5a, Zfp414, Zfp292... positive regulation of cardiac muscle contract...
9 9 Coq3, Nup160, Sugp1, Smurf2, Ankrd26, Sdccag8,... Nr4a2, Arnt, Zfp12, Zfp511, Tfam, Mynn, Kdm5a,... positive regulation of protein metabolic proce...
10 10 Cbwd1, Prmt10, Tbc1d7, Snhg12, Utp14a, Cyb5b, ... Hsf2, Mynn, Ahr, Zfp12, Klf9, Cdc5l, Zhx2, Zfp... regulation of TOR signaling (GO:0032006), posi...
11 11 Exosc10, 5330417C22Rik, 9130011E15Rik, Prmt10,... Mynn, Thrb, Cdc5l, Ncoa2, Mier1, Hsf2, Arnt, F... mitotic sister chromatid segregation (GO:00000...
12 12 Matn4, Tmem163, Col11a1, Ppp2r2b, Cacng4, Sox6... Sox6, Ezh2, Smad1, Cdc5l, Nono, Rfx7, Rcor2, A... positive regulation of hydrolase activity (GO:...
13 13 Bcas1, Sox10, Acta2, Cldn11, Tyrobp, P2ry12, H... Sox10, Erg, Tcf3, Klf2, Zfp410, Tsc22d1, Sox8,... response to axon injury (GO:0048678), myelinat...
14 14 Mog, Ppp1r14a, Mag, Arpc1b, Sept4, Spi1, B2m, ... Nfkbia, Fli1, Klf2, Nfic, Klf10, Id3, Foxn3, I... negative regulation of neurogenesis (GO:005076...
15 15 Gfap, Tnc, Gja1, Clu, Mt2, Slc1a3, Hepacam, Ph... Nr2e1, Rorb, Npas3, Id3, Sox6, Zfp398, Zfp651,... neurotransmitter uptake (GO:0001504), cell com...
[27]:
all_results[9]
[27]:
Gene_set Term Overlap P-value Adjusted P-value Old P-value Old Adjusted P-value Odds Ratio Combined Score Genes
0 GO_Biological_Process_2021 positive regulation of protein metabolic proce... 3/115 0.000022 0.004920 0 0 76.063776 817.214231 SMURF2;IL18;INHBA
1 GO_Biological_Process_2021 positive regulation of reproductive process (G... 2/35 0.000133 0.015128 0 0 151.189394 1349.727435 SMURF2;INHBA
2 GO_Biological_Process_2021 positive regulation of multicellular organisma... 3/345 0.000558 0.034648 0 0 24.621554 184.441905 SMURF2;IL18;INHBA
3 GO_Biological_Process_2021 regulation of interferon-gamma production (GO:... 2/86 0.000804 0.034648 0 0 59.244048 422.155178 IL18;INHBA
4 GO_Biological_Process_2021 regulation of phosphorylation (GO:0042325) 2/125 0.001688 0.034648 0 0 40.380081 257.806750 INHBA;NUP160
... ... ... ... ... ... ... ... ... ... ...
72 GO_Biological_Process_2021 regulation of interleukin-17 production (GO:00... 1/33 0.016382 0.049965 0 0 69.298611 284.928150 IL18
73 GO_Biological_Process_2021 protein modification by small protein conjugat... 2/409 0.016841 0.049965 0 0 12.028870 49.125003 SMURF2;NUP160
74 GO_Biological_Process_2021 regulation of activated T cell proliferation (... 1/34 0.016874 0.049965 0 0 67.195286 274.289235 IL18
75 GO_Biological_Process_2021 tRNA export from nucleus (GO:0006409) 1/34 0.016874 0.049965 0 0 67.195286 274.289235 NUP160
76 GO_Biological_Process_2021 tRNA-containing ribonucleoprotein complex expo... 1/34 0.016874 0.049965 0 0 67.195286 274.289235 NUP160

77 rows × 10 columns

We can plot top marker genes:

[ ]:
mod.plot_top_features(adata, tab, chosen_modules = [0,1,2,4,5,6,7,8,9], mode = 'all genes', n_top_features = 7, process = True,
                      save = results_path + data_name + 'top_genes_plot.pdf')
Reprocessing adata.X, set process = False if this is not desired.

… as well as the top TFs for each module:

[ ]:
mod.plot_top_features(adata, tab, chosen_modules =  [0,1,2,4,5,6,7,8,9], mode = 'TFs', n_top_features = 7, process = True,
                      save = results_path + data_name + 'top_TFs_plot.pdf')

This is another plotting functions to show marker genes and summary statistics for each module in a convenient format:

[ ]:
# def plot_module_summary_statistics_2(self, adata, chosen_modules, chosen_clusters,
#                                  marker_genes, marker_TFs, cluster_key = 'clusters', save = None):
#     'tbd'
#     limit = np.max([np.sort(adata.obs['Module ' + str(i) + ' Activation'])[int(np.round(0.99*len(adata.obs['Module ' + str(i) + ' Activation'])))]
#                     for i in chosen_modules])
#     adata.X = adata.layers['unspliced'] + adata.layers['spliced']
#     sc.pp.normalize_total(adata, target_sum=1e4)
#     sc.pp.log1p(adata)
#     sc.pp.scale(adata, max_value=10)
#     plt.rcParams.update({'font.size': 14})
#     subset = np.array([c in chosen_clusters for c in adata.obs[cluster_key]])
#     plt.scatter(1, 1, label='Induction', s = 100, c='lightgreen')
#     plt.scatter(1, 1, label='ON', s = 100, c='lime')
#     plt.scatter(1, 1, label='Repression', s = 100, c='orange')
#     plt.scatter(1, 1, label='OFF', s = 100, c='grey')
#     plt.legend()
#     if save:
#         plt.savefig(save[:-4] + '_legend.pdf')
#     plt.show()
#     fig, ax = plt.subplots(4, len(chosen_modules), figsize = (3*len(chosen_modules), 20))
#     adata = adata[subset,:]
#     for i in range(len(chosen_modules)):
#         m = chosen_modules[i]
#         sc.pl.umap(adata, color = ['Module ' + str(m) + ' Activation'], legend_loc = None,
#                     size = 200, color_map = 'viridis', ax = ax[0,i], show = False, s = 300,
#                   vmin = 0, vmax = limit)
#         sc.pl.umap(adata, color = ['Module ' + str(m) + ' State'],
#                     size = 200, ax = ax[1,i], show = False, legend_fontsize = 'x-large', s = 300, legend_loc = 'right_margin',
#                    palette = {'ON': 'lime', 'OFF': 'grey', 'Induction': 'lightgreen', 'Repression': 'orange'})
#         sc.pl.umap(adata, color = marker_genes[i], legend_loc = 'right margin',
#                         size = 200, show = False, ax = ax[2,i])
#         sc.pl.umap(adata, color = marker_TFs[i], legend_loc = 'right margin',
#                         size = 200, show = False, ax = ax[3,i])
#         plt.tight_layout()
#     if save:
#         plt.savefig(save)
[33]:
mod.plot_module_summary_statistics_2(adata,
                                 chosen_modules = [4,5,6,7,8,9],
                                 chosen_clusters = ['Radial Glia-like', 'Astrocytes', 'nIPC', 'Neuroblast',
                                                    'Granule immature', 'Granule mature'],
                                 marker_genes = ['Rbm24', 'Palm2',
                                    'Fst', 'Prr16', 'Rgs2', 'Nup160'],
                                 marker_TFs = ['Arid5b', 'Rfx3', 'Zmat4', 'Kdm5d', 'Irf9', 'Tfam'],
                                 save = results_path + data_name + 'module_overview_plot.pdf')
WARNING: adata.X seems to be already log-transformed.
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_54_1.png
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_54_2.png

And for simply plotting selected genes we can use this function:

[34]:
del adata.obsp
del adata.uns['Velocity_graph']
mod.plot_genes(adata,
           chosen_clusters = ['Radial Glia-like', 'Astrocytes', 'nIPC', 'Neuroblast',
                                                    'Granule immature', 'Granule mature'],
           marker_genes = ['Rapgef5', 'Moxd1', 'Prr16', 'Rpa3', '1700048O20Rik', 'Spg11', 'Ntng1'],
           save = results_path + data_name + 'additional_module_genes_plot.pdf')
WARNING: adata.X seems to be already log-transformed.
../../_images/notebooks_publication_figures_cell2fate_DentateGyrus_56_1.png