Data updates: See the change log.

2026-10-20: timpeoint post-vaccination timepoints added to 2025LJI.

The ex vivo flow cytometry assay (which measures the frequency of immune cell populations in the blood) 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

We performed spectral cytometry on PBMCs, staining for surface antibodies listed in Table 3. Gating strategy for all immune cell populations is available in Figure 3. For a given cell population, frequencies represent the proportion of cells within the indicated parent population. Samples with either less than 50% live PBMC at thawing or less than 30,000 live singlet cells recorded were excluded from the dataset.

Data processing and standardization

  • Cell population names were standardized according to the name_map .
  • Technical replicates were collapsed by taking the mean.
  • Baseline (“pre-vaccination”) values were established by averaging all values at pre-vaccination timepoints.
  • Publicly-available data were collated from multiple studies; note that these use a mix of reporting units (percentages, absolute counts, counts/µl).

The Data

Change log

2026-10-20 Post-vaccination timepoints added for Part 2 of the challenge

Data tables

The ex vivo flow data consists of 2 tables : publicData_ex_vivo_flow.tsv and 2025LJI_ex_vivo_flow.tsv contain both pre- and post-vaccination measurements collated from public studies and for the “prediction/challenge” cohort respectively. While these data are not directly linked to a specific prediction task, you can leverage them to build more robust models and improve predictions for other data types within the 2025 LJI prediction challenge cohort.

Ex vivo flow cytometry Assay
Data dictionary
Column Description
participant_id Donor ID, one for each subject in each study, in the format studyID.subjectID (links to participants.tsv)
timepoint Days relative to influenza vaccination, or 'Pre-vacc' for the mean of the pre-vaccination timepoints.
name Immune cell population measured (e.g. Classical_monocytes, Antibody-secreting_cells_(ASC), CD4_T_cells).
value Frequency of the population.
unit Unit of measurement. 'percentage' throughout the 2025LJI cohort; public data additionally include absolute counts (cells, cells/ul) and other units.
parent_population Denominator population for value (e.g. CD45plus_cells, CD4_T_cells, B_cells).
population_definition Gating strategy defining the population (e.g. CD3-CD19-CD56-HLADR+CD14+CD16- for classical monocytes).
material Sample type: PBMCs (2025LJI); public data also include Whole blood, Serum, and Other.
comments For 2025LJI, the batch/round in which the sample was acquired.
study_accession Study identifier. Links to the investigations.tsv file.
subject subject ID.

Data exploration

Data preview :

library(tidyverse)
flow = read_tsv('../datasets/260512/train_part2//2025LJI_ex_vivo_flow.tsv')
head(flow)
## # A tibble: 6 × 11
##   participant_id  timepoint name         value unit       parent_population
##   <chr>           <chr>     <chr>        <dbl> <chr>      <chr>            
## 1 2025LJI.SUB7666 Pre-vacc  Neutrophils 0.0006 percentage CD45plus_cells   
## 2 2025LJI.SUB7666 -14       Neutrophils 0.0013 percentage CD45plus_cells   
## 3 2025LJI.SUB7742 0         Neutrophils 0.0016 percentage CD45plus_cells   
## 4 2025LJI.SUB5670 -14       Neutrophils 0.0019 percentage CD45plus_cells   
## 5 2025LJI.SUB5670 Pre-vacc  Neutrophils 0.0023 percentage CD45plus_cells   
## 6 2025LJI.SUB7742 Pre-vacc  Neutrophils 0.0023 percentage CD45plus_cells   
## # ℹ 5 more variables: population_definition <chr>, material <chr>,
## #   comments <chr>, study_accession <chr>, subject <chr>
table(flow$timepoint)
## 
##      -14        0        1        7 Pre-vacc 
##     1478     1477     1479     1480     1478

The 2025LJI challenge cohort is measured uniformly as a percentage of parent. The plot below shows the landscape of all populations at baseline (Pre-vacc), ordered by median frequency.

lji <- read.delim("../datasets/260512/train_part2//2025LJI_ex_vivo_flow.tsv",
                  stringsAsFactors = FALSE)

base <- lji %>%
  filter(timepoint == "Pre-vacc", value > 0)

ord <- base %>%
  group_by(name) %>%
  summarise(med = median(value), .groups = "drop") %>%
  arrange(med) %>%
  pull(name)

base <- base %>% mutate(name = factor(name, levels = ord))

p1 <- ggplot(base, aes(name, value)) +
  geom_boxplot(outlier.shape = NA, alpha = 0.55, width = 0.6, fill = "#1b7837") +
  geom_jitter(width = 0.15, size = 0.7, alpha = 0.4) +
  scale_y_log10(labels = label_number()) +
  coord_flip() +
  labs(title = "Immune cell populations at baseline (2025LJI, Pre-vacc)",
       x = NULL, y = "% of parent population (log10)") +
  theme_bw(base_size = 11)

p1

Timpeoints:

library(patchwork)

pub <- read.delim("../datasets/260512/train_part2/publicData_ex_vivo_flow.tsv",
                  stringsAsFactors = FALSE)

table(pub$timepoint)
## 
##      -21       -5       -7        0        1        7 Pre-vacc 
##       92      112      677     6901     1148     5130     6911
# Kinetics of 2 cell populations from the public data

paired_traj <- function(pop, tps, use_unit) {
  d <- pub %>%
    filter(name == pop, unit == use_unit, timepoint %in% tps, value > 0) %>%
    mutate(timepoint = factor(timepoint, levels = c("Pre-vacc", "1", "7"),
                              labels = c("Pre-vacc", "Day 1", "Day 7"))) %>%
    droplevels() %>%
    # collapse technical replicates to one mean value per participant/timepoint
    group_by(participant_id, timepoint) %>%
    summarise(value = mean(value), .groups = "drop") %>%
    # keep only participants measured at both timepoints, so every point is
    # part of a connecting line (drops single-timepoint participants)
    group_by(participant_id) %>%
    filter(n_distinct(timepoint) == 2) %>%
    ungroup()

  # One line per participant (participant_id = studyID.subjectID, unique across
  # public studies); red line is the median.
  ggplot(d, aes(timepoint, value, group = participant_id)) +
    geom_line(alpha = 0.3) +
    geom_point(alpha = 0.4, size = 0.9) +
    stat_summary(aes(group = 1), fun = median, geom = "line",
                 color = "red", linewidth = 1.1) +
    scale_y_log10(labels = label_number()) +
    labs(title = pop, x = NULL,
         y = paste0(use_unit, " (log10)")) +
    theme_bw(base_size = 12)
}

p_mono <- paired_traj("Classical_monocytes",
                      c("Pre-vacc", "1"), use_unit = "cells/ul")
p_asc  <- paired_traj("Antibody-secreting_cells_(ASC)",
                      c("Pre-vacc", "7"), use_unit = "percentage")

p3 <- p_mono + p_asc +
  plot_annotation(
    title = "Post-vaccination kinetics (public data)"
    )

p3