The single-cell BCR-Seq (VDJ-B) assay resolves the paired B-cell receptor (immunoglobulin heavy and light chain) sequence of individual B cells, and is a component of the CMI-Flu prediction challenge. It accompanies the scRNA/TCR-Seq data and the single-cell TCR-Seq (VDJ-T) data. An overview of all the data, including links to more detailed descriptions of the other data, may be found at CMI-x .
PBMCs were processed for single-cell immune profiling on the 10x
Genomics Chromium platform, and BCR (VDJ-B) libraries were sequenced and
assembled into contig annotations using Cell Ranger vdj
against the GRCh38 V(D)J reference.
vdj, and are provided in the
AIRR
Rearrangement format.is_cell) and productive.2026-10-20 new dataset:
2025LJI_vdj-b.tsv
The VDJ-B data consists of 1 table : 2025LJI_vdj-b.tsv,
containing paired single-cell BCR sequences for the 2025LJI cohort at
the Day 7 post-vaccination timepoint. These data are not directly linked
to a specific prediction task, but can be leveraged to build more robust
models and improve predictions for other data types within the 2025LJI
prediction challenge cohort.
| Single-cell BCR-Seq (VDJ-B) Assay | |
| Data dictionary | |
| Column | Description |
|---|---|
| cell_id | Cell barcode (10x GEM barcode); identifies the individual cell within a sample. |
| clone_id | Cell Ranger clonotype assignment (per sample). Cells sharing a clonotype have matching paired receptor sequences. |
| sequence_id | Unique identifier of the assembled contig. |
| sequence | Full contig nucleotide sequence. |
| sequence_aa | Translated amino acid sequence of the contig. |
| productive | Whether the contig is productive (in-frame, no stop codon). TRUE throughout. |
| rev_comp | Whether the sequence is reverse-complemented relative to the reference (FALSE throughout). |
| v_call | V gene assignment (e.g. IGHV1-69). The prefix gives the locus: IGH (heavy), IGK (kappa), IGL (lambda). |
| v_cigar | CIGAR string of the V gene alignment to the contig. |
| d_call | D gene assignment. NA for light chains and where no D segment was called. |
| d_cigar | CIGAR string of the D gene alignment. |
| j_call | J gene assignment. |
| j_cigar | CIGAR string of the J gene alignment. |
| c_call | Constant gene assignment; for heavy chains this gives the isotype (e.g. IGHM, IGHG1, IGHA1). NA where no C segment was called. |
| c_cigar | CIGAR string of the C gene alignment. |
| sequence_alignment | Contig sequence aligned to the germline reference. |
| germline_alignment | Assembled germline (unmutated reference) sequence; compare with `sequence_alignment` to assess somatic hypermutation. |
| junction | Junction (CDR3 plus conserved flanking residues), nucleotide sequence. |
| junction_aa | Junction, amino acid sequence (the principal antigen-contacting loop). |
| junction_length | Junction length (nucleotides). |
| junction_aa_length | Junction length (amino acids). |
| v_sequence_start | Start position of the V segment within the contig. |
| v_sequence_end | End position of the V segment within the contig. |
| d_sequence_start | Start position of the D segment within the contig. |
| d_sequence_end | End position of the D segment within the contig. |
| j_sequence_start | Start position of the J segment within the contig. |
| j_sequence_end | End position of the J segment within the contig. |
| c_sequence_start | Start position of the C segment within the contig. |
| c_sequence_end | End position of the C segment within the contig. |
| consensus_count | Number of sequencing reads supporting the contig. |
| duplicate_count | Number of UMIs (unique molecules) supporting the contig. |
| is_cell | Whether the barcode was called as a cell (TRUE throughout the published table). |
| repertoire_id | Repertoire / 10x library identifier. |
| experiment | Experiment / GEM batch identifier. |
| sample | Sample identifier. |
| participant_id | Donor ID, in the format studyID.subjectID (links to participants.tsv). |
| timepoint | Days relative to influenza vaccination (7). |
| study_accession | Study identifier. Links to the investigations.tsv file. |
Data preview:
library(tidyverse)
vdj <- read_tsv("../datasets/260512/train_part2/2025LJI_vdj-b.tsv", show_col_types = FALSE) %>%
mutate(locus = substr(v_call, 1, 3))
head(vdj)
## # A tibble: 6 × 39
## cell_id clone_id sequence_id sequence sequence_aa productive rev_comp v_call
## <chr> <chr> <chr> <chr> <chr> <lgl> <lgl> <chr>
## 1 AAACCTCC… clonoty… AAACCTCCAA… GTGGGTC… MAWIPLLLPL… TRUE FALSE IGLV3…
## 2 AAACCTGT… clonoty… AAACCTGTCC… TGGGGAT… MDWTWRFLFV… TRUE FALSE IGHV1…
## 3 AAACCTGT… clonoty… AAACCTGTCC… TGGGGGA… MRLPAQLLGL… TRUE FALSE IGKV2…
## 4 AAACCTGT… clonoty… AAACCTGTCG… TGGGAGT… MAWTVLLLGL… TRUE FALSE IGLV3…
## 5 AAACCTGT… clonoty… AAACCTGTCG… TGGGGGA… MDWTWRILFL… TRUE FALSE IGHV1…
## 6 AAACGCGA… clonoty… AAACGCGAGC… GGGACCC… MDWTWSILFL… TRUE FALSE IGHV1…
## # ℹ 31 more variables: v_cigar <chr>, d_call <chr>, d_cigar <chr>,
## # j_call <chr>, j_cigar <chr>, c_call <chr>, c_cigar <chr>,
## # sequence_alignment <chr>, germline_alignment <chr>, junction <chr>,
## # junction_aa <chr>, junction_length <dbl>, junction_aa_length <dbl>,
## # v_sequence_start <dbl>, v_sequence_end <dbl>, d_sequence_start <dbl>,
## # d_sequence_end <dbl>, j_sequence_start <dbl>, j_sequence_end <dbl>,
## # c_sequence_start <dbl>, c_sequence_end <dbl>, consensus_count <dbl>, …
print("Contigs by locus:")
## [1] "Contigs by locus:"
table(vdj$locus)
##
## IGH IGK IGL
## 17481 9164 7711
Participants x Timepoints:
table(unique(vdj[, c("participant_id", "timepoint")])$timepoint)
##
## 7
## 35
IGH V-gene usage. The plot below shows the 20 most frequently used IGH (heavy chain) V genes across all cells, as a percentage of IGH contigs.
ighv <- vdj %>%
filter(locus == "IGH", !is.na(v_call)) %>%
count(v_call, name = "n") %>%
mutate(pct = 100 * n / sum(n)) %>%
slice_max(pct, n = 20)
p1 <- ggplot(ighv, aes(x = reorder(v_call, pct), y = pct)) +
geom_col(fill = "#1b7837", alpha = 0.85, width = 0.7) +
coord_flip() +
labs(title = "Top 20 IGH V-gene usage (2025LJI, Day 7)",
x = NULL, y = "% of IGH contigs") +
theme_bw(base_size = 12)
p1
Isotype usage. The heavy-chain constant gene
(c_call) gives each B cell’s isotype. The plot below shows
the share of IGH contigs assigned to each isotype; contigs with no
constant gene called are excluded.
isotype <- vdj %>%
filter(locus == "IGH", !is.na(c_call)) %>%
count(c_call, name = "n") %>%
mutate(pct = 100 * n / sum(n))
p2 <- ggplot(isotype, aes(x = reorder(c_call, -pct), y = pct)) +
geom_col(fill = "#E08214", alpha = 0.85, width = 0.7) +
geom_text(aes(label = sprintf("%.1f%%", pct)), vjust = -0.3, size = 3.5) +
labs(title = "Isotype usage (2025LJI, Day 7)",
x = NULL, y = "% of IGH contigs") +
expand_limits(y = max(isotype$pct) * 1.1) +
theme_bw(base_size = 12)
p2
Clone size Each cell is assigned to a clonotype (cells with matching paired receptors). The plot below bins clonotypes by size (number of cells) and shows what fraction of the sampled B cells fall into each expansion class. Most cells sit in small clonotypes, with a tail of larger expanded clones.
clone_size <- vdj %>%
filter(!is.na(clone_id)) %>%
distinct(participant_id, sample, clone_id, cell_id) %>%
count(participant_id, sample, clone_id, name = "clone_size")
cells_by_class <- clone_size %>%
mutate(class = cut(clone_size, breaks = c(0, 1, 2, 5, 20, Inf),
labels = c("1", "2", "3-5", "6-20", ">20"))) %>%
# weight by clone_size so the y-axis is the fraction of cells, not clonotypes
group_by(class) %>%
summarise(cells = sum(clone_size), .groups = "drop") %>%
mutate(pct_cells = 100 * cells / sum(cells))
p3 <- ggplot(cells_by_class, aes(x = class, y = pct_cells)) +
geom_col(fill = "#756BB1", alpha = 0.85, width = 0.7) +
geom_text(aes(label = sprintf("%.1f%%", pct_cells)), vjust = -0.3, size = 3.5) +
labs(title = "Clonal expansion (2025LJI, Day 7)",
subtitle = "Share of sampled B cells by clonotype size",
x = "Clone size (cells)", y = "% of cells") +
expand_limits(y = max(cells_by_class$pct_cells) * 1.1) +
theme_bw(base_size = 12)
p3