The different HLA-COclusters in the CMV ECOcluster range widely in diagnostic value. Let’s see if we can figure out why.
Published
July 25, 2026
HLA-COclusters are subclusters of ECOclusters that are each associated with a single HLA-allele. ECOclusters are built by clustering the HLA-COclusters, by breadth correlation in repertoires, across HLA alleles.
In the manuscript, we evaluated breadth (proportion of repertoire TCRs that are members) on each HLA-COcluster as a predictor of CMV status, in holdout repertoires. The publicly available CMV ECOcluster has AUROCs associated with each HLA-COcluster, describing their ability to classify holdout donors (120 donors from Emerson Cohort 2) as CMV+ or CMV-.
Let’s see if we can figure out why some HLA-COclusters are “better” than others.
1 Setup
Basic imports, load the 2026 CMV ECOcluster.
Code
# basic importsimport pandas as pdfrom matplotlib import pyplot as pltimport seaborn as sns# make plots look nicesns.set_context("talk")# statisticsfrom scipy.stats import mannwhitneyu# load the CMV ECOcluster, downloading if not already downloaded.from cmvividly.data.access import load_cmv_ecocluster_2026pdf_cmv_ecocluster_2026 = load_cmv_ecocluster_2026()pdf_cmv_ecocluster_2026.columns# make an HLA-COcluster-level dataframefrom cmvividly.data.manipulation import extract_hlacoclusters_pdfpdf_hla_coclusters = extract_hlacoclusters_pdf(pdf_cmv_ecocluster_2026)
2 HLA-aware AUROC
HLA-COclusters are only relevant for people with their associated HLA alleles. Let’s look at the AUROCs defined on those people first. Those are only defined for about half the HLA-COclusters (the rest didn’t have enough HLA-matched samples in holdout):
That’s quite a range. They’re mostly very good, but there are a few lousy ones.
Do they differ by HLA class?
Code
f, ax = plt.subplots()sns.boxplot(data=pdf_hla_coclusters, y="hla_cocluster_auroc_hlaaware", x="hla_class", ax=ax)f.set_size_inches(6, 4)ax.set_title("HLA-COcluster HLA-aware AUROCs by HLA class")ax.set_ylabel("HLA-aware AUROC")pdf_hla_coclusters_with_auroc = pdf_hla_coclusters[pdf_hla_coclusters.hla_cocluster_auroc_hlaaware.notnull()]mwu_p = mannwhitneyu(pdf_hla_coclusters_with_auroc[pdf_hla_coclusters_with_auroc.hla_class =="ci"].hla_cocluster_auroc_hlaaware, pdf_hla_coclusters_with_auroc[pdf_hla_coclusters_with_auroc.hla_class =="cii"].hla_cocluster_auroc_hlaaware, alternative="two-sided").pvaluemean_higher_class ="Class II"if pdf_hla_coclusters_with_auroc[pdf_hla_coclusters_with_auroc.hla_class =="cii"].hla_cocluster_auroc_hlaaware.mean() > pdf_hla_coclusters[pdf_hla_coclusters.hla_class =="ci"].hla_cocluster_auroc_hlaaware.mean() else"Class I"print(f"2-sided Mann-Whitney U test p-value: {mwu_p:.3e}. Mean HLA-aware AUROC is higher for {mean_higher_class}.")
2-sided Mann-Whitney U test p-value: 7.949e-02. Mean HLA-aware AUROC is higher for Class II.
Why, yes, they do! The AUROCs are (barely) significantly higher for HLA-COclusters associated with Class II alleles than for Class I. Let’s look at the worst ones:
HLA-COclusters vary widely in cardinality. Does the number of TCRs in an HLA-COcluster correlate with its AUROC?
Code
f, ax = plt.subplots()sns.scatterplot(x="n_tcrs", y="hla_cocluster_auroc_hlaaware", data=pdf_hla_coclusters, hue="hla_class")ax.set_title("HLA-COcluster HLA-aware AUROC vs.\nTCRs in the HLA-COcluster")ax.set_ylabel("HLA-aware AUROC")ax.set_xlabel("TCRs in HLA-COcluster")
It sure does! The biggest HLA-Coclusters (all Class II) all look stellar. The worst ones are mostly small (except one with ~1000 TCRs), though most small ones are also very good. And, as we saw in the Basics post, Class II-associated HLA-COclusters tend to be larger than Class I. So, it seems plausible that some of the CMV HLA-COclusters don’t perform well as diagnostics because they have too few TCRs.
Perhaps some of those small HLA-COclusters are of low quality, i.e., not very CMV-specific. They could also be high-quality but suffer from a lot of dropout, i.e., in many CMV+ donors none of the small number of TCRs happens to be in the repertoire, by chance. Bigger HLA-COclusters means more shots on goal. That’s why the general-purpose ECOcluster breadth metric we used in the manuscript weights HLA-COclusters by cardinality.
2.0.2 HLA-unaware AUROC
We also calculated an HLA-unaware AUROC for every HLA-COcluster. We expect those to be full of noise, because they include many people without the associated HLA allele. But are some of them good, anyway?
Not really, no! And there are some high-ish HLA-unaware AUROCs, but they’re all on HLA-COclusters that had enough samples in holdout to evaluate HLA-aware. That makes sense! If there aren’t enough samples in holdout to evaluate HLA-aware, we’re looking at a rare allele whose HLA-unaware AUROC will be swamped by noise.
Source Code
---title: "Individual HLA-COclusters as CMV biomarkers"date: "2026-07-25"description: "The different HLA-COclusters in the CMV ECOcluster range widely in diagnostic value. Let's see if we can figure out why."format: html: toc: truenumber-sections: true---HLA-COclusters are subclusters of ECOclusters that are each associatedwith a single HLA-allele. ECOclusters are built by clustering theHLA-COclusters, by breadth correlation in repertoires, across HLA alleles.In the manuscript, we evaluated breadth (proportion of repertoire TCRs that are members)on each HLA-COcluster as a predictor of CMV status, in holdout repertoires. Thepublicly available CMV ECOcluster has AUROCs associated with each HLA-COcluster,describing their ability to classify holdout donors (120 donors from [Emerson Cohort 2](https://clients.adaptivebiotech.com/pub/Emerson-2017-NatGen))as CMV+ or CMV-.Let's see if we can figure out why some HLA-COclusters are "better" than others.## SetupBasic imports, load the 2026 CMV ECOcluster.```{python}# basic importsimport pandas as pdfrom matplotlib import pyplot as pltimport seaborn as sns# make plots look nicesns.set_context("talk")# statisticsfrom scipy.stats import mannwhitneyu# load the CMV ECOcluster, downloading if not already downloaded.from cmvividly.data.access import load_cmv_ecocluster_2026pdf_cmv_ecocluster_2026 = load_cmv_ecocluster_2026()pdf_cmv_ecocluster_2026.columns# make an HLA-COcluster-level dataframefrom cmvividly.data.manipulation import extract_hlacoclusters_pdfpdf_hla_coclusters = extract_hlacoclusters_pdf(pdf_cmv_ecocluster_2026)```## HLA-aware AUROCHLA-COclusters are only relevant for people with their associated HLA alleles.Let's look at the AUROCs defined on those people first.Those are only defined for about half the HLA-COclusters (the rest didn't have enough HLA-matched samples in holdout):```{python}pdf_hla_coclusters["has_aware_auroc"] = pdf_hla_coclusters.hla_cocluster_auroc_hlaaware.notnull()print(pdf_hla_coclusters.has_aware_auroc.value_counts())```Let's see the distribution of AUROCs that are present:```{python}f, ax = plt.subplots()sns.histplot(pdf_hla_coclusters.hla_cocluster_auroc_hlaaware, ax=ax)f.set_size_inches(6, 2)ax.set_title("HLA-COcluster HLA-aware AUROCs")```That's quite a range. They're mostly very good, but there are a few lousy ones.Do they differ by HLA class?```{python}f, ax = plt.subplots()sns.boxplot(data=pdf_hla_coclusters, y="hla_cocluster_auroc_hlaaware", x="hla_class", ax=ax)f.set_size_inches(6, 4)ax.set_title("HLA-COcluster HLA-aware AUROCs by HLA class")ax.set_ylabel("HLA-aware AUROC")pdf_hla_coclusters_with_auroc = pdf_hla_coclusters[pdf_hla_coclusters.hla_cocluster_auroc_hlaaware.notnull()]mwu_p = mannwhitneyu(pdf_hla_coclusters_with_auroc[pdf_hla_coclusters_with_auroc.hla_class =="ci"].hla_cocluster_auroc_hlaaware, pdf_hla_coclusters_with_auroc[pdf_hla_coclusters_with_auroc.hla_class =="cii"].hla_cocluster_auroc_hlaaware, alternative="two-sided").pvaluemean_higher_class ="Class II"if pdf_hla_coclusters_with_auroc[pdf_hla_coclusters_with_auroc.hla_class =="cii"].hla_cocluster_auroc_hlaaware.mean() > pdf_hla_coclusters[pdf_hla_coclusters.hla_class =="ci"].hla_cocluster_auroc_hlaaware.mean() else"Class I"print(f"2-sided Mann-Whitney U test p-value: {mwu_p:.3e}. Mean HLA-aware AUROC is higher for {mean_higher_class}.")```Why, yes, they do! The AUROCs are (barely) significantly higher for HLA-COclustersassociated with Class II alleles than for Class I. Let's look at the worst ones:```{python}pdf_hla_coclusters.sort_values("hla_cocluster_auroc_hlaaware")[["hla_cocluster", "hla_cocluster_auroc_hlaaware", ]].head(5)```#### Relationship with HLA-COcluster TCR countHLA-COclusters vary widely in cardinality. Does the number of TCRs in an HLA-COcluster correlatewith its AUROC?```{python}f, ax = plt.subplots()sns.scatterplot(x="n_tcrs", y="hla_cocluster_auroc_hlaaware", data=pdf_hla_coclusters, hue="hla_class")ax.set_title("HLA-COcluster HLA-aware AUROC vs.\nTCRs in the HLA-COcluster")ax.set_ylabel("HLA-aware AUROC")ax.set_xlabel("TCRs in HLA-COcluster")```It sure does! The biggest HLA-Coclusters (all Class II) all look stellar. Theworst ones are mostly small (except one with ~1000 TCRs), though most small onesare also very good. And, as we saw in the [Basics post](../01-Basics/index.html),Class II-associated HLA-COclusters tend to be larger than Class I. So, it seems plausible that some ofthe CMV HLA-COclusters don't perform well as diagnostics *because*they have too few TCRs.Perhaps some of those small HLA-COclusters are of low quality, i.e., not very CMV-specific. They could also be high-quality but suffer from a lot of dropout, i.e., in many CMV+donors none of the small number of TCRs happens to be in the repertoire, by chance. Bigger HLA-COclusters means more shots on goal. That's why the general-purposeECOcluster breadth metric we used in the manuscript weights HLA-COclusters by cardinality.#### HLA-unaware AUROCWe also calculated an HLA-unaware AUROC for every HLA-COcluster. We expect those to befull of noise, because they include many people without the associated HLA allele.But are some of them good, anyway?```{python}f, ax = plt.subplots()sns.boxplot(x="has_aware_auroc", y="hla_cocluster_auroc_hlaunaware", data=pdf_hla_coclusters, ax=ax)ax.set_title("HLA-COcluster HLA-unaware AUROCs\nby presence of HLA-aware AUROC")ax.set_ylabel("HLA-unaware AUROC")```Not really, no! And there are some high-ish HLA-unaware AUROCs, but they're allon HLA-COclusters that had enough samples in holdout to evaluate HLA-aware. That makes sense!If there aren't enough samples in holdout to evaluate HLA-aware, we're looking ata rare allele whose HLA-unaware AUROC will be swamped by noise.