Skip to content

RafaelBotan/CorePAM-Study

Repository files navigation

CorePAM: A 24-Gene PAM50-Derived Expression Score with Cross-Platform External Validation for Breast Cancer Prognosis

DOI License: MIT R >= 4.4

PhD thesis project — Rafael de Negreiros Botan

Reducing the 50-gene PAM50 panel to the smallest prognostic subset (CorePAM, 24 genes) whose out-of-fold C-index is non-inferior (delta-C <= 0.010) to the full 50-gene elastic-net Cox model. Validated across four independent cohorts spanning RNA-seq and microarray platforms.


Overview

PAM50 is the gold-standard 50-gene expression panel for breast cancer molecular classification. CorePAM asks: how few of those 50 genes are actually needed for prognostic scoring?

Using elastic-net Cox regression on the SCAN-B cohort (n = 3,069), we derived a 24-gene subset that retains non-inferior prognostic discrimination, then validated it on four independent cohorts (TCGA-BRCA, METABRIC, GSE20685, GSE1456) covering both RNA-seq and microarray platforms. A secondary analysis evaluated the CorePAM score as a predictor of pathological complete response (pCR) to neoadjuvant chemotherapy across five additional cohorts.

Key results:

  • Random-effects meta-analysis HR = 1.37 (95% CI 1.24-1.52; I2 = 38.2%; p = 1.6 x 10^-9) per 1-SD increase
  • Concordance (C-index) 0.62-0.66 across validation cohorts
  • pCR meta-analysis OR = 1.69 (95% CI 1.39-2.05; I2 = 0%)

Cohorts

Survival (Overall Survival / Disease-Specific Survival)

Cohort Role Platform Endpoint N Events Median FU
SCAN-B (GSE96058) Training RNA-seq OS 3,069 322 (10.5%) 54.9 mo
TCGA-BRCA Validation RNA-seq OS 1,072 150 (14.0%) 32.0 mo
METABRIC Validation Microarray (Illumina) DSS 1,978 646 (32.7%) 159.0 mo
GSE20685 Validation Microarray (Affymetrix) OS 327 83 (25.4%) 112.8 mo
GSE1456 Validation Microarray (Affymetrix) OS 159 40 (25.2%) 91.0 mo

pCR (Pathological Complete Response to NACT)

Cohort Role Platform N (analyzed) pCR rate
GSE25066 Primary HGU133A 182 23.1%
GSE20194 Primary HGU133Plus2 278 20.1%
GSE32646 Primary HGU133Plus2 115 23.5%
I-SPY 1 (GSE22226) Primary Agilent 44K 122 26.2%
I-SPY 2 (GSE194040) Exploratory Agilent 44K 986 32.4%

Note: pCR sample sizes reflect post-QC cohorts (complete CorePAM score + endpoint + covariates).


Repository Structure

