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 plasmablasts, and is a component of the CMI-Flu prediction challenge. 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 collected at day 7 post-vaccination were individually hashtagged with oligonucleotide-barcoded antibodies. Antibody-secreting cells (gated as Live/CD3-CD14-CD19+CD20-CD71+CD27hiCD38hi) were sorted on a Cytek Aurora CS, with up to 6,000 cells per participant and pooled. Samples were processed in two runs of 20 participants each. Sorted cells were immediately loaded on a 10x Genomics Chromium Controller. Gene expression, hashtag (HTO) and BCR (V(D)J) libraries were prepared using the Chromium Next GEM Single Cell 5′ Reagent Kit v2 (Dual Index). Libraries were sequenced on an Illumina NovaSeq, targeting 20,000 reads per cell for gene expression and 5,000 reads per cell each for HTO and V(D)J.

Cellranger multi processed each of the 4 libraries (2 sorts × 2 GEM wells) against the GRCh38 reference, producing sample-demultiplexed gene expression count matrices and assembled paired BCR sequences. In Seurat (R), low-quality cells were removed. The libraries were then merged, log-normalized and clustered (PCA → UMAP → graph-based clustering) for downstream analysis. Following clustering, clusters showing no expression of B cell markers, but having expression of other immune cells (T cells, monocytes) were removed.

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