Single-cell machine learning uncovers genetically anchored, cell-type specific programs of Alzheimer's disease.
The 40 matches
- [1] § Methods › Cell type annotations across datasets ↔ revision/sea-ad_validation_analysis/pseudotime/label_seaad_subpops_centroid.py, lines 1 to 54 · score 0.96 · highest Pearson correlation, Mathys reference matrix, centroid correlations, expression vector, SEA AD cell, shared transcriptome · Report a mismatch
- [2] § Methods › GWAS enrichment ↔ data_tables.ipynb, lines 1190 to 1337 · score 0.88 · mappedGenes, NHGRI EBI, background gene, odds ratios, Overlapping genes, mondo · Report a mismatch
- [3] § Results › Cell type-specific biomarker discovery beyond demographics and APOE genotype ↔ revision/Revision_Figures/drug_bank_target_identification.ipynb, lines 172 to 229 · score 0.85 · HIF1A, COL4A3, IFI44L, ARL17B, CLDN11, stress · Report a mismatch
- [4] § Methods › Pseudotime inference and trajectory analysis ↔ revision/sea-ad_validation_analysis/pseudotime/pseudotime_seaad_centroid.py, lines 193 to 232 · score 0.84 · neighbor graph, highly variable genes, diffusion map, Scanpy, fewer, Seurat · Report a mismatch
- [5] § Methods › TriSCOPE predictive modelling ↔ revision/sea-ad_validation_analysis/Multirun_cell_on_cell_genes_apoe_seaad.py, lines 490 to 575 · score 0.82 · precision recall curve, Prediction probabilities, AutoML, ROC AUC, F1 score, retraining · Report a mismatch
- [6] § Methods › TriSCOPE predictive modelling ↔ revision/sea-ad_validation_analysis/ein_Multirun_cell_on_cell_genes_apoe_seaad.py, lines 509 to 594 · score 0.82 · precision recall curve, Prediction probabilities, AutoML, ROC AUC, F1 score, retraining · Report a mismatch
- [7] § Results › Subcluster profiles reveal transcriptional gradients of ad progression ↔ revision/Revision_Figures/drug_bank_target_identification.ipynb, lines 361 to 427 · score 0.81 · LRP1B, HSP90AA1, trajectory influencing, AD genes, canonical, NKAIN2 · Report a mismatch
- [8] § Methods › GWAS–eQTL colocalization analysis ↔ revision/code/Coloc.R, lines 139 to 203 · score 0.81 · eQTLs, odds ratios, deduplicated, pos, chromosomes, allele · Report a mismatch
- [9] § Methods › Cell type annotations across datasets ↔ revision/sea-ad_validation_analysis/pseudotime/label_seaad_subpops.py, lines 1 to 80 · score 0.78 · Mathys reference matrix, SEA AD broad, centroid correlations, Pearson, margin, confidence · Report a mismatch
- [10] § Methods › Cell type annotations across datasets ↔ revision/sea-ad_validation_analysis/pseudotime/pseudotime_seaad_centroid.py, lines 193 to 232 · score 0.77 · highly variable genes, Scanpy, flavor, graph, seurat, neighbors · Report a mismatch
- [11] § Methods › Cell type annotations across datasets ↔ revision/Revision_Figures/pseudotime_hvg.ipynb, lines 44 to 82 · score 0.77 · highly variable genes, AnnData, Scanpy, HVGs, flavor, seurat · Report a mismatch
- [12] § Methods › GWAS–eQTL colocalization analysis ↔ revision/code/Coloc.R, lines 139 to 203 · score 0.76 · GTEx v10, eQTLs, chromosome, allele, variant, position · Report a mismatch
- [13] § Methods › Pathway analysis of predictive and differentially expressed gene features ↔ figure2.ipynb, lines 705 to 844 · score 0.75 · GO_Molecular_Function_2023, prerank, GSEA, barplots, Inhibitory Neurons, pathway · Report a mismatch
- [14] § Methods › Data source and cohort selection ↔ revision/sea-ad_validation_analysis/pseudotime/pseudotime_seaad_centroid.py, lines 1 to 68 · score 0.74 · AD neuropathological Change, cognitive status, SEA AD, Intermediate, dementia, definitions · Report a mismatch
- [15] § Methods › Pathway analysis of predictive and differentially expressed gene features ↔ revision/Revision_Figures/figure_6_revision.ipynb, lines 190 to 336 · score 0.73 · GO_Molecular_Function_2023, prerank, GSEA, barplots, Inhibitory Neurons, pathway · Report a mismatch
- [16] § Results › Subcluster level expression improves AD prediction ↔ revision/Revision_Figures/drug_bank_target_identification.ipynb, lines 172 to 229 · score 0.73 · P2RY14, RASGEF1B, CA4, CPLX3, PNISR, TIMP3 · Report a mismatch
- [17] § Results › Genetic colocalization anchors cell-type–specific transcriptional predictors ↔ revision/Revision_Figures/Figure_10_revision.ipynb, lines 1 to 95 · score 0.72 · Strong colocalization, pp h4, AD GWAS, eQTL, tissue, 1 % · Report a mismatch
- [18] § Results › Cell type-specific biomarker discovery beyond demographics and APOE genotype ↔ figure2.ipynb, lines 451 to 566 · score 0.70 · square root, raw importance, APOE genotype, Inhibitory Neurons, Top predictive, predictive features · Report a mismatch
- [19] § Methods › External validation using independent differential expression studies ↔ revision/Revision_Figures/correlations_table.ipynb, lines 2796 to 2833 · score 0.69 · external DEG ranks, absolute log fold, Spearman rank correlation, predictor ranks, fold change · Report a mismatch
- [20] § Methods › Pseudotime inference and trajectory analysis ↔ revision/Revision_Figures/pseudotime_hvg.ipynb, lines 44 to 82 · score 0.68 · highly variable genes, Scanpy, HVGs, fewer, circularity, Seurat · Report a mismatch
- [21] § Methods › Differential expression analysis ↔ revision/code/Contrasts_DE_w_RandomEffect.R, lines 298 to 339 · score 0.67 · edgeR, RUV covariates, Deviance, RUVr, residual, seq · Report a mismatch
- [22] § Methods › Clinical and neuropathological definitions of alzheimer’s disease ↔ revision/sea-ad_validation_analysis/pseudotime/pseudotime_seaad_centroid.py, lines 1 to 68 · score 0.66 · probable AD, cognitive status, moderate, Dementia, definitions, DCFDX · Report a mismatch
- [23] § Methods › Differential expression analysis ↔ revision/sea-ad_validation_analysis/DEG_SEAAD_chunking.R, lines 265 to 344 · score 0.66 · nAGQ, lme4, bobyqa, glmer, optimizer, coefficients · Report a mismatch
- [24] § Methods › Differential expression analysis ↔ revision/code/Contrasts_DE_w_RandomEffect.R, lines 385 to 439 · score 0.66 · nAGQ, lme4, bobyqa, glmer, optimizer, coefficients · Report a mismatch
- [25] § Methods › Data source and cohort selection ↔ revision/sea-ad_validation_analysis/data_confirmation.ipynb, lines 27 to 84 · score 0.65 · AD neuropathological Change, cognitive status, neurotypical, Intermediate, dementia, SEA AD · Report a mismatch
- [26] § Methods › Pseudotime inference and trajectory analysis ↔ revision/Revision_Figures/pseudotime_hvg.ipynb, lines 84 to 132 · score 0.64 · selected root cell, random seed, Diffusion, DPT, candidate, robustness · Report a mismatch
- [27] § Methods › Pseudotime inference and trajectory analysis ↔ revision/Revision_Figures/runtime_analysis.ipynb, lines 303 to 351 · score 0.63 · selected root cell, random seed, Diffusion, DPT, candidate, robustness · Report a mismatch
- [28] § Methods › Pseudotime inference and trajectory analysis ↔ Trajectory_Inference.py, lines 243 to 283 · score 0.63 · Trajectory inference, odds ratios, logistic regression, pseudotime, seed, ROC · Report a mismatch
- [29] § Results › Subcluster level expression improves AD prediction ↔ Trajectory_Inference.py, lines 372 to 413 · score 0.63 · Smoothed expression, Pseudotime trajectory, UMAP, top trajectory, DPT, status · Report a mismatch
- [30] § Methods › Differential expression analysis ↔ revision/code/Contrasts_DE_w_RandomEffect.R, lines 211 to 253 · score 0.62 · RUVSeq, dcfdx_lv, clinical diagnosis, neuropathological, CERAD, donor · Report a mismatch
- [31] § Results › Genetic colocalization anchors cell-type–specific transcriptional predictors ↔ revision/Revision_Figures/drug_bank_target_identification.ipynb, lines 361 to 427 · score 0.62 · USP6NL, ARL17B, NDUFAF6, EGFR, CLU, colocalization · Report a mismatch
- [32] § Results › Donor-aware differential expression contextualizes cell-type-specific predictors of AD ↔ figure6.ipynb, lines 725 to 864 · score 0.62 · log2 fold change, FDR adjusted, overlapping genes, Volcano, Inhibitory Neurons, Excitatory Neurons · Report a mismatch
- [33] § Results › Subcluster level expression improves AD prediction ↔ revision/sea-ad_validation_analysis/pseudotime/pseudotime_seaad_centroid.py, lines 400 to 465 · score 0.61 · Smoothed expression, Pseudotime trajectory, UMAP, top trajectory, subcluster, ranked · Report a mismatch
- [34] § Results › Donor-aware differential expression contextualizes cell-type-specific predictors of AD ↔ data_tables.ipynb, lines 1039 to 1188 · score 0.61 · Benjamini Hochberg, Oligodendrocyte Progenitor Cells, overlapping genes, Inhibitory Neurons, Excitatory Neurons, log10 · Report a mismatch
- [35] § Results › Genetic colocalization anchors cell-type–specific transcriptional predictors ↔ revision/code/Coloc.R, lines 1 to 45 · score 0.61 · eQTLs, brain tissue, coloc, predictive gene, GWAS · Report a mismatch
- [36] § Methods › Differential expression analysis ↔ figure8.ipynb, lines 171 to 212 · score 0.60 · post mortem interval, dcfdx_lv, PMI, diagnosis · Report a mismatch
- [37] § Methods › Predictive validation cohort ↔ revision/Revision_Figures/correlations_table.ipynb, lines 593 to 732 · score 0.58 · smaller predictor, nonzero importance, correlations, recall, overlap, splits · Report a mismatch
- [38] § Methods › Single-nucleus RNA-seq preprocessing and quality control ↔ figure8.ipynb, lines 171 to 212 · score 0.58 · Post mortem interval, FDR adjusted, PMI, diagnostic, metadata · Report a mismatch
- [39] § Methods › SHAP-based model interpretation stratified by APOE genotype and sex ↔ revision/Revision_Figures/Figure_9_revision.ipynb, lines 58 to 112 · score 0.58 · TreeExplainer, SHAP, booster, female, aggregated, sex · Report a mismatch
- [40] § Methods › GWAS–eQTL colocalization analysis ↔ revision/code/Coloc.R, lines 301 to 364 · score 0.58 · coloc.abf, eQTL, SNPs, tissue, GWAS, brain · Report a mismatch
Paper
Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 609 lines · 28 KB · MIT · 5 matches
- """
- pseudotime_seaad_centroid.py
- Per-subcluster pseudotime pipeline on SEA-AD, using centroid-labeled
- subpopulations (top1 from label_seaad_subpops_centroid.py outputs).
- Script body is a copy of the Mathys per-subcluster pseudotime analysis from
- /home/adm808/Analysis_Scripts/Plotting_Figures/pseudotime_hvg.ipynb (cell 0)
- with the following and ONLY the following changes:
- 1. Data loaded from SEA-AD parquets instead of the Mathys CellMatrix:
- expression -> /n/groups/patel/adithya/SEAAD_Outputs/SEAAD_Matrix_{broad}.parquet
- (cells x genes, float32, already CPM->log2(x+1) normalized)
- metadata -> /n/groups/patel/adithya/SEAAD_Outputs/SEAAD_CellMetadata.parquet
- subcluster -> top1 from .../labels_centroid/seaad_subpop_centroid_{broad}.parquet
- (Mathys subpop name assigned per SEA-AD cell by Pearson centroid corr)
- 2. Two scanpy normalization lines REMOVED because the SEA-AD matrix is
- already log-normalized (see sea_ad_normalization_matrix.py):
- sc.pp.normalize_total(adata_template, target_sum=1e4)
- sc.pp.log1p(adata_template)
- 3. Mathys clinical column names replaced with the SEA-AD column names that
- carry the same information (no derived/injected columns):
- dcfdx_lv in {4,5} -> alzheimers_or_control == 1
- ceradsc -> 'Overall AD neuropathological Change'
- (ordered Categorical: High < Intermediate < Low < Not AD,
- so .max() = 'Not AD' = healthiest, matching ceradsc=4)
- cogdx -> 'Cognitive Status'
- (ordered Categorical: No dementia < Dementia,
- so .min() = 'No dementia' = healthiest, matching cogdx=1)
- projid -> 'Donor ID'
- Subcluster -> centroid top1
- The CERAD classifier binary outcome 'ceradsc in {1,2}' (definite/probable AD)
- becomes 'Overall AD neuropathological Change' in {'High','Intermediate'}
- (high/moderate AD pathology) -- same clinical definition.
- The pd.to_numeric(...) coercions that the Mathys script applied to ceradsc
- and cogdx were no-ops on already-typed numeric columns; with SEA-AD's
- ordered Categorical encoding those coercions are removed (they would error
- on string categories). The .max()/.min()/.isin() and dropna() that consume
- those columns are otherwise unchanged.
- 4. The outer for-loop runs for the single subcluster passed on the CLI; the
- per-subcluster validation row and ROC interpolation arrays are written
- to their own files so a separate aggregator can combine them into the
- final publication CSV + ROC plots after all jobs complete.
- Usage:
- python pseudotime_seaad_centroid.py --subcluster Ex5
- """
- import argparse
- import os
- import re
- import shutil
- import scanpy as sc
- import pandas as pd
- import numpy as np
- import matplotlib.pyplot as plt
- import statsmodels.api as sm
- from sklearn.metrics import roc_auc_score, roc_curve
- from scipy.stats import combine_pvalues, spearmanr
- from statsmodels.stats.multitest import multipletests
- from statsmodels.nonparametric.smoothers_lowess import lowess
- import seaborn as sns
- # Mathys subcluster prefix -> SEA-AD broad_cell_type (matches the centroid pipeline).
- SUBCLUSTER_PREFIX_TO_BROAD = {
- 'Ex': 'Ex', 'In': 'Inh', 'Ast': 'Ast',
- 'Oli': 'Oli', 'Mic': 'Mic', 'Opc': 'Opc',
- }
- # Ordered Categoricals so .max()/.min() pick the healthiest level on each axis:
- # Mathys ceradsc: 1 (def AD) < 2 < 3 < 4 (no AD) -> max = healthiest
- # SEA-AD pathology: High < Intermediate < Low < Not AD -> max = 'Not AD' (healthiest)
- # Mathys cogdx: 1 (NCI) < 2 < ... (higher = more dementia) -> min = healthiest
- # SEA-AD cognition: No dementia < Dementia -> min = 'No dementia' (healthiest)
- PATHOLOGY_CATEGORIES = ['High', 'Intermediate', 'Low', 'Not AD']
- COGNITION_CATEGORIES = ['No dementia', 'Dementia']
- def subcluster_to_broad(sub):
- m = re.match(r'^([A-Za-z]+)\d+$', sub)
- if not m:
- raise ValueError(f'Unrecognized subcluster name: {sub!r}')
- prefix = m.group(1)
- if prefix not in SUBCLUSTER_PREFIX_TO_BROAD:
- raise ValueError(f'Unknown subcluster prefix {prefix!r} for {sub!r}')
- return SUBCLUSTER_PREFIX_TO_BROAD[prefix]
- def parse_args():
- p = argparse.ArgumentParser()
- p.add_argument('--subcluster', required=True,
- help='Mathys subpop name (e.g. Ex5, In3, Opc2) to process.')
- p.add_argument('--matrix_dir',
- default='/n/groups/patel/adithya/SEAAD_Outputs')
- p.add_argument('--meta_path',
- default='/n/groups/patel/adithya/SEAAD_Outputs/SEAAD_CellMetadata.parquet')
- p.add_argument('--labels_dir',
- default='/n/groups/patel/adithya/Alz_Outputs/Final_Outputs/seaad_pseudotime/labels_centroid')
- p.add_argument('--output_dir',
- default='/n/groups/patel/adithya/Alz_Outputs/Final_Outputs/seaad_pseudotime/pseudotime_centroid')
- return p.parse_args()
- def main():
- args = parse_args()
- broad = subcluster_to_broad(args.subcluster)
- # --- Set up Paths ---
- expr_path = os.path.join(args.matrix_dir, f'SEAAD_Matrix_{broad}.parquet')
- meta_path = args.meta_path
- labels_path = os.path.join(args.labels_dir, f'seaad_subpop_centroid_{broad}.parquet')
- output_path = args.output_dir + '/'
- os.makedirs(output_path, exist_ok=True)
- # --- Load Data ---
- # SEA-AD per-cell metadata (TAG index).
- meta_df = pd.read_parquet(meta_path)
- # Centroid labels for this broad (TAG index, has 'top1' column with assigned Mathys subpop).
- labels_df = pd.read_parquet(labels_path)
- # 'Subcluster' column = centroid-assigned Mathys subpop (replaces Mathys's own Subcluster).
- meta_df = meta_df.join(labels_df['top1'].rename('Subcluster'), how='inner')
- # SEA-AD pathology and cognition columns -> ordered Categoricals so .max()/.min()
- # preserve clinical semantics directly (no derived numeric columns).
- meta_df['Overall AD neuropathological Change'] = pd.Categorical(
- meta_df['Overall AD neuropathological Change'],
- categories=PATHOLOGY_CATEGORIES, ordered=True,
- )
- meta_df['Cognitive Status'] = pd.Categorical(
- meta_df['Cognitive Status'],
- categories=COGNITION_CATEGORIES, ordered=True,
- )
- # AD/Control label (direct replacement for Mathys's `dcfdx_lv in {4,5}` derivation).
- meta_df['AD'] = meta_df['alzheimers_or_control'].map({1: 'AD', 0: 'Control'}).astype('category')
- print("AD status defined based on 'alzheimers_or_control':")
- print(meta_df['AD'].value_counts())
- # Mathys metadata had TAG as a column; restore that so the script body's
- # meta_sub['TAG'].tolist() / meta_sub.set_index('TAG') work unchanged.
- meta_df = meta_df.reset_index()
- # SEA-AD expression matrix (cells on rows; no transpose needed -- the Mathys
- # script transposed because its source was genes-on-rows).
- expr_df = pd.read_parquet(expr_path)
- expr_df_transposed = expr_df # name kept identical to the Mathys script body
- # --- Analysis Settings ---
- all_subclusters = [args.subcluster] # one per SLURM job
- random_seeds = [41, 42, 43]
- # 57, 62, 72, 73
- validation_results = []
- summary_roc_data = []
- summary_roc_data_cerad = []
- roc_plot_data_cerad = []
- # --- Main Loop (single iteration; loop kept for byte-identical script body) ---
- for current_subcluster in all_subclusters:
- print(f"\n--- Processing Subcluster: {current_subcluster} ---")
- meta_sub = meta_df[meta_df['Subcluster'] == current_subcluster]
- if meta_sub.shape[0] < 50 or len(meta_sub['AD'].unique()) < 2:
- print(f"Skipping {current_subcluster}: <50 cells or only one AD/Control group.")
- continue
- safe_subcluster_name = re.sub(r'[^a-zA-Z0-9_.-]', '_', current_subcluster)
- subcluster_cells = meta_sub['TAG'].tolist()
- expr_sub = expr_df_transposed.loc[subcluster_cells]
- # --- Create AnnData ---
- adata_template = sc.AnnData(X=expr_sub)
- adata_template.obs = meta_sub.set_index('TAG').loc[adata_template.obs.index]
- # --- NON-CIRCULAR: Use HVGs instead of DEGs ---
- # Two normalization lines from the Mathys script are REMOVED here because
- # the SEA-AD matrix is already CPM -> log2(x+1) normalized upstream
- # (see sea_ad_normalization_matrix.py):
- # sc.pp.normalize_total(adata_template, target_sum=1e4)
- # sc.pp.log1p(adata_template)
- print("Selecting HVGs for pseudotime (non-circular approach)...")
- # Select top 2000 HVGs (or all genes if fewer available)
- n_top = min(2000, adata_template.n_vars - 1)
- sc.pp.highly_variable_genes(adata_template, n_top_genes=n_top, flavor='seurat')
- hvg_genes = adata_template.var[adata_template.var['highly_variable']].index.tolist()
- if len(hvg_genes) < 50:
- print(f"Skipping {current_subcluster}: only {len(hvg_genes)} HVGs found.")
- continue
- adata_template = adata_template[:, hvg_genes].copy()
- print(f"Using {len(hvg_genes)} HVGs to build trajectories.")
- # --- PCA (no Harmony - not needed within subclusters) ---
- n_pcs = min(30, len(hvg_genes) - 1)
- sc.pp.pca(adata_template, n_comps=n_pcs)
- print(f"PCA complete: {adata_template.obsm['X_pca'].shape}")
- # --- FAST neighbor graph ---
- print(f"Building neighbor graph for {adata_template.n_obs} cells...")
- sc.pp.neighbors(adata_template, n_neighbors=10, n_pcs=n_pcs, method='umap')
- print("Neighbor graph complete!")
- # --- Downstream: UMAP, clustering, diffusion map ---
- sc.tl.umap(adata_template)
- sc.tl.leiden(adata_template, resolution=0.5)
- sc.tl.diffmap(adata_template)
- # --- Loop over random seeds to test trajectory robustness ---
- subcluster_aucs = []
- roc_plot_data = []
- top_trajectory_genes_from_gt = [] # will be filled after seed loop
- top_50_trajectory_genes_from_gt = [] # defensive init (used in final row regardless)
- subcluster_pvals, subcluster_odds_ratios = [], []
- all_fprs, all_tprs = [], [] # For mean ROC over seeds
- cerad_aucs = []
- cerad_odds_ratios = []
- cerad_pvals = []
- cerad_or_cis = []
- # Store pseudotime per seed (for later averaging)
- pseudotime_per_seed = []
- for seed in random_seeds:
- print(f"\n -- Running analysis for seed: {seed} --")
- # Create a fresh copy for each run
- adata = adata_template.copy()
- # --- Select Root Cell Based on pathology and cognition (with current seed) ---
- # SEA-AD analogs of Mathys (max ceradsc, then min cogdx):
- # max('Overall AD neuropathological Change') = 'Not AD' (healthiest pathology)
- # min('Cognitive Status') = 'No dementia' (healthiest cognition)
- subcluster_meta_dpt = adata.obs.copy()
- subcluster_meta_dpt.dropna(
- subset=['Overall AD neuropathological Change', 'Cognitive Status'],
- inplace=True,
- )
- if subcluster_meta_dpt.empty:
- print(" Skipping trajectory ordering: No cells with valid pathology/cognition values.")
- break
- max_pathology = subcluster_meta_dpt['Overall AD neuropathological Change'].max()
- root_candidates = subcluster_meta_dpt[
- subcluster_meta_dpt['Overall AD neuropathological Change'] == max_pathology
- ]
- min_cognition = root_candidates['Cognitive Status'].min()
- root_candidates = root_candidates[
- root_candidates['Cognitive Status'] == min_cognition
- ]
- root_cell_id = root_candidates.sample(n=1, random_state=seed).index[0]
- adata.uns['iroot'] = np.where(adata.obs_names == root_cell_id)[0][0]
- # --- Calculate Pseudotime (DPT) ---
- sc.tl.dpt(adata)
- # Store pseudotime for this seed (full vector, all cells)
- pseudotime_per_seed.append(adata.obs['dpt_pseudotime'].to_numpy())
- # --- Calculate AUC for the current seed ---
- validation_df = adata.obs[['dpt_pseudotime', 'AD']].copy()
- validation_df.dropna(subset=['dpt_pseudotime'], inplace=True)
- y_true = (validation_df['AD'] == 'AD').astype(int)
- y_score = validation_df['dpt_pseudotime']
- auc_score = roc_auc_score(y_true=y_true, y_score=y_score)
- subcluster_aucs.append(auc_score)
- print(f" --> Seed {seed}: AUC = {auc_score:.4f}")
- # Logistic regression for this seed
- X = sm.add_constant(validation_df['dpt_pseudotime'])
- if len(y_true.unique()) < 2:
- print(f" Skipping seed {seed}: only one class present after dropping NaNs.")
- continue
- log_reg = sm.Logit(y_true, X).fit(disp=0)
- subcluster_pvals.append(log_reg.pvalues['dpt_pseudotime'])
- subcluster_odds_ratios.append(np.exp(log_reg.params['dpt_pseudotime']))
- # ROC curve for this seed (clinical AD)
- fpr, tpr, _ = roc_curve(y_true=y_true, y_score=y_score)
- all_fprs.append(fpr)
- all_tprs.append(tpr)
- if auc_score > 0.55:
- roc_plot_data.append({'fpr': fpr, 'tpr': tpr, 'auc': auc_score, 'seed': seed})
- # CERAD-based classifier
- # SEA-AD analog of Mathys' ceradsc.isin([1,2]) (definite/probable AD):
- # 'Overall AD neuropathological Change'.isin(['High','Intermediate'])
- try:
- cerad_df = adata.obs[['dpt_pseudotime']].copy()
- # Clear the index name so the next line doesn't create a collision
- # between an index named 'TAG' and a column named 'TAG' (which newer
- # pandas rejects on .merge as ambiguous; latent bug from Mathys notebook).
- cerad_df.index.name = None
- cerad_df['TAG'] = cerad_df.index
- meta_subset = meta_df[['TAG', 'Overall AD neuropathological Change']].dropna()
- cerad_df = cerad_df.merge(meta_subset, on="TAG", how="left")
- cerad_df = cerad_df.dropna(subset=["Overall AD neuropathological Change", "dpt_pseudotime"])
- cerad_df['AD_cerad'] = cerad_df['Overall AD neuropathological Change'].isin(['High', 'Intermediate']).astype(int)
- if cerad_df['AD_cerad'].nunique() < 2:
- print(f" CERAD classifier skipped for seed {seed}: only one class present.")
- else:
- X_cerad = sm.add_constant(cerad_df['dpt_pseudotime'])
- y_cerad = cerad_df['AD_cerad']
- cerad_model = sm.Logit(y_cerad, X_cerad).fit(disp=0)
- cerad_auc = roc_auc_score(y_cerad, cerad_df['dpt_pseudotime'])
- cerad_or = np.exp(cerad_model.params['dpt_pseudotime'])
- cerad_p = cerad_model.pvalues['dpt_pseudotime']
- cerad_conf = cerad_model.conf_int().loc['dpt_pseudotime']
- cerad_or_lower = np.exp(cerad_conf[0])
- cerad_or_upper = np.exp(cerad_conf[1])
- cerad_aucs.append(cerad_auc)
- cerad_odds_ratios.append(cerad_or)
- cerad_pvals.append(cerad_p)
- cerad_or_cis.append((cerad_or_lower, cerad_or_upper))
- fpr_cerad, tpr_cerad, _ = roc_curve(y_cerad, cerad_df['dpt_pseudotime'])
- if cerad_auc > 0.55:
- roc_plot_data_cerad.append({
- 'fpr': fpr_cerad,
- 'tpr': tpr_cerad,
- 'auc': cerad_auc,
- 'seed': seed
- })
- print(f" CERAD AUC (seed {seed}): {cerad_auc:.3f}")
- except Exception as e:
- print(f" CERAD model failed for seed {seed}: {e}")
- # --- After all seeds are run, aggregate results and create combined outputs ---
- if not subcluster_aucs:
- print(f"No valid results generated for {current_subcluster}. Skipping final aggregation.")
- continue
- # === 1) Aggregate pseudotime across seeds (Option A: rank-normalized) ===
- if pseudotime_per_seed:
- pseudo_array = np.column_stack(pseudotime_per_seed) # cells x n_seeds_used
- n_cells, n_seeds_used = pseudo_array.shape
- ranked = np.zeros_like(pseudo_array, dtype=float)
- for j in range(n_seeds_used):
- col = pseudo_array[:, j]
- valid = ~np.isnan(col)
- ranks = np.empty_like(col, dtype=float)
- ranks[~valid] = np.nan
- # rank 0..(n_valid-1)
- valid_vals = col[valid]
- order = np.argsort(valid_vals)
- rank_values = np.empty_like(order, dtype=float)
- rank_values[order] = np.arange(len(order), dtype=float)
- ranks[valid] = rank_values
- denom = max(len(order) - 1, 1)
- ranked[:, j] = ranks / denom
- mean_pseudotime = np.nanmean(ranked, axis=1)
- adata_template.obs['mean_pseudotime'] = mean_pseudotime
- adata_template.obs['TAG'] = adata_template.obs.index
- # --- Save GAM input using mean pseudotime ---
- # Mathys 'projid' -> SEA-AD 'Donor ID' (subject identifier)
- gam_df = adata_template.obs[['Donor ID', 'AD', 'TAG']].copy()
- gam_df['AD_binary'] = (gam_df['AD'] == 'AD').astype(int)
- gam_df['dpt_pseudotime'] = mean_pseudotime
- gam_df = gam_df.dropna(subset=['Donor ID', 'AD_binary', 'TAG', 'dpt_pseudotime'])
- gam_df.to_csv(os.path.join(output_path, f"pseudotime_gam_input_{safe_subcluster_name}.csv"), index=False)
- # === 2) PC selection by correlation with mean pseudotime ===
- pc_scores = adata_template.obsm['X_pca'][:, :n_pcs] # cells x PCs
- pc_correlations = []
- for k in range(pc_scores.shape[1]):
- rho, _ = spearmanr(pc_scores[:, k], mean_pseudotime)
- pc_correlations.append(rho)
- best_pc = int(np.nanargmax(np.abs(pc_correlations)))
- # "Average" gene loadings across seeds (PCA is shared; average = original)
- loadings_matrix = adata_template.varm['PCs'] # genes x PCs
- avg_loadings = loadings_matrix[:, best_pc]
- top_idx = np.argsort(np.abs(avg_loadings))[::-1][:10]
- top_trajectory_genes_from_gt = adata_template.var_names[top_idx].tolist()
- print(f" Top trajectory-informing genes (PC{best_pc + 1}): {top_trajectory_genes_from_gt}")
- top_50 = np.argsort(np.abs(avg_loadings))[::-1][:50]
- top_50_trajectory_genes_from_gt = adata_template.var_names[top_50].tolist()
- # --- Save top 50 gene loadings in rank order ---
- top50_df = pd.DataFrame({
- 'rank': np.arange(1, 51),
- 'gene': adata_template.var_names[top_50],
- 'loading': avg_loadings[top_50],
- 'abs_loading': np.abs(avg_loadings[top_50])
- })
- top50_path = os.path.join(output_path, f"top50_PC_loadings_{safe_subcluster_name}.csv")
- top50_df.to_csv(top50_path, index=False)
- print(f"Saved top-50 PC loadings -> {top50_path}")
- # === 3a) UMAP plot colored by mean pseudotime ===
- plot_filename_umap = os.path.join(output_path, f"umap_pseudotime_{safe_subcluster_name}.png")
- sc.pl.umap(
- adata_template,
- color='mean_pseudotime',
- title=f'{current_subcluster}: Mean Pseudotime Trajectory',
- show=False,
- save=f"_{safe_subcluster_name}_umap.png",
- cmap="viridis"
- )
- shutil.move(f'figures/umap_{safe_subcluster_name}_umap.png', plot_filename_umap)
- # === 3b) Smoothed expression of top genes vs mean pseudotime ===
- if top_trajectory_genes_from_gt:
- fig, ax = plt.subplots(figsize=(10, 6))
- genes_to_plot = top_trajectory_genes_from_gt[:4]
- colors = sns.color_palette("colorblind", n_colors=len(genes_to_plot))
- pseudo_vals = mean_pseudotime
- valid_idx = ~np.isnan(pseudo_vals)
- sorted_idx = np.argsort(pseudo_vals[valid_idx])
- for gene, color in zip(genes_to_plot, colors):
- expr_vals = adata_template[:, gene].X
- # Handle sparse
- if not isinstance(expr_vals, np.ndarray):
- expr_vals = expr_vals.toarray()
- expr_vals = np.asarray(expr_vals).flatten()
- expr_valid = expr_vals[valid_idx][sorted_idx]
- pseudo_sorted = pseudo_vals[valid_idx][sorted_idx]
- smoothed = lowess(expr_valid, pseudo_sorted, frac=0.3)
- smoothed_z = (smoothed[:, 1] - smoothed[:, 1].mean()) / smoothed[:, 1].std()
- ax.plot(smoothed[:, 0], smoothed_z, label=gene, color=color)
- ax.set_xlabel('Mean Pseudotime (rank-normalized)')
- ax.set_ylabel('Z-scored Expression')
- ax.set_title(f'{current_subcluster}: Smoothed Expression of Top Genes')
- ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
- fig.savefig(os.path.join(output_path, f"lineplot_top_genes_{safe_subcluster_name}.png"),
- bbox_inches='tight')
- plt.close(fig)
- # === Aggregate CERAD stats across seeds ===
- if cerad_aucs:
- mean_auc_cerad = np.mean(cerad_aucs)
- se_auc_cerad = np.std(cerad_aucs, ddof=1) / np.sqrt(len(cerad_aucs))
- auc_cerad_ci = (mean_auc_cerad - 1.96 * se_auc_cerad, mean_auc_cerad + 1.96 * se_auc_cerad)
- log_cerad_ors = np.log(cerad_odds_ratios)
- mean_log_or_cerad = np.mean(log_cerad_ors)
- se_log_or_cerad = np.std(log_cerad_ors, ddof=1) / np.sqrt(len(log_cerad_ors))
- or_cerad_ci = (
- np.exp(mean_log_or_cerad - 1.96 * se_log_or_cerad),
- np.exp(mean_log_or_cerad + 1.96 * se_log_or_cerad)
- )
- mean_or_cerad = np.exp(mean_log_or_cerad)
- _, combined_p_cerad = combine_pvalues(cerad_pvals, method='fisher')
- print(f" CERAD mean AUC: {mean_auc_cerad:.3f} (95% CI: {auc_cerad_ci[0]:.3f}-{auc_cerad_ci[1]:.3f})")
- print(f" CERAD mean OR: {mean_or_cerad:.2f} (95% CI: {or_cerad_ci[0]:.2f}-{or_cerad_ci[1]:.2f}), p={combined_p_cerad:.3g}")
- if mean_auc_cerad > 0.55:
- mean_fpr_cerad = np.linspace(0, 1, 100)
- interp_tprs_cerad = []
- for data in roc_plot_data_cerad:
- interp_tprs_cerad.append(np.interp(mean_fpr_cerad, data['fpr'], data['tpr']))
- mean_tpr_cerad = np.mean(interp_tprs_cerad, axis=0)
- mean_tpr_cerad[0], mean_tpr_cerad[-1] = 0, 1
- summary_roc_data_cerad.append({
- 'fpr': mean_fpr_cerad,
- 'tpr': mean_tpr_cerad,
- 'Subcluster': current_subcluster,
- 'mean_auc': mean_auc_cerad,
- 'se_auc': se_auc_cerad
- })
- else:
- mean_auc_cerad = np.nan
- auc_cerad_ci = (np.nan, np.nan)
- mean_or_cerad = np.nan
- or_cerad_ci = (np.nan, np.nan)
- combined_p_cerad = np.nan
- # === Aggregate clinical AD stats across seeds ===
- mean_auc = np.mean(subcluster_aucs)
- se_auc = np.std(subcluster_aucs, ddof=1) / np.sqrt(len(subcluster_aucs))
- auc_ci_95 = (mean_auc - 1.96 * se_auc, mean_auc + 1.96 * se_auc)
- log_ors = np.log(subcluster_odds_ratios)
- mean_log_or = np.mean(log_ors)
- se_log_or = np.std(log_ors, ddof=1) / np.sqrt(len(log_ors))
- log_or_ci_95 = (mean_log_or - 1.96 * se_log_or, mean_log_or + 1.96 * se_log_or)
- mean_or = np.exp(mean_log_or)
- or_ci_95 = (np.exp(log_or_ci_95[0]), np.exp(log_or_ci_95[1]))
- _, combined_p_value = combine_pvalues(subcluster_pvals, method='fisher')
- print(f"\n--- Aggregated Results for {current_subcluster} ---")
- print(f"Mean AUC: {mean_auc:.4f} (95% CI: {auc_ci_95[0]:.4f} - {auc_ci_95[1]:.4f})")
- print(f"Mean Odds Ratio: {mean_or:.4f} (95% CI: {or_ci_95[0]:.4f} - {or_ci_95[1]:.4f})")
- print(f"Combined P-value (Fisher's): {combined_p_value:.4g}")
- # === Mean ROC curve across seeds for this subcluster ===
- if mean_auc > 0.55:
- mean_fpr = np.linspace(0, 1, 100)
- interp_tprs = []
- for fpr, tpr in zip(all_fprs, all_tprs):
- interp_tprs.append(np.interp(mean_fpr, fpr, tpr))
- if interp_tprs:
- mean_tpr = np.mean(interp_tprs, axis=0)
- mean_tpr[0], mean_tpr[-1] = 0, 1
- summary_roc_data.append({
- 'fpr': mean_fpr,
- 'tpr': mean_tpr,
- 'Subcluster': current_subcluster,
- 'mean_auc': mean_auc,
- 'se_auc': se_auc
- })
- # Store per-subcluster summary row
- validation_results.append({
- 'Subcluster': current_subcluster,
- 'mean_auc': mean_auc,
- 'auc_ci_lower': auc_ci_95[0],
- 'auc_ci_upper': auc_ci_95[1],
- 'mean_odds_ratio': mean_or,
- 'or_ci_lower': or_ci_95[0],
- 'or_ci_upper': or_ci_95[1],
- 'combined_p_value': combined_p_value,
- 'mean_auc_cerad': mean_auc_cerad,
- 'auc_cerad_lower': auc_cerad_ci[0],
- 'auc_cerad_upper': auc_cerad_ci[1],
- 'odds_ratio_cerad': mean_or_cerad,
- 'or_cerad_lower': or_cerad_ci[0],
- 'or_cerad_upper': or_cerad_ci[1],
- 'p_value_cerad': combined_p_cerad,
- 'n_cells': adata_template.n_obs,
- 'top_trajectory_genes': ', '.join(top_trajectory_genes_from_gt) if top_trajectory_genes_from_gt else "N/A",
- 'top_pc_loadings': ', '.join(top_50_trajectory_genes_from_gt) if top_50_trajectory_genes_from_gt else "N/A",
- })
- # --- Save per-subcluster outputs (this job's piece of the final aggregation) ---
- # The Mathys notebook accumulated across all subclusters in one process and
- # wrote a single publication_summary_statistics.csv + combined ROC plots at
- # the end. With per-subcluster SLURM parallelization, each job writes its
- # own row/ROC arrays; a separate aggregator combines them after all jobs run.
- if not validation_results:
- print(f"\nNo valid results for {args.subcluster}. Nothing to save.")
- else:
- safe = re.sub(r'[^a-zA-Z0-9_.-]', '_', args.subcluster)
- row_df = pd.DataFrame(validation_results)
- row_path = os.path.join(output_path, f"summary_row_{safe}.csv")
- row_df.to_csv(row_path, index=False)
- print(f"Saved per-subcluster summary row -> {row_path}")
- if summary_roc_data:
- d = summary_roc_data[0]
- roc_clin_path = os.path.join(output_path, f"roc_clinical_{safe}.npz")
- np.savez(roc_clin_path,
- fpr=d['fpr'], tpr=d['tpr'],
- mean_auc=d['mean_auc'], se_auc=d['se_auc'],
- subcluster=current_subcluster)
- print(f"Saved per-subcluster clinical ROC -> {roc_clin_path}")
- if summary_roc_data_cerad:
- d = summary_roc_data_cerad[0]
- roc_cerad_path = os.path.join(output_path, f"roc_cerad_{safe}.npz")
- np.savez(roc_cerad_path,
- fpr=d['fpr'], tpr=d['tpr'],
- mean_auc=d['mean_auc'], se_auc=d['se_auc'],
- subcluster=current_subcluster)
- print(f"Saved per-subcluster CERAD ROC -> {roc_cerad_path}")
- print("\n--- Analysis complete. ---")
- print("\n--- Pseudotime robustness analysis complete for all subclusters. ---")
- if __name__ == '__main__':
- main()
pseudotime_seaad_centroid.py at commit cf785a2, under MIT · at the source
Overview
- Department of Biomedical Informatics, Harvard Medical School,Boston, MA USA
- Harvard College,Cambridge, MA USA
- New York Genome Center,New York, NY USA
- Rush Alzheimer’s Disease Center, Rush University,Chicago, IL USA
Abstract
Univariate differential expression analysis dominates single-cell transcriptomic studies of Alzheimer’s disease, yet marginal testing alone cannot resolve multivariate disease-associated structure or distinguish clinical from neuropathological signatures. Here we introduce TriSCOPE, a framework integrating multivariate predictive modeling, donor-aware differential expression, and pseudotime inference, applied to six Alzheimer’s disease snRNA-seq datasets comprising ~4.2 million nuclei from 627 donors. Across six major brain cell types and 41 subclusters, we identify cell-type-specific predictively expressed genes that are largely non-overlapping with differentially expressed genes, revealing disease-relevant signals invisible to univariate approaches. Conditional GWAS–eQTL colocalization anchors a subset of these predictively expressed genes to inherited Alzheimer’s disease risk loci, including 27 genetically supported gene–cell type pairs (among them ARL17B, JAZF1) with no significant differential expression. Integration across all three analytical branches nominates high-confidence convergent programs including astrocytic stress markers (such as GFAP, MT2A), neuronal signaling genes (like RASGEF1B, LINGO1, NRG1), and microglial and oligodendrocyte regulators (including SYTL3, SPP1, APOD). Comparison of clinical and neuropathological models reveals cell-type-specific divergence between cognitive and pathological Alzheimer’s disease signatures. Key predictively expressed genes including ARL17B, LINGO1, and APOE replicate across independent cohorts, with cross-cohort generalizability confirmed across all three analytical branches of TriSCOPE.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 40 matches between paragraphs and lines of code.
AdiVM/TriSCOPE_RNASeq_Alzheimers_Analysis
cf785a20811d40284f647d0323346b18a29b28e6, 8 August 2026Availability: 1 check, the latest on 12 November 2026: the link answers
- 12 November 2026: the link answers
110 files
- Differential_Expression.
R , R, 150 lines - PM_Gex.py, Python, 554 lines
- PM_Multimodal.py, Python, 542 lines
- Trajectory_Inference.py, Python, 655 lines, 2 matches
- Validation_data_tables.i
pynb , Jupyter, 327 lines - data_tables.ipynb, Jupyter, 1,514 lines, 2 matches
- figure1.ipynb, Jupyter, 146 lines
- figure10.ipynb, Jupyter, 1,099 lines
- figure2.ipynb, Jupyter, 844 lines, 2 matches
- figure3.ipynb, Jupyter, 824 lines
- figure4.ipynb, Jupyter, 524 lines
- figure5.ipynb, Jupyter, 250 lines
- figure6.ipynb, Jupyter, 960 lines, 1 match
- figure7.ipynb, Jupyter, 294 lines
- figure8.ipynb, Jupyter, 492 lines, 2 matches
- figure9.ipynb, Jupyter, 1,136 lines
- main_tables.ipynb, Jupyter, 311 lines
- revision/
Revision_Figures/ , Jupyter, 927 lines427_DEG_analysis.ipynb - revision/
Revision_Figures/ , Jupyter, 175 linesFig_8_revision.ipynb - revision/
Revision_Figures/ , Jupyter, 277 lines, 1 matchFigure_10_revision.ipynb - revision/
Revision_Figures/ , Jupyter, 1,118 linesFigure_7_Revision.ipynb - revision/
Revision_Figures/ , Jupyter, 354 lines, 1 matchFigure_9_revision.ipynb - revision/
Revision_Figures/ , Jupyter, 186 linesFigure__revision_shap.ip ynb - revision/
Revision_Figures/ , Jupyter, 216 linesbaselines_analysis.ipynb - revision/
Revision_Figures/ , Jupyter, 371 linesbrier_plots_revision.ipy nb - revision/
Revision_Figures/ , Jupyter, 3,555 lines, 2 matchescorrelations_table.ipynb - revision/
Revision_Figures/ , Jupyter, 430 lines, 4 matchesdrug_bank_target_identif ication.ipynb - revision/
Revision_Figures/ , Jupyter, 809 linesfigure3_re.ipynb - revision/
Revision_Figures/ , Jupyter, 760 linesfigure4_revision.ipynb - revision/
Revision_Figures/ , Jupyter, 114 linesfigure_1.ipynb - revision/
Revision_Figures/ , Jupyter, 462 lines, 1 matchfigure_6_revision.ipynb - revision/
Revision_Figures/ , Jupyter, 1,176 lines, 3 matchespseudotime_hvg.ipynb - revision/
Revision_Figures/ , Jupyter, 706 linesrevision_tables.ipynb - revision/
Revision_Figures/ , Jupyter, 983 lines, 1 matchruntime_analysis.ipynb - revision/
code/ , R, 365 lines, 4 matchesColoc.R - revision/
code/ , R, 439 lines, 3 matchesContrasts_DE_w_RandomEff ect.R - revision/
code/ , R, 424 linesDE_w_RandomEffect.R - revision/
rosmap_427/ , Python, 490 linesMultirun_cell_on_cell_de mo_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesMultirun_cell_on_cell_de mo_rosmap.sh - revision/
rosmap_427/ , Python, 583 linesMultirun_cell_on_cell_ge nes_all_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesMultirun_cell_on_cell_ge nes_all_rosmap.sh - revision/
rosmap_427/ , Python, 581 linesMultirun_cell_on_cell_ge nes_apoe_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesMultirun_cell_on_cell_ge nes_apoe_rosmap.sh - revision/
rosmap_427/ , Python, 572 linesMultirun_cell_on_cell_ge nes_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesMultirun_cell_on_cell_ge nes_rosmap.sh - revision/
rosmap_427/ , Python, 600 linesein_Multirun_cell_on_cel l_genes_all_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesein_Multirun_cell_on_cel l_genes_all_rosmap.sh - revision/
rosmap_427/ , Python, 598 linesein_Multirun_cell_on_cel l_genes_apoe_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesein_Multirun_cell_on_cel l_genes_apoe_rosmap.sh - revision/
rosmap_427/ , Python, 598 linesein_Multirun_cell_on_cel l_genes_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesein_Multirun_cell_on_cel l_genes_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesein_submit_multirun_gene s_all_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesein_submit_multirun_gene s_apoe_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesein_submit_multirun_gene s_rosmap.sh - revision/
rosmap_427/ , Python, 619 linesexoli_Multirun_cell_on_c ell_genes_all_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesexoli_Multirun_cell_on_c ell_genes_all_rosmap.sh - revision/
rosmap_427/ , Python, 617 linesexoli_Multirun_cell_on_c ell_genes_apoe_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesexoli_Multirun_cell_on_c ell_genes_apoe_rosmap.sh - revision/
rosmap_427/ , Python, 617 linesexoli_Multirun_cell_on_c ell_genes_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesexoli_Multirun_cell_on_c ell_genes_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesexoli_submit_multirun_ge nes_all_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesexoli_submit_multirun_ge nes_apoe_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesexoli_submit_multirun_ge nes_rosmap.sh - revision/
rosmap_427/ , R, 94 linesinspect_rosmap427.R - revision/
rosmap_427/ , R, 208 linesrosmap_normalization_mat rix.R - revision/
rosmap_427/ , Shell, 16 linessubmit_inspect_rosmap427 .sh - revision/
rosmap_427/ , Shell, 32 linessubmit_multirun_demo_ros map.sh - revision/
rosmap_427/ , Shell, 32 linessubmit_multirun_genes_al l_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linessubmit_multirun_genes_ap oe_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linessubmit_multirun_genes_ro smap.sh - revision/
rosmap_427/ , Shell, 16 linessubmit_rosmap_normalizat ion.sh - revision/
sea-ad_validation_analys , R, 359 lines, 1 matchis/ DEG_SEAAD_chunking.R - revision/
sea-ad_validation_analys , Python, 490 linesis/ Multirun_cell_on_cell_de mo_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ Multirun_cell_on_cell_de mo_seaad.sh - revision/
sea-ad_validation_analys , Python, 581 linesis/ Multirun_cell_on_cell_ge nes_all_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ Multirun_cell_on_cell_ge nes_all_seaad.sh - revision/
sea-ad_validation_analys , Python, 579 lines, 1 matchis/ Multirun_cell_on_cell_ge nes_apoe_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ Multirun_cell_on_cell_ge nes_apoe_seaad.sh - revision/
sea-ad_validation_analys , Python, 570 linesis/ Multirun_cell_on_cell_ge nes_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ Multirun_cell_on_cell_ge nes_seaad.sh - revision/
sea-ad_validation_analys , Python, 196 linesis/ SEAAD_extract_Ex_split.p y - revision/
sea-ad_validation_analys , Python, 179 linesis/ SEAAD_extract_raw_for_DE G.py - revision/
sea-ad_validation_analys , Jupyter, 152 linesis/ Sea_ad_check.ipynb - revision/
sea-ad_validation_analys , Jupyter, 84 lines, 1 matchis/ data_confirmation.ipynb - revision/
sea-ad_validation_analys , Python, 600 linesis/ ein_Multirun_cell_on_cel l_genes_all_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ ein_Multirun_cell_on_cel l_genes_all_seaad.sh - revision/
sea-ad_validation_analys , Python, 598 lines, 1 matchis/ ein_Multirun_cell_on_cel l_genes_apoe_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ ein_Multirun_cell_on_cel l_genes_apoe_seaad.sh - revision/
sea-ad_validation_analys , Python, 598 linesis/ ein_Multirun_cell_on_cel l_genes_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ ein_Multirun_cell_on_cel l_genes_seaad.sh - revision/
sea-ad_validation_analys , Shell, 32 linesis/ ein_submit_multirun_gene s_all_seaad.sh - revision/
sea-ad_validation_analys , Shell, 32 linesis/ ein_submit_multirun_gene s_apoe_seaad.sh - revision/
sea-ad_validation_analys , Shell, 32 linesis/ ein_submit_multirun_gene s_seaad.sh - revision/
sea-ad_validation_analys , Jupyter, 420 linesis/ feature_overlap.ipynb - revision/
sea-ad_validation_analys , Jupyter, 181 linesis/ inference_seaad.ipynb - revision/
sea-ad_validation_analys , Python, 113 linesis/ pseudotime/ aggregate_pseudotime_cen troid.py - revision/
sea-ad_validation_analys , Jupyter, 100 linesis/ pseudotime/ data_check.ipynb - revision/
sea-ad_validation_analys , Python, 302 lines, 1 matchis/ pseudotime/ label_seaad_subpops.py - revision/
sea-ad_validation_analys , Python, 344 lines, 1 matchis/ pseudotime/ label_seaad_subpops_cent roid.py - revision/
sea-ad_validation_analys , Python, 609 lines, 5 matchesis/ pseudotime/ pseudotime_seaad_centroi d.py - revision/
sea-ad_validation_analys , Shell, 41 linesis/ pseudotime/ run_label_seaad_centroid .sh - revision/
sea-ad_validation_analys , Shell, 35 linesis/ pseudotime/ run_label_seaad_subpops. sh - revision/
sea-ad_validation_analys , Shell, 36 linesis/ pseudotime/ run_pseudotime_seaad_cen troid.sh - revision/
sea-ad_validation_analys , Shell, 40 linesis/ pseudotime/ submit_label_seaad_centr oid.sh - revision/
sea-ad_validation_analys , Shell, 39 linesis/ pseudotime/ submit_label_seaad_subpo ps.sh - revision/
sea-ad_validation_analys , Shell, 60 linesis/ pseudotime/ submit_pseudotime_seaad_ centroid.sh - revision/
sea-ad_validation_analys , Jupyter, 70 linesis/ pseudotime/ visualizing_final_result s.ipynb - revision/
sea-ad_validation_analys , Jupyter, 323 linesis/ results_check.ipynb - repository limit reached (2,000 files or 30 MB): the rest is at the source (28 files)
- LICENSE, License, 21 lines
- README.md, Text, 77 lines
Zenodo 21793738
Availability: 1 check, the latest on 12 November 2026: the link answers (HTTP 200)
- 12 November 2026: the link answers (HTTP 200)
110 files
- Differential_Expression.
R , R, 150 lines - PM_Gex.py, Python, 554 lines
- PM_Multimodal.py, Python, 542 lines
- Trajectory_Inference.py, Python, 655 lines
- Validation_data_tables.i
pynb , Jupyter, 327 lines - data_tables.ipynb, Jupyter, 1,514 lines
- figure1.ipynb, Jupyter, 146 lines
- figure10.ipynb, Jupyter, 1,099 lines
- figure2.ipynb, Jupyter, 844 lines
- figure3.ipynb, Jupyter, 824 lines
- figure4.ipynb, Jupyter, 524 lines
- figure5.ipynb, Jupyter, 250 lines
- figure6.ipynb, Jupyter, 960 lines
- figure7.ipynb, Jupyter, 294 lines
- figure8.ipynb, Jupyter, 492 lines
- figure9.ipynb, Jupyter, 1,136 lines
- main_tables.ipynb, Jupyter, 311 lines
- revision/
Revision_Figures/ , Jupyter, 927 lines427_DEG_analysis.ipynb - revision/
Revision_Figures/ , Jupyter, 175 linesFig_8_revision.ipynb - revision/
Revision_Figures/ , Jupyter, 277 linesFigure_10_revision.ipynb - revision/
Revision_Figures/ , Jupyter, 1,118 linesFigure_7_Revision.ipynb - revision/
Revision_Figures/ , Jupyter, 354 linesFigure_9_revision.ipynb - revision/
Revision_Figures/ , Jupyter, 186 linesFigure__revision_shap.ip ynb - revision/
Revision_Figures/ , Jupyter, 216 linesbaselines_analysis.ipynb - revision/
Revision_Figures/ , Jupyter, 371 linesbrier_plots_revision.ipy nb - revision/
Revision_Figures/ , Jupyter, 3,555 linescorrelations_table.ipynb - revision/
Revision_Figures/ , Jupyter, 430 linesdrug_bank_target_identif ication.ipynb - revision/
Revision_Figures/ , Jupyter, 809 linesfigure3_re.ipynb - revision/
Revision_Figures/ , Jupyter, 760 linesfigure4_revision.ipynb - revision/
Revision_Figures/ , Jupyter, 114 linesfigure_1.ipynb - revision/
Revision_Figures/ , Jupyter, 462 linesfigure_6_revision.ipynb - revision/
Revision_Figures/ , Jupyter, 1,176 linespseudotime_hvg.ipynb - revision/
Revision_Figures/ , Jupyter, 706 linesrevision_tables.ipynb - revision/
Revision_Figures/ , Jupyter, 983 linesruntime_analysis.ipynb - revision/
code/ , R, 365 linesColoc.R - revision/
code/ , R, 439 linesContrasts_DE_w_RandomEff ect.R - revision/
code/ , R, 424 linesDE_w_RandomEffect.R - revision/
rosmap_427/ , Python, 490 linesMultirun_cell_on_cell_de mo_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesMultirun_cell_on_cell_de mo_rosmap.sh - revision/
rosmap_427/ , Python, 583 linesMultirun_cell_on_cell_ge nes_all_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesMultirun_cell_on_cell_ge nes_all_rosmap.sh - revision/
rosmap_427/ , Python, 581 linesMultirun_cell_on_cell_ge nes_apoe_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesMultirun_cell_on_cell_ge nes_apoe_rosmap.sh - revision/
rosmap_427/ , Python, 572 linesMultirun_cell_on_cell_ge nes_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesMultirun_cell_on_cell_ge nes_rosmap.sh - revision/
rosmap_427/ , Python, 600 linesein_Multirun_cell_on_cel l_genes_all_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesein_Multirun_cell_on_cel l_genes_all_rosmap.sh - revision/
rosmap_427/ , Python, 598 linesein_Multirun_cell_on_cel l_genes_apoe_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesein_Multirun_cell_on_cel l_genes_apoe_rosmap.sh - revision/
rosmap_427/ , Python, 598 linesein_Multirun_cell_on_cel l_genes_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesein_Multirun_cell_on_cel l_genes_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesein_submit_multirun_gene s_all_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesein_submit_multirun_gene s_apoe_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesein_submit_multirun_gene s_rosmap.sh - revision/
rosmap_427/ , Python, 619 linesexoli_Multirun_cell_on_c ell_genes_all_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesexoli_Multirun_cell_on_c ell_genes_all_rosmap.sh - revision/
rosmap_427/ , Python, 617 linesexoli_Multirun_cell_on_c ell_genes_apoe_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesexoli_Multirun_cell_on_c ell_genes_apoe_rosmap.sh - revision/
rosmap_427/ , Python, 617 linesexoli_Multirun_cell_on_c ell_genes_rosmap.py - revision/
rosmap_427/ , Shell, 36 linesexoli_Multirun_cell_on_c ell_genes_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesexoli_submit_multirun_ge nes_all_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesexoli_submit_multirun_ge nes_apoe_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linesexoli_submit_multirun_ge nes_rosmap.sh - revision/
rosmap_427/ , R, 94 linesinspect_rosmap427.R - revision/
rosmap_427/ , R, 208 linesrosmap_normalization_mat rix.R - revision/
rosmap_427/ , Shell, 16 linessubmit_inspect_rosmap427 .sh - revision/
rosmap_427/ , Shell, 32 linessubmit_multirun_demo_ros map.sh - revision/
rosmap_427/ , Shell, 32 linessubmit_multirun_genes_al l_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linessubmit_multirun_genes_ap oe_rosmap.sh - revision/
rosmap_427/ , Shell, 32 linessubmit_multirun_genes_ro smap.sh - revision/
rosmap_427/ , Shell, 16 linessubmit_rosmap_normalizat ion.sh - revision/
sea-ad_validation_analys , R, 359 linesis/ DEG_SEAAD_chunking.R - revision/
sea-ad_validation_analys , Python, 490 linesis/ Multirun_cell_on_cell_de mo_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ Multirun_cell_on_cell_de mo_seaad.sh - revision/
sea-ad_validation_analys , Python, 581 linesis/ Multirun_cell_on_cell_ge nes_all_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ Multirun_cell_on_cell_ge nes_all_seaad.sh - revision/
sea-ad_validation_analys , Python, 579 linesis/ Multirun_cell_on_cell_ge nes_apoe_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ Multirun_cell_on_cell_ge nes_apoe_seaad.sh - revision/
sea-ad_validation_analys , Python, 570 linesis/ Multirun_cell_on_cell_ge nes_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ Multirun_cell_on_cell_ge nes_seaad.sh - revision/
sea-ad_validation_analys , Python, 196 linesis/ SEAAD_extract_Ex_split.p y - revision/
sea-ad_validation_analys , Python, 179 linesis/ SEAAD_extract_raw_for_DE G.py - revision/
sea-ad_validation_analys , Jupyter, 152 linesis/ Sea_ad_check.ipynb - revision/
sea-ad_validation_analys , Jupyter, 84 linesis/ data_confirmation.ipynb - revision/
sea-ad_validation_analys , Python, 600 linesis/ ein_Multirun_cell_on_cel l_genes_all_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ ein_Multirun_cell_on_cel l_genes_all_seaad.sh - revision/
sea-ad_validation_analys , Python, 598 linesis/ ein_Multirun_cell_on_cel l_genes_apoe_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ ein_Multirun_cell_on_cel l_genes_apoe_seaad.sh - revision/
sea-ad_validation_analys , Python, 598 linesis/ ein_Multirun_cell_on_cel l_genes_seaad.py - revision/
sea-ad_validation_analys , Shell, 36 linesis/ ein_Multirun_cell_on_cel l_genes_seaad.sh - revision/
sea-ad_validation_analys , Shell, 32 linesis/ ein_submit_multirun_gene s_all_seaad.sh - revision/
sea-ad_validation_analys , Shell, 32 linesis/ ein_submit_multirun_gene s_apoe_seaad.sh - revision/
sea-ad_validation_analys , Shell, 32 linesis/ ein_submit_multirun_gene s_seaad.sh - revision/
sea-ad_validation_analys , Jupyter, 420 linesis/ feature_overlap.ipynb - revision/
sea-ad_validation_analys , Jupyter, 181 linesis/ inference_seaad.ipynb - revision/
sea-ad_validation_analys , Python, 113 linesis/ pseudotime/ aggregate_pseudotime_cen troid.py - revision/
sea-ad_validation_analys , Jupyter, 100 linesis/ pseudotime/ data_check.ipynb - revision/
sea-ad_validation_analys , Python, 302 linesis/ pseudotime/ label_seaad_subpops.py - revision/
sea-ad_validation_analys , Python, 344 linesis/ pseudotime/ label_seaad_subpops_cent roid.py - revision/
sea-ad_validation_analys , Python, 609 linesis/ pseudotime/ pseudotime_seaad_centroi d.py - revision/
sea-ad_validation_analys , Shell, 41 linesis/ pseudotime/ run_label_seaad_centroid .sh - revision/
sea-ad_validation_analys , Shell, 35 linesis/ pseudotime/ run_label_seaad_subpops. sh - revision/
sea-ad_validation_analys , Shell, 36 linesis/ pseudotime/ run_pseudotime_seaad_cen troid.sh - revision/
sea-ad_validation_analys , Shell, 40 linesis/ pseudotime/ submit_label_seaad_centr oid.sh - revision/
sea-ad_validation_analys , Shell, 39 linesis/ pseudotime/ submit_label_seaad_subpo ps.sh - revision/
sea-ad_validation_analys , Shell, 60 linesis/ pseudotime/ submit_pseudotime_seaad_ centroid.sh - revision/
sea-ad_validation_analys , Jupyter, 70 linesis/ pseudotime/ visualizing_final_result s.ipynb - revision/
sea-ad_validation_analys , Jupyter, 323 linesis/ results_check.ipynb - repository limit reached (2,000 files or 30 MB): the rest is at the source
- LICENSE, License, 21 lines
- README.md, Text, 33 lines
Code availability
All code used in this study is available in a public GitHub repository (https://
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:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 216 scripts, each with its path and the digest of its content;
- 40 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
- doi:10.7303/
syn18485175 , at the source; found in “Data availability” - doi:10.7303/
syn3157322 , at the source; found in “Data availability” - doi:10.7303/
syn51758062.1 , at the source; found in “Data availability” - geo:GSE157827, at NCBI GEO; found in “Data availability”
- portal.brain-map.org/
explore/ , at Allen Brain Map; found in “Data availability”seattle-alzheimers-disea se - synapse.org/
synapse:syn26223298 , at Synapse; found in “Data availability” - synapse.org/
synapse:syn52293417 , at Synapse; found in “Data availability” - synapse.org/
synapse:syn52293442 , at Synapse; found in “Data availability”
Data availability
The data used in this study are available as follows. 1) The Mathys-48 dataset is available on Synapse under accession syn18485175 (http://
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 1, 12 November 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 3 keywords, 11 MeSH terms, 3 funders, 67 references.
Cite
This paper
Madduri, A. V., Ellis, R. J., Lakhani, C. M., Bennett, D. A., & Patel, C. J. (2026). Single-cell machine learning uncovers genetically anchored, cell-type specific programs of Alzheimer's disease. Nature communications, 17(1), 11124. https://
BibTeX
@article{madduri2026sing
author = {Madduri, Adithya V. and Ellis, Randall J. and Lakhani, Chirag M. and Bennett, David A. and Patel, Chirag J.},
title = {{Single-cell machine learning uncovers genetically anchored, cell-type specific programs of Alzheimer's disease}},
journal = {Nature communications},
year = {2026},
month = nov,
volume = {17},
number = {1},
pages = {11124},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/
url = {https://
pmid = {43003833},
pmcid = {PMC13748584}
}
RIS
TY - JOUR
AU - Madduri, Adithya V.
AU - Ellis, Randall J.
AU - Lakhani, Chirag M.
AU - Bennett, David A.
AU - Patel, Chirag J.
TI - Single-cell machine learning uncovers genetically anchored, cell-type specific programs of Alzheimer's disease
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/
VL - 17
IS - 1
SP - 11124
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Single-cell machine learning uncovers genetically anchored, cell-type specific programs of Alzheimer's disease",
"container-title": "Nature communications",
"author": [
{
"family": "Madduri",
"given": "Adithya V."
},
{
"family": "Ellis",
"given": "Randall J."
},
{
"family": "Lakhani",
"given": "Chirag M."
},
{
"family": "Bennett",
"given": "David A."
},
{
"family": "Patel",
"given": "Chirag J."
}
],
"container-title-short":
"volume": "17",
"issue": "1",
"page": "11124",
"DOI": "10.1038/
"PMID": "43003833",
"PMCID": "PMC13748584",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
11,
9
]
]
}
}
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.cell.2025.06.031 [code]
- Single-cell multiregion epigenomic rewiring in Alzheimer's disease progression and cognitive resilience.Journal: CellIn common: anndata, Scanpy, UMAP, 9 other tools, synapse.org/synapse:syn52293442, synapse.org/synapse:syn52293417, Alzheimer's / dementia, genetics / omics, cellular / molecular, 12 references
- [2] doi:10.14336/ad.2024.0429 [code]
- Systematic Analysis of Biological Processes Reveals Gene Co-expression Modules Driving Pathway Dysregulation in Alzheimer's Disease.Journal: Aging and diseaseIn common: glmmTMB, edgeR, anndata, 13 other tools, portal.brain-map.org/explore/seattle-alzheimers-disease, Alzheimer's / dementia, genetics / omics, cellular / molecular, 8 references
- [3] doi:10.1038/s41586-024-07606-7 [code]
- Single-cell multiregion dissection of Alzheimer's disease.Journal: NatureIn common: anndata, Scanpy, UMAP, 11 other tools, Alzheimer's / dementia, genetics / omics, cellular / molecular, 12 references
- [4] doi:10.1038/s41593-024-01774-5 [code]
- Integrated multimodal cell atlas of Alzheimer's disease.Journal: Nature neuroscienceIn common: anndata, Scanpy, UMAP, 10 other tools, portal.brain-map.org/explore/seattle-alzheimers-disease, Alzheimer's / dementia, genetics / omics, cellular / molecular, 7 references
- [5] doi:10.1093/brain/awaf285 [code]
- Deep learning-based cell type profiles reveal signatures of Alzheimer's disease resilience and resistance.Journal: Brain : a journal of neurologyIn common: edgeR, anndata, Scanpy, 10 other tools, portal.brain-map.org/explore/seattle-alzheimers-disease, Alzheimer's / dementia, genetics / omics, cellular / molecular, 5 references
- [6] doi:10.14336/ad.2025.0409 [code]
- Layer-Specific Colocalization of Microglia with Amyloid Plaques in the Middle Temporal Gyrus Predicts Cognitive Decline in Alzheimer's Disease.Journal: Aging and diseaseIn common: anndata, Scanpy, UMAP, 10 other tools, portal.brain-map.org/explore/seattle-alzheimers-disease, Alzheimer's / dementia, cellular / molecular, 5 references
- [7] doi:10.1016/j.celrep.2024.115149 [code]
- Single-nucleus and spatial landscape of the sub-ventricular zone in human glioblastoma.Journal: Cell reportsIn common: glmmTMB, edgeR, anndata, 13 other tools, cellular / molecular, 2 references
- [8] doi:10.1038/s41467-025-59949-y [code]
- Whole-genome sequencing analyses suggest novel genetic factors associated with Alzheimer's disease and a cumulative effects model for risk liability.Journal: Nature communicationsIn common: edgeR, Scanpy, data.table, 8 other tools, portal.brain-map.org/explore/seattle-alzheimers-disease, Alzheimer's / dementia, genetics / omics, 5 references
- [9] doi:10.1038/s41467-025-59654-w [code]
- An integrative systems-biology approach defines mechanisms of Alzheimer's disease neurodegeneration.Journal: Nature communicationsIn common: edgeR, Scanpy, Seurat, 10 other tools, Alzheimer's / dementia, genetics / omics, cellular / molecular, 6 references
- [10] doi:10.1038/s41467-026-68920-4 [code]
- Molecular signatures of resilience to Alzheimer's disease in neocortical layer 4 neurons.Journal: Nature communicationsIn common: anndata, Scanpy, Seurat, 9 other tools, Alzheimer's / dementia, cellular / molecular, 7 references
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 216 scripts, and 40 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:048a8cee3e23bba8…
Add the badge to the code's README
If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.
Markdown
<!-- oscr-badge:start -->
[](https://openscicode.org/badge/doi/10.1038/s41467-026-77651-5)
<!-- oscr-badge:end -->reStructuredText
.. oscr-badge:start
.. image:: https://openscicode.org/badge/doi/10.1038/s41467-026-77651-5.svg
:target: https://openscicode.org/badge/doi/10.1038/s41467-026-77651-5
:alt: OSCR
.. oscr-badge:endHTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1038/s41467-026-77651-5"><img src="https://openscicode.org/badge/doi/10.1038/s41467-026-77651-5.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".
If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge 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: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.
Report a mismatch between the code and the paper · Report a code error (signed in)
Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.
Report a reproduction failure (signed in)
Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
