# Bio Applied Immune Repertoire

> Analyze TCR/BCR repertoires with scirpy: import MiXCR/10x/AIRR clonotypes, define clonotypes, compute clonal expansion/diversity/VDJ usage. Use when analyzing scTCR-seq/scBCR-seq, clonotype tables, or CDR3 spectratypes.

- Skill: `pavel-kravchenko/bio-applied-immune-repertoire` (Agent Skill)
- Install (CLI): `npx skillmds@latest add pavel-kravchenko/bio-applied-immune-repertoire`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pavel-kravchenko/bio-applied-immune-repertoire/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: pavel-kravchenko (https://skillmd.com/u/pavel-kravchenko)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/pavel-kravchenko/bio-applied-immune-repertoire

---


# TCR/BCR Repertoire Analysis with scirpy

## When to Use

- Importing 10x Genomics `filtered_contig_annotations.csv`, MiXCR clones, or AIRR-format `airr_rearrangement.tsv` into an AnnData for repertoire analysis
- Defining clonotypes and quantifying clonal expansion (singleton vs. expanded clones) in scTCR-seq or scBCR-seq data
- Computing repertoire diversity (Shannon entropy, Gini index, D50) per sample or condition
- Plotting V(D)J gene segment usage or CDR3 length spectratypes to characterize a repertoire's selection pressure
- Integrating clonotype calls with paired scRNA-seq gene expression for clonal-lineage-aware clustering/annotation

## Version Compatibility

- scirpy ≥0.20 (0.23.x current), anndata ≥0.10, scanpy ≥1.10, Python ≥3.10
- Input generators: Cell Ranger `vdj` pipeline ≥7.0, MiXCR ≥4.x, Immcantation/Change-O ≥1.3 (`MakeDb.py`, `DefineClones.py` produce AIRR-compatible TSVs scirpy reads directly)

## Prerequisites

- `pip install scirpy scanpy anndata`
- A clonotype/contig table: 10x `filtered_contig_annotations.csv`, a MiXCR `clones.tsv` exported to AIRR schema (`mixcr exportAirr`), or a native AIRR rearrangement TSV
- V(D)J recombination and CDR3 basics (see `bio-applied-vdj-biology`); for paired GEX integration see `bio-applied-single-cell-scanpy`

## Importing Clonotypes and Defining Clonal Identity

**Goal:** get 10x/MiXCR/AIRR data into a scirpy-ready AnnData and collapse contigs into clonotypes.
**Approach:** `ir.io.read_10x_vdj` or `ir.io.read_airr` builds one AnnData row per cell with chain data in `.obs`; `ir.pp.index_chains` + `ir.tl.chain_qc` flag ambiguous/multichain cells before `ir.tl.define_clonotypes` groups cells by identical (or near-identical) receptor sequence.

```python
import scirpy as ir
import scanpy as sc


def load_and_define_clonotypes(contig_csv, distance="identity"):
    """Load 10x VDJ contigs and define clonotypes by CDR3 nucleotide identity/similarity.

    contig_csv: path to Cell Ranger filtered_contig_annotations.csv
                (use ir.io.read_airr(path) instead for an AIRR rearrangement TSV,
                e.g. from `mixcr exportAirr` or Immcantation's MakeDb.py).
    distance: "identity" for exact CDR3 match, or a scirpy metric name
              (e.g. "hamming") for near-identical clonotype merging.
    Returns an AnnData with adata.obs['clone_id'] populated.
    """
    adata = ir.io.read_10x_vdj(contig_csv)
    ir.tl.chain_qc(adata)  # flags orphan/multichain/ambiguous cells in adata.obs
    adata = adata[adata.obs["chain_pairing"] != "multichain"].copy()
    ir.pp.ir_dist(adata, metric=distance, sequence="aa")
    ir.tl.define_clonotypes(adata, receptor_arms="all", dual_ir="primary_only")
    return adata
```

## Clonal Expansion and Diversity Metrics

**Goal:** quantify how skewed a repertoire is toward a few dominant clones, per sample/condition.
**Approach:** `ir.tl.clonal_expansion` buckets cells into expansion categories directly on `.obs`; `ir.tl.alpha_diversity` computes a named diversity metric per group (`normalized_shannon_entropy`, `D50`, `gini_index`, or any scikit-bio alpha-diversity metric) grouped by a categorical column such as `sample_id`.

```python
def compute_expansion_and_diversity(adata, groupby="sample_id"):
    """Annotate clonal expansion per cell and diversity per sample group.

    adata: AnnData with adata.obs['clone_id'] from define_clonotypes().
    groupby: adata.obs column identifying samples/timepoints to compare.
    Adds adata.obs['clonal_expansion'] and returns a per-group diversity DataFrame
    with normalized Shannon entropy, D50, and Gini index columns.
    """
    ir.tl.clonal_expansion(adata, clip_at=3)  # categories: 1, 2, >= 3 cells per clone
    metrics = {}
    for metric in ("normalized_shannon_entropy", "D50", "gini_index"):
        ir.tl.alpha_diversity(adata, groupby=groupby, target_col="clone_id", metric=metric)
        metrics[metric] = adata.uns[f"{groupby}_alpha_diversity_{metric}"]
    import pandas as pd
    return pd.DataFrame(metrics)
```

## V(D)J Gene Usage and CDR3 Spectratyping

**Goal:** characterize V/J segment usage bias and CDR3 length distribution, e.g. to compare pre/post-vaccination repertoires.
**Approach:** `ir.tl.group_abundance` tabulates V/J gene frequencies per group for a usage heatmap; `ir.tl.spectratype` bins CDR3 lengths per group, which `ir.pl.spectratype` renders as a stacked histogram (spectratyping reveals oligoclonal length skewing that a symmetric polyclonal repertoire would not show).

```python
def vdj_usage_and_spectratype(adata, groupby="sample_id", chain="VJ"):
    """Tabulate V-gene usage frequencies and CDR3-length spectratype per sample.

    adata: AnnData with defined clonotypes and chain-level v_call/junction_aa in .obs.
    chain: "VJ" (alpha/light) or "VDJ" (beta/heavy) locus to summarize.
    Returns (v_usage_df, spectratype_df) — both indexed by groupby categories.
    """
    v_usage = ir.tl.group_abundance(
        adata, groupby=f"{chain}_1_v_call", target_col=groupby, fraction=True
    )
    spectratype = ir.tl.spectratype(
        adata, cdr3_col=f"{chain}_1_junction_aa", groupby=groupby, fraction=True
    )
    return v_usage, spectratype
```

## Pitfalls

- **Clonotype vs. clonotype cluster**: `define_clonotypes` (exact/near-identical CDR3) merges convergent recombination events differently than `define_clonotype_clusters` (sequence-similarity network); pick the one matching your biological question (identity tracking vs. specificity grouping) before comparing diversity across studies.
- **Multichain and orphan cells**: always run `ir.tl.chain_qc` and filter `chain_pairing == "multichain"` before diversity/usage stats — doublets and multi-clone captures otherwise inflate apparent diversity.
- **Diversity metrics need a common denominator**: `alpha_diversity` values are only comparable across samples with similar cell counts; downsample or use a rarefaction-aware metric (e.g. Chao1 via scikit-bio) when group sizes differ substantially.
- **AIRR field naming from MiXCR/Immcantation**: exported TSVs must have exact AIRR column names (`junction_aa`, `v_call`, `j_call`, `duplicate_count`) — `mixcr exportAirr` and Change-O's `MakeDb.py`/`DefineClones.py` handle this, but hand-built TSVs often drop `productive` or `locus`, breaking `read_airr`.
- **BCR needs different tooling for lineage/SHM**: scirpy handles clonotyping and repertoire stats well but does not build somatic hypermutation lineage trees — hand BCR data to Immcantation (Change-O `DefineClones.py` + `BuildTrees.py`, `SHazaM`) for that step.

## See Also

- `bio-applied-vdj-biology` — V(D)J recombination and CDR3 structure background
- `immunogenomics` — joint scTCR/BCR + GEX analysis, HLA typing, neoantigen pipelines
- `bio-applied-hla-typing` — patient HLA typing feeding TCR-epitope/MHC restriction analysis
- `bio-applied-single-cell-scanpy` — paired gene-expression preprocessing and clustering for GEX+VDJ integration

