1. Getting Started#

This notebook demonstrates a minimal end-to-end workflow using synthetic data. No project files or large datasets are required.

Pipeline phase: Covers Phases 3 and 3.5b in brief.

What you will learn:

  • Create a synthetic AnnData with spatial coordinates

  • Run the preprocessing recipe for Visium data

  • Score Hallmark gene sets

  • Produce a basic spatial plot

import numpy as np
import anndata as ad
import sc_tools as st

print(f"sc_tools version: {st.__version__}")

Create a synthetic Visium-like AnnData#

In a real workflow this would be a Phase 1 checkpoint loaded from disk:

adata = ad.read_h5ad("results/adata.raw.p1.h5ad")
np.random.seed(42)
n_spots, n_genes = 500, 200

# Raw count matrix (integer)
X = np.random.negative_binomial(5, 0.5, size=(n_spots, n_genes)).astype(float)

# Gene names: first 10 are Hallmark HYPOXIA genes (for scoring demo)
hallmark_genes = ["ALDOA", "CDKN3", "ENO1", "LDHA", "PGK1", "TPI1", "VEGFA", "HK1", "HK2", "PFKL"]
gene_names = hallmark_genes + [f"GENE_{i}" for i in range(n_genes - len(hallmark_genes))]

# Spatial coordinates (grid layout)
row_idx, col_idx = np.divmod(np.arange(n_spots), 25)
spatial_coords = np.column_stack([col_idx * 100, row_idx * 100]).astype(float)

adata = ad.AnnData(
    X=X,
    obs={"sample": "sample_A", "library_id": "lib_A"},
    var={"gene_ids": gene_names},
    obsm={"spatial": spatial_coords},
)
adata.var_names = gene_names
adata.obs_names = [f"spot_{i}" for i in range(n_spots)]

print(adata)

Phase 3: Preprocess#

The preprocess recipe handles normalization, HVG selection, PCA, neighbors, UMAP, and Leiden clustering in one call. For Visium data the default integration is scvi; we use harmony here to avoid the heavy dependency.

st.pp.preprocess(
    adata,
    modality="visium",
    integration="harmony",
    batch_key=None,         # single sample — no batch correction
    n_top_genes=100,
    n_pcs=20,
    resolution=0.5,
)

print("Clusters:", adata.obs["leiden"].value_counts().to_dict())

Phase 3.5b: Score gene signatures#

Load the bundled MSigDB Hallmark gene sets and score them against the data. Scores are written to adata.obsm['signature_score'] and adata.obsm['signature_score_z'].

# Load bundled Hallmark sets (offline — no network required)
hallmark = st.tl.load_hallmark()
print(f"Loaded {len(hallmark)} Hallmark gene sets")

# Score (scanpy method by default)
st.tl.score_signature(adata, hallmark)

# Inspect available score columns
from sc_tools.utils.signatures import get_signature_columns
cols = get_signature_columns(adata)
print(f"Scored {len(cols)} signatures. First 5: {cols[:5]}")

Save Phase 3.5b checkpoint#

In a real project, save to the standard checkpoint path:

adata.write_h5ad("results/adata.normalized.scored.p35.h5ad")

Next steps#