| name | bio-applied-immune-repertoire |
| description | 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. |
| tool_type | python |
| primary_tool | scirpy |
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.
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)
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.
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)
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).
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