The serology assay measures antibody titers against a panel of influenza strains, 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 .
Antibody responses were primarily measured by serum hemagglutination inhibition (HAI) against a panel of influenza strains, with HAI titers reported as the reciprocal of the highest serum dilution that inhibits hemagglutination. We also include ELISA (IgG, IgA) and neutralization data when available that can be used as additional model input, although all prediction tasks will predict HAI titers.
Serology data generated for CMI-Flu was combined with other publicly-available data:
The serology dataset consists of 2 tables :
publicData_serology.tsv contains both pre- and
post-vaccination measurements from previous studies. Train your model on
these data, then apply it to 2025LJI_serology.tsv (which
contains pre-vaccination data only) to predict the unseen
post-vaccination values. See the Prediction Task section description
below for details.
| Serology 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. 'Pre-vacc' is the mean of all pre-vaccination timepoints. |
| virus_strain | Influenza strain against which the antibody titer was measured (e.g. H1N1 A/California/7/2009). |
| assay | Serological assay: hai (hemagglutination inhibition), elisa_IgG, elisa_IgA, or neutralization. |
| value | Measured titer. For HAI, the reciprocal of the highest inhibitory serum dilution (two-fold series). |
| virus_in_vaccine | 1 represents a vaccine strain, otherwise 0. |
| study_accession | Study identifier. Links to the investigations.tsv file. |
| subject | subject ID. |
The main serology readout is the HAI titer; the exploration below focuses on HAI. The analyses below provide a high-level overview of the HAI data that you can use to confirm that you have successfully downloaded the data.”Post-vaccination titers are measured at Day 28 (magnitude/breadth) and Day 365 (durability).
train <- read.delim("../datasets/260512/train/publicData_serology.tsv", sep = "\t", stringsAsFactors = FALSE)
challenge <- read.delim("../datasets/260512/train/2025LJI_serology.tsv", sep = "\t", stringsAsFactors = FALSE)
train$set <- "train"
challenge$set <- "challenge"
# focus on HAI; keep the baseline mean and the post-vaccination (Day 28) timepoint
train <- train %>% filter(assay == "hai", timepoint %in% c("Pre-vacc", "28"))
challenge <- challenge %>% filter(assay == "hai", timepoint %in% c("Pre-vacc", "28"))
tp_levels <- c("Pre-vacc", "28")
train$timepoint <- factor(train$timepoint, levels = tp_levels)
challenge$timepoint <- factor(challenge$timepoint, levels = tp_levels)
both <- bind_rows(train, challenge)
Data preview :
head(both)
## participant_id timepoint virus_strain assay value
## 1 2016_UGA.ID_001 28 H1N1 A/Beijing/262/1995 hai 40
## 2 2016_UGA.ID_001 28 H1N1 A/Brazil/11/1978 hai 5
## 3 2016_UGA.ID_001 28 H1N1 A/Brisbane/59/2007 hai 40
## 4 2016_UGA.ID_001 28 H1N1 A/California/7/2009 hai 320
## 5 2016_UGA.ID_001 28 H1N1 A/Chile/1/1983 hai 10
## 6 2016_UGA.ID_001 28 H1N1 A/Denver/1/1957 hai 5
## virus_in_vaccine study_accession subject set
## 1 0 2016_UGA ID_001 train
## 2 0 2016_UGA ID_001 train
## 3 0 2016_UGA ID_001 train
## 4 1 2016_UGA ID_001 train
## 5 0 2016_UGA ID_001 train
## 6 0 2016_UGA ID_001 train
The public HAI data spans many influenza vaccination studies. The number of distinct participants per study is shown below.
participants_per_study <- both %>%
distinct(set, study_accession, participant_id) %>%
count(set, study_accession, name = "n_participants")
p1 <- ggplot(participants_per_study,
aes(x = reorder(study_accession, n_participants), y = n_participants,
fill = set)) +
geom_col(position = position_dodge(width = 0.7), width = 0.6) +
coord_flip() +
scale_fill_manual(values = c(train = "#2C7FB8", challenge = "#E34A33")) +
labs(title = "Participants per study (HAI)",
x = "Study accession", y = "# distinct participants", fill = NULL)
p1
Below is the distribution of HAI titers in the training data at the
Pre-vacc and Day 28 timepoints for the vaccine strains
(virus_in_vaccine == 1). The 15 strains with the largest
number of measurements are shown, ordered by their median titer at both
timepoints. Models of HAI titers typically use log-scaled values, as
shown in the plot.”
vaccine_strains_top <- train %>%
filter(virus_in_vaccine == 1) %>%
count(virus_strain, sort = TRUE) %>%
slice_head(n = 15) %>%
pull(virus_strain)
train_vac <- train %>%
filter(virus_in_vaccine == 1, virus_strain %in% vaccine_strains_top) %>%
mutate(timepoint = factor(timepoint, levels = c("Pre-vacc", "28"),
labels = c("Pre-vacc", "Day 28")))
strain_order <- train_vac %>%
group_by(virus_strain) %>%
summarise(med = median(value), .groups = "drop") %>%
arrange(desc(med)) %>%
pull(virus_strain)
train_plot <- train_vac %>% mutate(virus_strain = factor(virus_strain, levels = strain_order))
p4 <- ggplot(train_plot, aes(x = virus_strain, y = value, fill = timepoint)) +
geom_boxplot(outlier.size = 0.5, outlier.alpha = 0.3, alpha = 0.7,
position = position_dodge(width = 0.75)) +
scale_y_log10(labels = label_number()) +
scale_fill_manual(values = c("Pre-vacc" = "#31A354", "Day 28" = "#756BB1")) +
coord_flip() +
labs(title = "HAI titer by vaccine strain (train set)",
subtitle = "Top 15 vaccine strains by number of measurements",
x = NULL, y = "HAI titer (log scaling)", fill = NULL) +
theme(axis.text.y = element_text(size = 9), legend.position = "top")
p4
The investigations.tsv table specifies the vaccine
season for each study (where that information is available). Specific
details about the strains for each season are available in the
vaccine_strains_per_season
reference file.
Hemagglutinin sequences for all virus strains with serological data are provided here. The distance between these sequences could help models estimate the difference in HAI titers expected when the vaccine strain changes. Sequences were either provided by the study authors or retrieved from public databases.
Three serology tasks are based on HAI titers, for the 40 donors in the 2025LJI challenge dataset. Each task asks you to predict the geometric mean of HAI titers across a defined set of strains at a given timepoint.
H1N1 A/Victoria/4897/2022,
H3N2 A/District Of Columbia/27/2023_MDCK, and
Vic B/Austria/1359417/2021.H1N1 A/Brisbane/2/2018,
H1N1 A/California/7/2009,
H1N1 A/Victoria/2570/2019,
H3N2 A/Darwin/9/2021,
H3N2 A/Massachusetts/18/2022_MDCK,
H3N2 A/Pennsylvania/525/2025,
H3N2 A/Tasmania/503/2020,
Vic B/Colorado/6/2017, and
Vic B/Washington/2/2019).Training coverage of the challenge strains. Not all strains are present in the public training data. For each strain whose HAI must be predicted in Tasks 2.1-2.3, the table below counts the number of distinct training participants with HAI titers measured against this virus at Day 28 and Day 365. Rows highlighted in red indicate that no training data are available for that strain at the corresponding timepoint. Models must predict the titers of these strains using information from antigenically related viruses.
| Challenge-strain training coverage (HAI) | ||||
| Distinct training participants per strain and timepoint | ||||
| Virus strain | In 2025-26 vaccine | Studies | Train participants (D28) | Train participants (D365) |
|---|---|---|---|---|
| H1N1 A/Victoria/4897/2022 | yes | 2 | 238 | 0 |
| H3N2 A/District Of Columbia/27/2023_MDCK | yes | 0 | 0 | 0 |
| Vic B/Austria/1359417/2021 | yes | 5 | 903 | 439 |
| H1N1 A/Brisbane/2/2018 | 6 | 1233 | 668 | |
| H1N1 A/California/7/2009 | 32 | 2628 | 906 | |
| H1N1 A/Victoria/2570/2019 | 5 | 903 | 439 | |
| H3N2 A/Darwin/9/2021 | 5 | 903 | 439 | |
| H3N2 A/Massachusetts/18/2022_MDCK | 0 | 0 | 0 | |
| H3N2 A/Pennsylvania/525/2025 | 0 | 0 | 0 | |
| H3N2 A/Tasmania/503/2020 | 5 | 903 | 439 | |
| Vic B/Colorado/6/2017 | 10 | 1673 | 880 | |
| Vic B/Washington/2/2019 | 6 | 1233 | 668 | |
The distribution of vaccine-strain HAI titers across the relevant timepoints in the training data is shown below (Day 28 is the magnitude/breadth window; Day 365 the durability window). The challenge cohort includes pre-vaccination measurements only.
sero_pub <- read.delim("../datasets/260512/train/publicData_serology.tsv",
stringsAsFactors = FALSE)
tp <- c("Pre-vacc", "28", "365")
vac <- sero_pub %>%
filter(assay == "hai", virus_in_vaccine == 1, timepoint %in% tp, value > 0) %>%
mutate(timepoint = factor(timepoint, levels = tp,
labels = c("Pre-vacc", "Day 28", "Day 365")))
# one value per donor/timepoint: geometric mean of vaccine-strain HAI (the task metric)
donor_geo <- vac %>%
group_by(participant_id, timepoint) %>%
summarise(geo = exp(mean(log(value))), .groups = "drop") %>%
group_by(participant_id) %>%
filter(n_distinct(timepoint) >= 2) %>%
ungroup()
p5c <- ggplot(donor_geo, aes(x = timepoint, y = geo, group = participant_id)) +
geom_line(alpha = 0.2) +
geom_point(alpha = 0.3, 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 = "Per-donor vaccine-strain HAI trajectory (train set)",
subtitle = "Grey = individual donors (geometric mean across vaccine strains); red = median.",
x = "Timepoint", y = "Geometric-mean HAI titer (log scaling)") +
theme(legend.position = "none")
p5c