.
├── scripts/                     R scripts (numbered 00-36)
│   ├── 00_setup.R               Environment + frozen parameters (sourced by all)
│   ├── 00_colors.R              Colour palette for all figures
│   ├── 01-04_*.R                Data download, clinical harmonisation, expression preprocessing
│   ├── 05_reduce_pam50_to_corepam_FINAL.R   CorePAM derivation (FREEZE)
│   ├── 06_*.R                   Z-score and scoring per cohort
│   ├── 07_*.R                   Survival analysis per cohort + figures
│   ├── 08_meta_survival.R       Random-effects meta-analysis (K = 4 validation cohorts)
│   ├── 11_*.R                   Incremental value (delta-C-index) + DCA
│   ├── 13-16_*.R                Quality control (correlations, PCA, schema, anti-hard-code)
│   ├── 17-18_*.R                Manuscript rendering + submission bundle
│   ├── 19-23b_*.R               pCR analysis block (download -> analysis -> meta -> figures)
│   ├── 30-36_*.R                Major revision analyses (frozen z-score, Uno's C, calibration, ER subgroup, full baseline)
│   ├── 24_integrate_GSE1456_stockholm.R   4th validation cohort integration
│   └── 25-29_*.R                Sensitivity analyses (CORE-A expanded, genefu, bootstrap, drop-gene)
│
├── results/
│   ├── corepam/                 Model artefacts (weights, Pareto, training card)
│   ├── main/                    Manuscript tables (Table 3, meta-analysis summary)
│   ├── supp/                    Supplementary tables + QC reports
│   └── pcr/                     pCR logistic results + meta-analysis
│
├── figures/
│   ├── main/en/{pdf,png}/       Main figures (vector + raster)
│   ├── supp/en/{pdf,png}/       Supplementary figures
│   └── pcr/en/{pdf,png}/        pCR figures
│
├── 01_docs/
│   ├── registry/                Frozen parameters, artefact inventory, audit reports
│   └── endpoint_mapping_templates/  Clinical endpoint mapping per cohort
│
├── manuscript/
│   └── CorePAM_manuscript.qmd   Quarto source (all numbers read from CSVs)
│
├── submission/                  Submitted manuscript (TeX, PDF, figures, supplementary files)
├── config/                      pCR cohort manifest
├── CITATION.cff                 Citation metadata
├── INSTALL.R                    One-step package installer
├── LICENSE                      MIT License
└── README.md                    This file

Data lake (01_Base_Pura_CorePAM/) is git-ignored. All raw and processed data are downloaded automatically by scripts/01_download_raw_data.R.


Frozen Parameters

All analytical parameters were pre-specified and locked before any data analysis. They are stored in 01_docs/registry/analysis_freeze.csv.

Parameter Value Description
delta_c 0.010 Non-inferiority margin for C-index
alpha 0.5 Elastic-net mixing parameter
k_folds 10 Stratified CV folds
seed_folds 42 Fold assignment seed
min_genes_fraction 0.80 Minimum gene coverage per cohort
time_unit_divisor 30.4375 Days to months conversion

Reproducibility

Requirements

Component Version
R >= 4.4.0 (developed on 4.5.2)
Quarto >= 1.4 (for manuscript rendering only)
Internet Required for initial data download
Disk space >= 15 GB
RAM >= 16 GB

Step 0 — Install packages

source("INSTALL.R")

Step 1 — Run the full pipeline

Scripts are numbered and must be executed in order. Each script sources 00_setup.R automatically and checks whether its outputs already exist (skip/force pattern — set FORCE_RERUN=TRUE to re-execute).

# Download raw data (requires internet, ~1-4 hours)
source("scripts/01_download_raw_data.R")

# Clinical harmonisation (4 cohorts)
source("scripts/02_harmonize_clinical_SCANB.R")
source("scripts/02_harmonize_clinical_TCGA_BRCA.R")
source("scripts/02_harmonize_clinical_METABRIC.R")
source("scripts/02_harmonize_clinical_GSE20685.R")

# Expression preprocessing (4 cohorts)
source("scripts/03_expression_preprocess_SCANB.R")
source("scripts/03_expression_preprocess_TCGA_BRCA.R")
source("scripts/03_expression_preprocess_METABRIC.R")
source("scripts/03_expression_preprocess_GSE20685.R")

# Gene audit + CorePAM derivation
source("scripts/04_gene_audit_freeze.R")
source("scripts/05_reduce_pam50_to_corepam_FINAL.R")

# Scoring (4 cohorts)
source("scripts/06_zscore_and_score_SCANB.R")
source("scripts/06_zscore_and_score_TCGA_BRCA.R")
source("scripts/06_zscore_and_score_METABRIC.R")
source("scripts/06_zscore_and_score_GSE20685.R")

# Survival analysis (preflight + 4 cohorts + meta)
source("scripts/07A_preflight_files_strict.R")
source("scripts/07_survival_analysis_SCANB.R")
source("scripts/07_survival_analysis_TCGA_BRCA.R")
source("scripts/07_survival_analysis_METABRIC.R")
source("scripts/07_survival_analysis_GSE20685.R")
source("scripts/08_meta_survival.R")

# Figures + tables
source("scripts/07x_extra_figures.R")
source("scripts/07y_pam50full_comparison.R")
source("scripts/07z_table_figures_survival.R")

# Incremental value + DCA
source("scripts/11_incremental_value_and_dca.R")
source("scripts/11b_dca_corepam.R")

# Quality control
source("scripts/13_qc_correlations_offdiag.R")
source("scripts/14_qc_metabric_pca_forensics.R")
source("scripts/15_qc_schema_range_checks.R")
source("scripts/16_qc_text_vs_results_assert.R")

# Manuscript rendering
source("scripts/17_render_manuscript_quarto.R")

# --- 4th validation cohort (GSE1456 / Stockholm) ---
source("scripts/24_integrate_GSE1456_stockholm.R")

# --- pCR block (secondary analysis) ---
source("scripts/19_download_pCR_raw_data.R")
source("scripts/20_prepare_pCR_GSE25066.R")
source("scripts/20_prepare_pCR_GSE20194.R")
source("scripts/20_prepare_pCR_GSE32646.R")
source("scripts/20_prepare_pCR_ISPY1.R")
source("scripts/20_prepare_pCR_ISPY2.R")
source("scripts/21_pCR_logistic_analysis.R")
source("scripts/21c_pcr_er_interaction.R")
source("scripts/21_ispy2_analysis.R")
source("scripts/22_meta_pCR.R")
source("scripts/22b_meta_pCR_with_ispy2.R")
source("scripts/23_pCR_figures.R")
source("scripts/23b_pCR_ispy2_figures.R")

# --- Sensitivity analyses ---
source("scripts/25_sensitivity_corea_expanded.R")
source("scripts/26_sensitivity_genefu_comparison.R")
source("scripts/27_bootstrap_gene_stability.R")
source("scripts/28_dropgene_sensitivity.R")
source("scripts/29_corea_sensitivity_forest.R")

# --- Major revision analyses ---
source("scripts/30_frozen_zscore_sensitivity.R")
source("scripts/31_uno_c_and_tdauc.R")
source("scripts/32_calibration_formal.R")
source("scripts/34_subgroup_er_stratified.R")
source("scripts/35_er_neg_bootstrap_stability.R")
source("scripts/36_full_clinical_baseline.R")

Step 2 — Validate

source("scripts/00_validate_all.R")

This checks syntax of all scripts and verifies that expected output files exist.


Reproducibility Safeguards

Safeguard Implementation
Frozen parameters All choices locked in analysis_freeze.csv before analysis
SHA-256 audit trail Every raw file and output artefact hashed in registry/study_registry.csv
No inter-cohort pooling Expression matrices never combined; Z-scoring always intra-cohort
Strict I/O options(warn = 2) — any warning is a fatal error
Skip/force pattern Scripts check for existing outputs; FORCE_RERUN=TRUE to override
Anti-hard-code check Script 16 verifies no manuscript number was typed manually
Leakage-proof Training (SCAN-B) and validation cohorts are never mixed
No coefficient refitting Validation cohorts use training weights only

Key Output Artefacts

Artefact Path Description
CorePAM_weights.csv results/corepam/ 24 genes with Cox elastic-net weights
CorePAM_model.rds results/corepam/ Fitted glmnet model (git-ignored; regenerated by script 05)
selected_CorePAM_summary.json results/corepam/ Derivation metadata (C-index, genes, lambda)
pareto_df_cindex_oof.csv results/corepam/ Pareto frontier: gene count vs. OOF C-index
Table3_*.csv results/main/ Survival performance by cohort (HR, C-index, events)
meta_survival_summary.csv results/main/ Random-effects meta-analysis results
pCR_results_by_cohort.csv results/pcr/ Logistic regression per pCR cohort
meta_pCR_results.csv results/pcr/ pCR meta-analysis (OR, I2)

CorePAM Score Formula

For a given sample in cohort c:

G_present = CorePAM genes ∩ genes available in cohort c
z_i       = intra-cohort Z-score for gene i
score     = Σ(w_i · z_i) / Σ|w_i|     (over G_present)
score_z   = scale(score)                (for HR per 1-SD)

Weights (w_i) are fixed from training. Score direction is established in SCAN-B and never re-inverted per cohort.


Data Sources

All data are publicly available:


Troubleshooting

Problem Solution
Package installation fails Run source("INSTALL.R") in a clean R session
Download times out Re-run 01_download_raw_data.R; the skip/force pattern resumes from where it stopped
"Output already exists" Set FORCE_RERUN=TRUE environment variable to re-execute
Warning treated as error This is by design (options(warn=2)). Investigate the warning — it may indicate a data issue
Memory error Ensure >= 16 GB RAM. Close other applications before running preprocessing scripts

Citation

If you use this work, please cite:

Botan RN, Sousa JB. CorePAM: a 24-gene PAM50-derived expression score with cross-platform external validation for breast cancer prognosis. Breast Cancer Res. 2026 (under review).

See also CITATION.cff for machine-readable citation metadata.


Contact

  • Rafael de Negreiros Botanoncologista@gmail.com
  • Faculty of Medicine, University of Brasilia (UnB), Brazil

License

Analysis code: MIT License. Data are from public repositories (GEO, GDC, cBioPortal) under their respective terms of use.

About

CorePAM: a PAM50-derived minimum gene expression score for breast cancer prognosis

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors