Skip to content
Development
Skill

/multimodal-omics-integration

Multimodal and multi-section integration for spatial and single-cell omics: totalVI joint RNA + protein (Visium CytAssist Gene and Protein, CITE-seq), MultiVI joint RNA + ATAC (10x Multiome, spatial ATAC-RNA), BBKNN integration of several tissue sections, scanpy ingest label

BOOST
From plugin
sciagent-skills
382200 skills
Install
$ npx -y skills add jaechang-hits/SciAgent-Skills --skill multimodal-omics-integration --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/multimodal-omics-integration

Context preview

The summary Claude sees to decide when to auto-load this skill.

Multimodal and multi-section integration for spatial and single-cell omics: totalVI joint RNA + protein (Visium CytAssist Gene and Protein, CITE-seq), MultiVI joint RNA + ATAC (10x Multiome, spatial ATAC-RNA), BBKNN integration of several tissue sections, scanpy ingest label

SKILL.md

multimodal-omics-integration.SKILL.md
name: "multimodal-omics-integration"
description: "Multimodal and multi-section integration for spatial and single-cell omics: totalVI joint RNA + protein (Visium CytAssist Gene and Protein, CITE-seq), MultiVI joint RNA + ATAC (10x Multiome, spatial ATAC-RNA), BBKNN integration of several tissue sections, scanpy ingest label transfer between sections, and gene signature scores mapped on tissue. Outputs joint latent spaces, denoised protein maps, integrated clusters, transferred labels. For dissociated CITE-seq and scVI/scANVI use scvi-tools-single-cell; for MOFA+ factor analysis use mofaplus-multi-omics or muon-multiomics-singlecell; for Harmony use harmony-batch-correction."
license: "CC-BY-4.0"

Multimodal Omics Integration

Overview

Combine modalities measured on the same spots or cells, or several sections of the same tissue, into one representation. Output: joint latent spaces and clusters, denoised protein maps, integrated sections, transferred labels and signature scores on the tissue.

| Question | Method | Module | |---|---|---| | RNA + protein on the same spots / cells | totalVI | 1-2 | | RNA + chromatin accessibility on the same cells | MultiVI | 3 | | Several sections or samples, one modality | BBKNN | 4 | | Labels from an annotated section onto a new one | scanpy ingest | 5 | | Pathway or cell state activity on tissue | `sc.tl.score_genes` | 6 | | Unsupervised factors across 2+ omics | MOFA+ | `mofaplus-multi-omics` |

When to Use

  • Analysing Visium CytAssist Gene and Protein (or CITE-seq) data with RNA and protein in one model
  • Integrating RNA and ATAC measured on the same cells (10x Multiome, spatial ATAC-RNA)
  • Clustering several tissue sections together without merging their coordinates
  • Copying labels from an annotated section to a new section of the same platform
  • Mapping pathway or cell state signatures onto the tissue
  • Use `scvi-tools-single-cell` instead for dissociated scVI/scANVI workflows without a spatial angle
  • For factor analysis across omics layers, use `mofaplus-multi-omics` or `muon-multiomics-singlecell` instead

Prerequisites

  • **Input**: raw counts for every modality (totalVI and MultiVI model counts)
  • **Compute**: scvi-tools pulls torch; GPU recommended for MultiVI, totalVI runs on CPU

for a few thousand spots

  • **Python packages**: `scvi-tools`, `mudata` (Module 3), `bbknn` (Module 4), `scanpy`
# Install only what `import` reports missing. `--pypi` keeps the env's scanpy/numba
# stack; a conda add can swap it and bare pip can shadow it.
pixi add --pypi scvi-tools mudata bbknn scanpy

Quick Start

Integrate two sections and cluster them jointly:

import bbknn
import scanpy as sc

adata = sc.concat({"s1": sc.read_h5ad("section1.h5ad"), "s2": sc.read_h5ad("section2.h5ad")},
                  label="section", index_unique="-")
sc.pp.normalize_total(adata); sc.pp.log1p(adata); sc.pp.pca(adata, n_comps=30)
bbknn.bbknn(adata, batch_key="section", n_pcs=30)
sc.tl.leiden(adata, key_added="joint_cluster", flavor="igraph", n_iterations=2)

Core API

Module 1: Load Spatial RNA + Protein (Visium CytAssist)

Demo: 10x human tonsil, FFPE, CytAssist Gene and Protein (~35 antibodies). For your own data set `OUTS` to the Space Ranger `outs/` folder.

import tarfile
import urllib.request
from pathlib import Path

import numpy as np
import pandas as pd
import scanpy as sc

CACHE = Path("data_cache/CytAssist_FFPE_Protein_Expression_Human_Tonsil")
BASE = ("https://cf.10xgenomics.com/samples/spatial-exp/2.1.0/CytAssist_FFPE_Protein_Expression_Human_Tonsil/"
        "CytAssist_FFPE_Protein_Expression_Human_Tonsil_")
CACHE.mkdir(parents=True, exist_ok=True)
for name in ["filtered_feature_bc_matrix.h5", "spatial.tar.gz"]:
    if not (CACHE / name).exists():
        urllib.request.urlretrieve(BASE + name, CACHE / name)          # ~37 MB + ~34 MB
if not (CACHE / "spatial").exists():
    with tarfile.open(CACHE / "spatial.tar.gz") as tar:
        tar.extractall(CACHE, filter="data")
OUTS = CACHE

raw = sc.read_10x_h5(OUTS / "filtered_feature_bc_matrix.h5", gex_only=False)
raw.var_names_make_unique()
print(raw.var["feature_types"].value_counts())   # Gene Expression + Antibody Capture
pos = pd.read_csv(OUTS / "spatial/tissue_positions.csv", index_col=0)
raw.obsm["spatial"] = pos.loc[raw.obs_names, ["pxl_col_in_fullres", "pxl_row_in_fullres"]].to_numpy(float)

rna = raw[:, raw.var["feature_types"] == "Gene Expression"].copy()
prot = raw[:, raw.var["feature_types"] == "Antibody Capture"]
rna.obsm["protein_expression"] = pd.DataFrame(prot.X.toarray(), index=prot.obs_names,
                                              columns=prot.var_names)
sc.pp.filter_cells(rna, min_counts=200)
print(rna, f"\nproteins: {rna.obsm['protein_expression'].shape[1]}")

Module 2: Joint RNA + Protein with totalVI

totalVI separates protein foreground from background binding and learns one latent space from both modalities.

import scvi

rna.layers["counts"] = rna.X.copy()
tmp = rna.copy()                                 # HVGs on log data; model on counts
sc.pp.normalize_total(tmp)
sc.pp.log1p(tmp)
sc.pp.highly_variable_genes(tmp, n_top_genes=4000)
rna = rna[:, tmp.var["highly_variable"]].copy()

scvi.model.TOTALVI.setup_anndata(rna, layer="counts", protein_expression_obsm_key="protein_expression",
                                 batch_key=None)  # set to the section/sample column for several slides
model = scvi.model.TOTALVI(rna, n_latent=20)
model.train(max_epochs=400, early_stopping=True)

rna.obsm["X_totalVI"] = model.get_latent_representation()
rna_norm, prot_norm = model.get_normalized_expression(n_samples=25, return_mean=True)
rna.obsm["protein_denoised"] = prot_norm         # DataFrame, spots x proteins
sc.pp.neighbors(rna, use_rep="X_totalVI")
sc.tl.umap(rna)
sc.tl.leiden(rna, resolution=0.6, key_added="totalvi_leiden", flavor="igraph", n_iterations=2)

show = list(prot_norm.columns[:4])
for p in show:
Read more
Ships withsciagent-skills

Turn your AI coding agent into a life sciences expert — 199 bioinformatics skills for Claude Code covering RNA-seq, single-cell analysis, genomics, proteomics, drug discovery, and more. Boosted BixBench from 65% to 92%. Open source.

Get the whole plugin

Other skills on sciagent-skills.