spatial-cnv
Load when inferring copy-number variation per spot on a preprocessed spatial AnnData with chromosome-annotated genes via infercnvpy (default — log-ratio sliding-window) or Numbat (R, allele-aware clon
SKILL.mdskills/spatial-cnv/SKILL.md
GitHubspatial-cnv
When to use
The user has a preprocessed spatial AnnData with gene-coordinate
metadata in var["chromosome"] / var["start"] / var["end"] and
wants per-spot copy-number variation inferred for tumour / normal
deconvolution. Two backends:
infercnvpy(default) — log-ratio sliding-window over chromosomes usinginfercnvpy. Tunables--window-size,--step,--infercnv-lfc-clip,--infercnv-chunksize,--infercnv-n-jobs.numbat— R-based, allele-aware clone deconvolution using phased SNP allele counts. Requiresobsm["allele_counts"]. Tunables--numbat-genome(hg19/hg38),--numbat-max-entropy,--numbat-min-llr,--numbat-min-cells,--numbat-ncores.
--reference-key <obs col> + --reference-cat <category> define
the normal-reference subset (strongly recommended).
Inputs & Outputs
<!-- AUTO-GENERATED from skill.yaml (interface) — do not edit by hand. Regenerate: python scripts/generate_skill_md.py <skill_dir> -->Inputs
- File types:
.h5ad - Requires a preprocessed AnnData (
Xnormalised, PCA/neighbours present) - Expects
obsm:spatial,allele_counts
Outputs
tables/allele_counts.csvtables/cnv_bin_summary.csvtables/cnv_group_sizes.csvtables/cnv_run_summary.csvtables/cnv_scores.csvtables/cnv_spatial_points.csvtables/cnv_umap_points.csvtables/numbat_calls.csvtables/numbat_clone_post.csvtables/numbat_results.csvfigures/cnv_bin_summary.pngfigures/cnv_group_sizes.pngfigures/cnv_groups_umap.pngfigures/cnv_heatmap.pngfigures/cnv_score_distribution.pngfigures/cnv_spatial.pngfigures/cnv_umap.pngfigures/cnv_uncertainty_distribution.pngfigures/cnv_uncertainty_spatial.pngnumbat_input.h5adprocessed.h5adreport.mdresult.json- Processed AnnData (
saves_h5ad) — addsobs:cnv_score,cnv_leiden,numbat_p_cnv,numbat_clone,numbat_entropy;uns:numbat_calls,numbat_clone_post
Flow
- Load AnnData (
--input) or build a demo with synthetic gene-coords (spatial_cnv.py:982-991). For Numbat, validateobsm["allele_counts"](_lib/cnv.py:93). - Sync
obsm["spatial"]↔obsm["X_spatial"]; castobs["cnv_leiden"]andobs["numbat_clone"]outputs to Categorical (spatial_cnv.py:84-86). The user-supplied--reference-keycolumn is NOT cast. - For
infercnvpy: runcnv.tl.infercnv(_lib/cnv.py:171) →cnv.tl.pca(_lib/cnv.py:188) →cnv.tl.leiden(_lib/cnv.py:210) →cnv.tl.cnv_score(_lib/cnv.py:219). - For
numbat: invoke R Numbat via the adapter; read clone posterior + per-cell entropy; write touns+obs(_lib/cnv.py:344-366). - Compute per-clone group sizes + per-bin mean CNV (
spatial_cnv.py:175-177). - Render heatmap / spatial / UMAP / clone-overlay / score-distribution / uncertainty plots.
- Save tables +
processed.h5ad+ report.
Gotchas
var["chromosome"]/var["start"]/var["end"]REQUIRED. Without per-gene genomic coordinates, infercnvpy cannot bin genes by chromosome. The--demopath injects synthetic coords (spatial_cnv.py:989-991); for real data, run gene-coord lookup (Ensembl / GENCODE) upstream.- No
--reference-key⇒ per-cell baseline drift. When no normal reference is set, infercnvpy treats the cohort mean as baseline, which inflates CNV calls in homogeneous tumour samples. Always pass--reference-key cell_type --reference-cat Normal(or analogous). - Numbat needs
obsm["allele_counts"]AND raw counts._lib/cnv.py:93raisesValueErrorif allele counts are missing; raw counts come fromadata.layers["counts"](_lib/cnv.py:67). Build the allele-count DataFrame upstream from phased VCFs (Numbat docs). obs["cnv_leiden"]has a no-cluster fallback._lib/cnv.py:216writespd.Categorical(np.repeat("cnv_all", adata.n_obs))when leiden clustering fails (e.g. degenerate CNV PCA). Inspectcnv_run_summary.csvto distinguish "1 clone" (real) from "fallback" (failure).--stepmust be ≤--window-size.spatial_cnv.py:1001-1002rejects withparser.errorwhen violated. Default window 100, step 10 ≈ 90% overlap.obs["cnv_score"]is set by infercnvpy'scnv.tl.cnv_score. OmicsClaw only fills NaNs (_lib/cnv.py:226) — don't expect this column when--method numbat; Numbat writesobs["numbat_p_cnv"]instead.
Key CLI
# Demo
python omicsclaw.py run spatial-cnv --demo --output /tmp/cnv_demo
# infercnvpy with explicit normal reference (default)
python omicsclaw.py run spatial-cnv \
--input preprocessed.h5ad --output results/ \
--method infercnvpy \
--reference-key cell_type --reference-cat Normal Stromal \
--window-size 100 --step 10 --infercnv-n-jobs 4
# Numbat (R, allele-aware) — requires obsm["allele_counts"]
python omicsclaw.py run spatial-cnv \
--input preprocessed_with_alleles.h5ad --output results/ \
--method numbat --numbat-genome hg38 \
--numbat-min-llr 5.0 --numbat-min-cells 50 --numbat-ncores 4
See also
references/parameters.md— every CLI flag, per-method tunablesreferences/methodology.md— when each backend wins; reference choicereferences/output_contract.md— per-methodobs/unskeys- Adjacent skills:
spatial-preprocess(upstream),spatial-domains(upstream — providesobs["leiden"]for cluster overlays),spatial-annotate(upstream — providesobs[cell_type]for--reference-cat),spatial-condition(parallel — DE between conditions),spatial-trajectory(parallel — clonal lineage if combined with CNV)