V(D)J Recombination and Adaptive Immune Receptors
Scanned 5/27/2026
Install via CLI
openskills install Pavel-Kravchenko/Bioinformatics---
name: bio-applied-vdj-biology
description: V(D)J Recombination and Adaptive Immune Receptors
tool_type: python
primary_tool: Python
---
# V(D)J Recombination and Adaptive Immune Receptors
- [IMGT database](https://www.imgt.org/)
- [AIRR Community standards](https://docs.airr-community.org/)
- [scirpy documentation](https://scirpy.scverse.org/)
- [Dandelion documentation](https://sc-dandelion.readthedocs.io/)
## Adaptive Immune Receptor Biology
The adaptive immune system recognizes pathogens through highly diverse antigen receptors generated by **V(D)J recombination** — a somatic DNA rearrangement unique to lymphocytes. Each lymphocyte expresses a single receptor specificity, and the collection of all receptor sequences in an organism forms the **immune repertoire**.
### T-Cell Receptors (TCR)
- **αβ TCR** (most peripheral T cells): heterodimer of α chain (TRAV + TRAJ) and β chain (TRBV + TRBD + TRBJ)
- **γδ TCR** (tissue-resident surveillance): heterodimer of γ chain (TRGV + TRGJ) and δ chain (TRDV + TRDD + TRDJ)
- TCRs bind **peptide-MHC (pMHC)** complexes presented by antigen-presenting cells
### B-Cell Receptors (BCR) and Antibodies
- Heavy chain: IGHV + IGHD + IGHJ (rearranged), then class switching to different constant regions (IgM, IgG, IgA, IgE, IgD)
- Light chain: κ (IGKV + IGKJ) or λ (IGLV + IGLJ) — no D segment
- Secreted BCR = antibody
### CDR Loops and Antigen Contact
Each chain contributes three **Complementarity Determining Regions (CDR1, CDR2, CDR3)** to the antigen-binding site:
- CDR1 and CDR2 are encoded in the V gene segment (germline-encoded)
- **CDR3 spans the V–D–J junction** — the most variable region due to imprecise joining and nucleotide additions
### Diversity Generation Mechanisms
| Mechanism | Description | Approx. contribution |
|-----------|-------------|----------------------|
| Combinatorial V(D)J | Number of V×D×J combinations | ~10⁶ |
| Junctional diversity | Exonuclease trimming + P/N nucleotides | ~10⁶ |
| Heavy×Light chain pairing | Paired receptor combinations | ~10¹² |
| Somatic hypermutation | B cells only; AID-mediated point mutations | Affinity maturation |
**Total theoretical diversity**: >10¹⁵ unique antibodies; >10¹⁸ for T cells
## IMGT Nomenclature
The **International ImMunoGeneTics information system (IMGT)** provides the standardized nomenclature for all immunoglobulin and T-cell receptor genes.
### Gene Segment Naming Convention
Format: `[locus][gene family]-[subgroup]*[allele]`
Examples:
- `TRBV20-1*01` — T-cell receptor Beta Variable gene 20, subgroup 1, allele 01
- `IGHV3-30*03` — Immunoglobulin Heavy Variable gene family 3, gene 30, allele 03
- `TRBJ2-7*01` — T-cell receptor Beta Joining gene 2, position 7, allele 01
### Locus Prefixes
| Prefix | Chain | Receptor |
|--------|-------|----------|
| `TRAV`, `TRAJ` | alpha (α) | TCR αβ |
| `TRBV`, `TRBD`, `TRBJ` | beta (β) | TCR αβ |
| `TRGV`, `TRGJ` | gamma (γ) | TCR γδ |
| `TRDV`, `TRDD`, `TRDJ` | delta (δ) | TCR γδ |
| `IGHV`, `IGHD`, `IGHJ` | heavy | BCR/Ab |
| `IGKV`, `IGKJ` | kappa light | BCR/Ab |
| `IGLV`, `IGLJ` | lambda light | BCR/Ab |
### CDR3 Definition (IMGT)
CDR3 spans from position 105 (conserved Cys at V gene end) to position 117 (conserved Phe/Trp at J gene start). The CDR3 includes:
1. Last few codons of V (trimmed by RAG/exonuclease)
2. N-nucleotides added by TdT (non-templated)
3. All of D segment (if present)
4. N-nucleotides
5. First few codons of J
### Somatic Hypermutation (B cells only)
After antigen activation, germinal center B cells undergo AID (Activation-Induced Cytidine Deaminase)-mediated hypermutation at ~10⁻³ mutations/bp/generation (10⁶× higher than somatic mutation rate). Mutations accumulate in CDRs → affinity maturation.
## Clonotype Definition and AIRR Format
### Clonotype Definition Strategies
| Strategy | Definition | Use case |
|----------|-----------|----------|
| Strict | Identical V + J + CDR3 nucleotide | Tracking exact clones |
| AA | Same V + J + CDR3 amino acid | Convergent recombination |
| CDR3 only | Identical CDR3 amino acid | Cross-individual public clones |
| Distance-based | CDR3 Hamming distance ≤ 1 | Clonal family clustering |
### AIRR (Adaptive Immune Receptor Repertoire) Format
The AIRR Community standard defines a TSV format for clonotype tables with required fields:
| Field | Description |
|-------|-------------|
| `sequence_id` | Unique read/UMI identifier |
| `sequence` | Full nucleotide sequence |
| `v_call` | IMGT V gene call (e.g., `TRBV20-1*01`) |
| `d_call` | IMGT D gene call |
| `j_call` | IMGT J gene call |
| `junction` | CDR3 nucleotide sequence (incl. conserved C/F) |
| `junction_aa` | CDR3 amino acid sequence |
| `duplicate_count` | Read/UMI count supporting this sequence |
### 10x Genomics V(D)J output
Cell Ranger outputs `filtered_contig_annotations.csv` with columns: `barcode`, `chain`, `v_gene`, `d_gene`, `j_gene`, `cdr3`, `cdr3_nt`, `reads`, `umis`, `raw_clonotype_id`
```python
np.random.seed(0)
# Generate a synthetic AIRR-format clonotype table for a CD8+ T cell sample
trbv_genes = ["TRBV2", "TRBV5-1", "TRBV6-5", "TRBV9", "TRBV12-3",
"TRBV14", "TRBV18", "TRBV20-1", "TRBV25-1", "TRBV28",
"TRBV29-1", "TRBV4-1", "TRBV7-2", "TRBV30"]
trbj_genes = ["TRBJ1-1", "TRBJ1-2", "TRBJ1-3", "TRBJ1-4", "TRBJ1-5", "TRBJ1-6",
"TRBJ2-1", "TRBJ2-2", "TRBJ2-3", "TRBJ2-5", "TRBJ2-7"]
amino_acids = list("ACDEFGHIKLMNPQRSTVWY")
def random_cdr3_aa(min_len=8, max_len=18):
"""Generate a realistic CDR3 amino acid sequence starting with C and ending with F."""
length = np.random.randint(min_len, max_len + 1)
middle = "".join(np.random.choice(amino_acids, length - 2))
return "C" + middle + "F"
# Create clonotype table with realistic power-law clone size distribution
n_clones = 200
clone_sizes = np.random.zipf(1.8, n_clones)
clone_sizes = np.sort(clone_sizes)[::-1]
clone_sizes = np.minimum(clone_sizes, 5000) # cap at 5000
clonotype_table = pd.DataFrame({
'clone_id': [f"clonotype{i+1}" for i in range(n_clones)],
'v_call': np.random.choice(trbv_genes, n_clones),
'd_call': np.random.choice(["TRBD1", "TRBD2"], n_clones),
'j_call': np.random.choice(trbj_genes, n_clones),
'junction_aa': [random_cdr3_aa() for _ in range(n_clones)],
'duplicate_count': clone_sizes,
'cdr3_length': None
})
clonotype_table['cdr3_length'] = clonotype_table['junction_aa'].str.len()
total_reads = clonotype_table['duplicate_count'].sum()
clonotype_table['frequency'] = clonotype_table['duplicate_count'] / total_reads
print("=== Synthetic AIRR Clonotype Table (CD8+ TIL sample) ===")
print(f"Total clones: {n_clones}")
print(f"Total reads: {total_reads:,}")
print(f"Top clone frequency: {clonotype_table['frequency'].max():.1%}")
print()
print(clonotype_table[['clone_id', 'v_call', 'j_call', 'junction_aa', 'duplicate_count', 'frequency']].head(10).to_string(index=False))
```
## Clonotype Diversity Metrics
Immune repertoire diversity is a key indicator of immune health, aging, and disease.
### Shannon Entropy (H)
Measures the uncertainty/diversity of a distribution:
$$H = -\sum_{i=1}^{N} p_i \ln(p_i)$$
- $p_i$ = frequency of clone $i$
- $H_{max} = \ln(N)$ for a perfectly uniform repertoire
- Normalized Shannon: $H' = H / \ln(N)$ (0 to 1 scale)
- A tumor-infiltrating sample dominated by 1-2 clones has low $H$
### Simpson Index (D) / Gini-Simpson (1-D)
$$D = \sum_{i=1}^{N} p_i^2$$
- D = 1 if one clone dominates (maximum clonality)
- D ≈ 0 if all clones are equally abundant
- Often reported as 1-D (diversity) or 1/D (effective number of clones)
### Clonality
$$\text{Clonality} = 1 - H' = 1 - \frac{H}{\ln(N)}$$
Values approach 1 for monoclonal expansions (e.g., acute infection, leukemia).
### Chao1 Estimator (Unseen Species)
$$\hat{S}_{Chao1} = S_{obs} + \frac{f_1^2}{2 f_2}$$
Where $f_1$ = singletons, $f_2$ = doubletons. Estimates true repertoire size from sampled data.
```python
np.random.seed(42)
# Simulate two samples with different V-gene usage profiles
# Healthy sample: balanced usage tumor TIL: biased toward TRBV20-1 (common in tumor-reactive clones)
trbv_all = ["TRBV2", "TRBV5-1", "TRBV6-5", "TRBV9", "TRBV12-3",
"TRBV14", "TRBV18", "TRBV20-1", "TRBV25-1", "TRBV28",
"TRBV29-1", "TRBV4-1", "TRBV7-2", "TRBV30"]
# Healthy: roughly uniform V gene usage
healthy_vgene_probs = np.ones(len(trbv_all)) / len(trbv_all)
healthy_vgene_counts = np.random.multinomial(2000, healthy_vgene_probs)
# TIL: TRBV20-1 expanded (common antigen-specific motif)
til_vgene_probs = np.ones(len(trbv_all))
til_vgene_probs[trbv_all.index("TRBV20-1")] = 8.0
til_vgene_probs[trbv_all.index("TRBV9")] = 4.0
til_vgene_probs /= til_vgene_probs.sum()
til_vgene_counts = np.random.multinomial(1200, til_vgene_probs)
# CDR3 length distribution
cdr3_lengths_healthy = np.random.normal(14, 2.5, 500).round().astype(int)
cdr3_lengths_healthy = np.clip(cdr3_lengths_healthy, 8, 22)
cdr3_lengths_til = np.random.normal(13, 2.0, 300).round().astype(int)
cdr3_lengths_til = np.clip(cdr3_lengths_til, 8, 22)
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
# Panel 1: V gene usage comparison
x = np.arange(len(trbv_all))
w = 0.35
axes[0].bar(x - w/2, healthy_vgene_counts/healthy_vgene_counts.sum()*100,
w, label='Healthy Blood', color='steelblue', alpha=0.8)
axes[0].bar(x + w/2, til_vgene_counts/til_vgene_counts.sum()*100,
w, label='Tumor TIL', color='firebrick', alpha=0.8)
axes[0].set_xticks(x)
axes[0].set_xticklabels([g.replace("TRBV","") for g in trbv_all], rotation=45, ha='right', fontsize=8)
axes[0].set_xlabel('TRBV Gene')
axes[0].set_ylabel('Usage (%)')
axes[0].set_title('TRBV Gene Usage')
axes[0].legend(fontsize=9)
# Panel 2: CDR3 length distribution (spectratype)
bins = range(7, 24)
axes[1].hist(cdr3_lengths_healthy, bins=bins, alpha=0.6, label='Healthy Blood',
color='steelblue', density=True)
axes[1].hist(cdr3_lengths_til, bins=bins, alpha=0.6, label='Tumor TIL',
color='firebrick', density=True)
axes[1].set_xlabel('CDR3 Length (aa)')
axes[1].set_ylabel('Density')
axes[1].set_title('CDR3 Length Distribution\n(Spectratype)')
axes[1].legend(fontsize=9)
# Panel 3: Clone size distribution (rank-abundance)
healthy_sorted = np.sort(healthy_vgene_counts)[::-1]
til_sorted = np.sort(til_vgene_counts)[::-1]
axes[2].semilogy(range(1, len(healthy_sorted)+1),
healthy_sorted/healthy_sorted.sum(),
'o-', color='steelblue', ms=4, label='Healthy Blood')
axes[2].semilogy(range(1, len(til_sorted)+1),
til_sorted/til_sorted.sum(),
's-', color='firebrick', ms=4, label='Tumor TIL')
axes[2].set_xlabel('Rank')
axes[2].set_ylabel('Clone Frequency (log scale)')
axes[2].set_title('Rank-Abundance (Clonal\nExpansion)')
axes[2].legend(fontsize=9)
plt.suptitle('TCR Beta Chain Repertoire Analysis', fontsize=13, fontweight='bold')
plt.tight_layout()
plt.savefig('tcr_repertoire_analysis.png', dpi=120, bbox_inches='tight')
plt.show()
print("Figure saved.")
```
## Clonotype Diversity Metrics
> Shannon entropy, Simpson index, clonal dominance. Compare diversity between conditions (tumor vs blood, pre/post treatment). Rarefaction curves for sampling depth normalization.
## Pitfalls
- **Coordinate systems**: BED uses 0-based half-open; VCF/GFF use 1-based inclusive — mixing them causes off-by-one errors
- **Batch effects**: Always check for batch confounding before interpreting biological signal
- **Multiple testing**: Apply FDR correction (Benjamini-Hochberg) when testing thousands of features simultaneously
No comments yet. Be the first to comment!