Data updates: See the change log.

2026-10-20: new dataset: 2025LJI_vdj-b.tsv.

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 .

Experimental protocol

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.

Data Standardization

  • Contigs were assembled and annotated with CellRanger vdj, and are provided in the AIRR Rearrangement format.
  • The published table is restricted to contigs called as cells (is_cell) and productive.

The Data

Change log

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.

Data tables

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 exploration

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