Gene Module Detection with Hotspot
Table of contents
- TOC
Hotspot identifies genes that are spatially autocorrelated in the cell embedding — i.e., genes that vary coherently across the transcriptional landscape. GeneCircuitry groups these genes into modules and optionally annotates them with pathway enrichment labels.
Functions live in genecircuitry/hotspot_processing.py. Canonical plots are in genecircuitry/plotting/hotspot_plots.py.
Hotspot is an optional dependency. Install it with
pip install -e ".[hotspot]". The pipeline skips module detection gracefully when Hotspot is not installed.
What Hotspot does
- Autocorrelation test: for every gene, tests whether its expression is more similar between neighbouring cells (in PCA/UMAP space) than expected by chance.
- Module detection: clusters autocorrelated genes into modules using their pairwise local correlations.
- Module scoring: computes per-cell scores for each module (analogous to a gene signature score).
- Enrichment annotation (optional): annotates each module with the top pathway term from over-representation analysis.
Step 1 — Create Hotspot object (create_hotspot_object)
from genecircuitry.hotspot_processing import create_hotspot_object
hs = create_hotspot_object(
adata,
top_genes=None, # genes to test (config.HOTSPOT_TOP_GENES = 500)
layer_key="raw_count", # raw count layer (must exist in adata.layers)
model="danb", # count model: "danb" | "bernoulli" | "normal" | "none"
embedding_key="X_pca", # embedding for spatial graph construction
normalization_key="n_counts",# adata.obs column for total counts normalization
)
Choosing the count model:
| Model | Use when |
|---|---|
danb (default) |
UMI-based scRNA-seq (recommended for most data) |
bernoulli |
Binary (0/1) data |
normal |
Already-normalized data |
none |
No count model (not recommended) |
Step 2 — Run Hotspot analysis (run_hotspot_analysis)
Executes the full autocorrelation test, module detection, and local correlation computation.
from genecircuitry.hotspot_processing import run_hotspot_analysis
hs = run_hotspot_analysis(
hs,
adata,
cluster_key="leiden", # adata.obs column for grouping in plots
)
What it does:
hs.create_knn_graph()— builds k-nearest-neighbour graph in embedding space (config.HOTSPOT_N_NEIGHBORSneighbours)hs.compute_autocorrelations()— tests all genes; filters by FDR (config.HOTSPOT_FDR_THRESHOLD)hs.compute_local_correlations()— pairwise local correlations between significant geneshs.create_modules()— clusters genes into modules (minimumconfig.HOTSPOT_MIN_GENES_PER_MODULEgenes)hs.calculate_module_scores()— per-cell module activity scores
Key outputs stored in the Hotspot object:
| Attribute | Description |
|---|---|
hs.results |
Autocorrelation results DataFrame (all genes) |
hs.modules |
Gene → module assignment Series (-1 = unassigned) |
hs.local_correlation_z |
Pairwise local correlation z-scores matrix |
hs.module_scores |
Per-cell module scores DataFrame |
Output files
Hotspot results are saved to <output>/hotspot/:
| File | Description |
|---|---|
autocorrelation_results.csv |
Per-gene autocorrelation statistics |
gene_modules.csv |
Gene–module assignments |
significant_genes.csv |
Genes passing FDR threshold |
hotspot_result.pkl |
Full Hotspot object (pickle) |
Visualisations
Generated by genecircuitry/plotting/hotspot_plots.py:
Local correlation heatmap
from genecircuitry.plotting.hotspot_plots import plot_hotspot_local_correlations
plot_hotspot_local_correlations(hs)
# Saved to: config.FIGURES_DIR_HOTSPOT/hotspot_local_correlations.png
Shows the pairwise local correlation matrix across all significant genes, with genes ordered by module assignment.
Module scores violin plot
from genecircuitry.plotting.hotspot_plots import plot_module_scores_violin
plot_module_scores_violin(
hs,
adata,
cluster_key="leiden",
)
# Saved to: config.FIGURES_DIR_HOTSPOT/module_scores_violin.png
Enrichment-annotated heatmap
from genecircuitry.hotspot_processing import plot_hotspot_annotation
plot_hotspot_annotation(
hs,
gene_sets=["MSigDB_Hallmark_2020"], # see enrichment_analysis module
top_n_annotations=1,
)
Runs pathway over-representation analysis (ORA) on each module’s gene set and annotates the heatmap rows with the top enriched term. Requires pip install -e ".[enrichment]".
Complete workflow example
import scanpy as sc
from genecircuitry import config, set_random_seed
from genecircuitry.hotspot_processing import (
create_hotspot_object,
run_hotspot_analysis,
)
set_random_seed(42)
# Load clustered data
adata = sc.read_h5ad("results/clustered_adata.h5ad")
# Create Hotspot object
hs = create_hotspot_object(
adata,
top_genes=500,
layer_key="raw_count",
embedding_key="X_pca",
)
# Run full analysis
hs = run_hotspot_analysis(hs, adata, cluster_key="leiden")
# Inspect modules
print(f"Modules detected: {hs.modules.max()}")
print(f"Genes per module:\n{hs.modules.value_counts()}")
# Save
import pickle
with open(f"{config.OUTPUT_DIR}/hotspot/hotspot_result.pkl", "wb") as f:
pickle.dump(hs, f)
hs.results.to_csv(f"{config.OUTPUT_DIR}/hotspot/autocorrelation_results.csv")
Configuration reference
| Config parameter | Default | Description |
|---|---|---|
HOTSPOT_TOP_GENES |
500 |
Top autocorrelated genes to test |
HOTSPOT_N_NEIGHBORS |
30 |
Neighbours for spatial KNN graph |
HOTSPOT_FDR_THRESHOLD |
0.05 |
FDR cutoff for significant genes |
HOTSPOT_MIN_GENES_PER_MODULE |
10 |
Minimum genes required to form a module |
HOTSPOT_CORE_ONLY |
True |
Use only core genes per module |
HOTSPOT_N_JOBS |
8 |
Parallel jobs for autocorrelation test |
FIGURES_DIR_HOTSPOT |
output/figures/hotspot |
Plot output directory |
Troubleshooting
ImportError: hotspot — install with pip install -e ".[hotspot]" or skip with --skip-hotspot.
No modules detected — try increasing config.HOTSPOT_FDR_THRESHOLD (e.g. to 0.1) or decreasing config.HOTSPOT_MIN_GENES_PER_MODULE.
KeyError: 'raw_count' — ensure adata.layers['raw_count'] exists. Run perform_normalization() which stores raw counts automatically, or set layer_key=None to use adata.X.
top_genes too small — if fewer than top_genes genes pass the initial variance filter, Hotspot uses all available genes. Check hs.results to see how many were tested.