6. Spatial Analysis#

Pipeline phase: Phase 5 — Downstream Biology

What you will learn:

  • Compute co-localization scores (truncated_similarity, pearson_correlation)

  • Run Moran’s I spatial autocorrelation per library

  • Run neighborhood enrichment analysis

  • Generate a multipage spatial PDF with signature overlays

Prerequisites: adata.normalized.scored.p35.h5ad (Phase 3.5b checkpoint)

import numpy as np
import pandas as pd
import anndata as ad
import sc_tools.tl as tl
import sc_tools.pl as pl
from sc_tools.tl.colocalization import (
    truncated_similarity,
    pearson_correlation,
    morans_i_batch,
    neighborhood_enrichment_batch,
)

Synthetic spatial AnnData with signature scores#

np.random.seed(42)
n_spots, n_genes = 400, 100

# Spatial grid
row, col = np.divmod(np.arange(n_spots), 20)
spatial = np.column_stack([col * 100.0, row * 100.0])

gene_names = [f"GENE_{i}" for i in range(n_genes)]
X = np.random.rand(n_spots, n_genes).astype(np.float32)

adata = ad.AnnData(X=X, obsm={"spatial": spatial})
adata.var_names = gene_names
adata.obs["library_id"] = pd.Categorical([f"lib_{i % 2}" for i in range(n_spots)])
adata.obs["leiden"] = pd.Categorical([str(i % 5) for i in range(n_spots)])

# Simulate signature scores in obsm (as produced by score_signature)
sig_cols = ["Hallmark/HYPOXIA", "Hallmark/MYC_TARGETS_V1", "Myeloid/Macrophage_Core"]
score_data = np.random.randn(n_spots, len(sig_cols)).astype(np.float32)
# Make HYPOXIA and Macrophage positively correlated in one region
score_data[:100, 0] += 1.5
score_data[:100, 2] += 1.2

adata.obsm["signature_score"] = pd.DataFrame(score_data, index=adata.obs_names, columns=sig_cols)
adata.obsm["signature_score_z"] = adata.obsm["signature_score"].apply(lambda c: (c - c.mean()) / c.std())

print(adata)

Co-localization: truncated similarity#

truncated_similarity computes score_a * score_b where both are positive, else 0. This highlights spots where two programs are jointly active.

hypoxia_scores = adata.obsm["signature_score"]["Hallmark/HYPOXIA"].values
macro_scores   = adata.obsm["signature_score"]["Myeloid/Macrophage_Core"].values

coloc = truncated_similarity(hypoxia_scores, macro_scores)
print(f"Co-localization score: mean={coloc.mean():.3f}, max={coloc.max():.3f}")
print(f"Fraction of spots with joint signal: {(coloc > 0).mean():.2%}")

Co-localization: Pearson correlation#

from sc_tools.tl.colocalization import pearson_correlation

corr_df = pearson_correlation(
    adata,
    sig_a="Hallmark/HYPOXIA",
    sig_b="Myeloid/Macrophage_Core",
    groupby="library_id",
)
print(corr_df)

Moran’s I spatial autocorrelation (per library)#

morans_i_batch runs Moran’s I for each signature column separately per library. Requires squidpy.

# In a real run (squidpy must be installed):
# morans_results = morans_i_batch(
#     adata,
#     keys=sig_cols,
#     library_key="library_id",
#     n_perms=100,
# )
# print(morans_results)
print("morans_i_batch returns a DataFrame with columns: key, library_id, I, pval, fdr")

Neighborhood enrichment#

neighborhood_enrichment_batch tests whether cell types co-occur in spatial neighborhoods more than expected by chance (squidpy gr.nhood_enrichment wrapper).

# In a real run:
# nhood_df = neighborhood_enrichment_batch(
#     adata,
#     cluster_key="leiden",
#     library_key="library_id",
# )
# print(nhood_df)
print("neighborhood_enrichment_batch returns a long-format DataFrame:")
print("  columns: library_id, celltype_A, celltype_B, zscore, pval, fdr")

Multipage spatial PDF#

multipage_spatial_pdf generates a PDF with one page per signature per library, overlaid on the tissue slide.

import tempfile, os

with tempfile.TemporaryDirectory() as tmpdir:
    pdf_path = os.path.join(tmpdir, "spatial_signatures.pdf")
    pl.spatial.multipage_spatial_pdf(
        adata,
        keys=sig_cols,
        library_key="library_id",
        output_path=pdf_path,
        spot_size=50,
    )
    print(f"PDF written: {os.path.getsize(pdf_path)} bytes")

Next steps#

For a real project, Phase 5 scripts live under projects/<platform>/<project>/scripts/. Example:

# Run spatial multipage colocalization plot for ggo_visium
make -C projects/visium/ggo_visium spatial-multipage-colocalization

Publication figures are saved to projects/<platform>/<project>/figures/manuscript/.