Branch status: This repo currently has two active branches —
master(this one) andmain— that have diverged independently and now contain different Dockerfile, scripts, and README revisions. The content below describes themastervariant. Treat either branch as authoritative only once they have been consolidated.
Research question: How does MSI status remodel the immune microenvironment in gastroesophageal adenocarcinoma?
This is the second project in a computational biology portfolio. After Project 1 surfaced immune-related pathways in bulk RNA-seq, this project digs into the tumour microenvironment at single-cell resolution to understand how MSI-high status reshapes immune infiltration — a question directly relevant to checkpoint inhibitor response in GEA.
scripts/run_workflow.py is a Python/Scanpy end-to-end orchestrator that runs nine steps on either a user-provided .h5ad or synthetic data:
- Load — read
.h5ad(or callgenerate_synthetic_data()when no input is passed) - QC —
qc_metrics.calculate_qc_metrics+filter_cells(min 200 / max 5000 genes, ≤ 20 % MT, ≤ 50 % ribo) +filter_genes(≥ 3 cells) - Normalize —
sc.pp.normalize_total(target_sum=1e4)+log1p - HVG + PCA — 2 000 HVGs (
seurat_v3) + 50-component PCA - Neighbors / UMAP / Leiden — resolution configurable via
--resolution - Annotation — marker-based (
annotation.annotate_by_markers) over a default T/B/Monocyte/NK/DC panel + cluster-majority refinement - Differential expression —
sc.tl.rank_genes_groups(Wilcoxon) by cluster - Plots — Leiden UMAP, cell-type UMAP, DE gene ranks (written under
<output>/plots/) - Save —
processed.h5ad,cell_metadata.csv,de_results.csv
Two standalone R scripts (scripts/clustering.R Seurat clustering, scripts/trajectory.R pseudotime) sit alongside the Python workflow for the specific steps where R tooling is preferred; they are not orchestrated by run_workflow.py.
| Category | Tools |
|---|---|
| End-to-end workflow | Python + Scanpy (Leiden, UMAP, Wilcoxon DE) |
| Complementary R | Seurat clustering, pseudotime trajectory |
| Clustering | Graph-based / Leiden |
| Dimensionality reduction | PCA, UMAP |
| Visualisation | matplotlib, Scanpy plotting |
| Testing | pytest |
| Environment | Docker, Conda |
project-2-scrnaseq-analysis/
├── Dockerfile
├── environment.yml
├── config/
│ └── analysis_config.yaml
├── scripts/
│ ├── run_workflow.py # Python/Scanpy end-to-end orchestrator
│ ├── generate_synthetic_data.py# Reproducible synthetic input data
│ ├── qc_metrics.py # QC metrics + filtering
│ ├── annotation.py # Marker-based cell-type annotation
│ ├── visualization_utils.py # Plot helpers
│ ├── clustering.R # Standalone Seurat clustering
│ └── trajectory.R # Standalone pseudotime trajectory
├── tests/ # pytest suite
└── .gitignore
Run outputs (processed .h5ad, QC reports, figures, DE tables) are written under data/ and results/ at runtime and are gitignored.
git clone https://github.com/adamhoffman2155-hue/project-2-scrnaseq-analysis.git
cd project-2-scrnaseq-analysis
# Using Docker
docker build -t scrnaseq-analysis .
docker run -it -v $(pwd):/workspace scrnaseq-analysis bash
# Or Conda
conda env create -f environment.yml
conda activate scrnaseq-analysis
# Synthetic end-to-end run (no input data required)
python scripts/run_workflow.py --output results/
# or with real data:
python scripts/run_workflow.py --input data/your.h5ad --output results/I scoped the MSI/TME question based on my thesis cohort, evaluated cluster assignments against known immune biology, and interpreted findings in the clinical context of GEA immunotherapy response. Implementation was heavily AI-assisted.
This is Project 2 of 7. It extends Project 1's bulk transcriptomics into single-cell resolution, asking whether the immune signatures detected in bulk are driven by specific cell populations. The immune subtype classifications from this work feed into the survival model in Project 6. See the portfolio site for the full narrative.
MIT
Adam Hoffman — M.Sc. Cancer Research, McGill University