Supplementary material

Worked example: the calibration computed by hand

Supplement to Genome-anchored calibration of a phenotype measured across laboratories. Every number below is recomputed by the script that generates this page and checked against the output of phenocal.

The main text derives the method; this supplement follows one data set through every step of it, by hand. The example is deliberately small, but it is constructed so that each step has an effect: the bound τ must widen once for lack of pairs and is once stopped by biological drift, and the three laboratories do not agree perfectly, so that the least-squares solution is a genuine compromise between them. Equations of the main text are cited as Eq. (1)–(9); those of this supplement are numbered (S1), (S2), ….

S1The data

Three laboratories (labA, labB, labC) measured a phenotype (an MIC, in mg/L) on 24 isolates of 12 strains. Each strain was measured by two of the laboratories.

Table S1. Values measured by each laboratory (mg/L); a dot means the strain was not measured there.

strainlabAlabBlabC
S112·
S20.51·
S322·
S41·4
S50.5·2
S62·2
S71·1
S81·4
S90.5·4
S10·14
S11·28
S12·12

The file examples/worked_phenotypes.tsv has one row per isolate, S1_A  1  labA and so on; the isolate name is the strain followed by the laboratory.

The genomes are given as cgMLST allelic profiles, 12 loci per isolate, as chewBBACA [1] writes them (examples/worked_profiles.tsv). Each number is the id of the allele found at that locus. Highlighted: INF- alleles (new alleles inferred by chewBBACA), missing calls and alleles that differ from the rest of the strain.

Table S2. Allelic profiles, 12 loci (columns) per isolate.

sample123456789101112
S1_A111INF-111111111
S1_B111111111111
S2_A222222222222
S2_B2222124222222LNF
S3_A333333333333
S3_B33333333313933
S4_A444444444444
S4_C444444444444
S5_A555555555555
S5_C555555555555
S6_A61616666666666
S6_C66666661676666
S7_A77172777777777
S7_CASM7777777771807
S8_A8881838185888888
S8_C88888888888191
S9_A999999999999
S9_C190999991969198999
S10_B101010101010101010101010
S10_C101010101010101010101010
S11_B111111111111111111111111
S11_C1111111111INF-11111111111111
S12_B121212121212121212121212
S12_C121212121212121212121212

S2From allelic profiles to distances

The distance between two isolates is the number of loci where both have a call and the two alleles differ. Each call is read as follows:

Isolates S7_A and S7_C, locus by locus:

Table S3. Isolates S7_A and S7_C compared locus by locus, Eq. (3).

locusS7_AS7_C
17ASMnot compared (missing)
277same
31727different: +1
477same
577same
677same
777same
877same
977same
1077same
117180different: +1
1277same

Eleven loci are compared and two differ, so \(d(\mathrm{S7\_A}, \mathrm{S7\_C}) = 2\). For S1_A and S1_B the only unusual call is INF-1, which is allele 1, so \(d = 0\): the two isolates have identical profiles. Isolates of different strains differ at every locus they share, so their distance is 11 or 12.

This is the distance of Eq. (3), computed in the same way by chewBBACA, cgmlst-dists [2] and ReporTree [3]. phenocal computes it only for the isolates that have a phenotype, and only between isolates of different laboratories, since those are the only pairs it uses.

S3Cross-cohort pairs and log-ratios

Values are compared on the \(\log_{10}\) scale, where a constant factor between laboratories becomes a constant difference:

$$ y = \log_{10}(\text{value}), \qquad y_{ic} = \mu_i + \delta_c + \varepsilon_{ic}. \tag{1} $$

\(\mu_i\) is the true value of strain \(i\), \(\delta_c\) the offset of laboratory \(c\) (what we want), \(\varepsilon\) the measurement noise. For a pair of isolates from laboratories \(a\) and \(b\) the log-ratio is

$$ s = y_a - y_b = \log_{10}\frac{\text{value}_a}{\text{value}_b}. \tag{2} $$

If the two isolates are the same strain, \(\mu\) cancels and \(s\) measures \(\delta_a - \delta_b\) plus noise. For example S1_A = 1 and S1_B = 2: \(s = \log_{10}(1/2) = -0.301\), so on this strain labB reads twice as high as labA.

The 9 isolates of labA, 6 of labB and 9 of labC give \(9\times 6 + 9\times 9 + 6\times 9 = 189\) cross-cohort pairs, grouped by the two laboratories they connect. Pairs within one laboratory say nothing about the offsets and are not used.

S4One edge per pair of laboratories: τ, Δ, SE

For each pair of laboratories phenocal decides which pairs to trust, summarises them in a single value \(\Delta\), and attaches a standard error. Three quantities are fixed by the options of the run (here the defaults, with --min-support 3):

The rule for τ. Sort the pairs by distance. Let \(m_0\) be the median of \(s\) over the closest pairs (distance \(d_{\min}\)). Try the distinct distances in increasing order; τ is the first one for which

$$ n(\tau) = \#\{d \le \tau\} \ge 3 \qquad\text{and}\qquad \frac{|\operatorname{median}\{s : d \le \tau\} - m_0|}{\bar\sigma} \le \kappa. \tag{4} $$

The first condition asks for enough pairs; the second stops τ when more distant pairs start to pull the median away, the signature of biology rather than protocol. The edge value and its uncertainty are then

$$ \Delta = \operatorname{median}\{s : d \le \tau\}, \qquad \mathrm{SE} = \sqrt{\frac{\sigma_a^2 + \sigma_b^2 + \lambda\,\tau}{n}}, \qquad w = \frac{1}{\mathrm{SE}^2}. \tag{5, 6} $$

A median of an even number of values is the mean of the two middle ones.

labA – labB

One strain (S1) is identical in the two laboratories; two more (S2, S3) differ by one allele. At τ = 0 there is a single pair, fewer than 3, so τ widens to 1. The median does not move, so τ = 1 is accepted. Note that S3 reads the same in both laboratories: one noisy pair does not move a median of three.

Table S4. Cross-cohort pairs of labA and labB up to distance 3, with their log-ratio s; highlighted rows enter the median.

isolate in labAisolate in labBdvalue labAvalue labBsused
S1_AS1_B012−0.301yes
S2_AS2_B10.51−0.301yes
S3_AS3_B1220.000yes

The other 51 pairs of these two laboratories are different strains, at distance 11 or more.

Table S5. Candidate bounds τ for labA–labB, Eq. (4): the first row satisfying both conditions is chosen.

τpairs with d ≤ τmedian of sdrift / σ̄≥ 3 pairsdrift ≤ κ
01−0.3010.00noyestoo few pairs
13−0.3010.00yesyeschosen
$$ \begin{aligned} \Delta_{\mathrm{AB}} &= \operatorname{median}\{-0.301, -0.301, 0.000\} = -0.301 \end{aligned} $$

a factor of \(10^{-0.301} = 0.50\), and

$$ \mathrm{SE} = \sqrt{\frac{0.04531 + 0.04531 + 0.09062\times 1}{3}} = 0.2458, $$ $$ w = \frac{1}{\mathrm{SE}^2} = 16.55. $$

labA – labC

The interesting edge. S4 and S5 are identical in the two laboratories and both say that labC reads four times higher (\(s = -0.602\)). That is two pairs, not enough. At distance 2 come S6 and S7, which read the same in both laboratories: they are not quite the same strain, and their biology differs. With them the median moves by a whole dilution (0.301), 1.41 times σ̄, more than κ = 1 allows, so τ = 2 is rejected. At τ = 3 two more pairs bring the median back, and τ = 3 is accepted with six pairs.

Table S6. Cross-cohort pairs of labA and labC up to distance 3, with their log-ratio s; highlighted rows enter the median.

isolate in labAisolate in labCdvalue labAvalue labCsused
S4_AS4_C014−0.602yes
S5_AS5_C00.52−0.602yes
S6_AS6_C2220.000yes
S7_AS7_C2110.000yes
S8_AS8_C314−0.602yes
S9_AS9_C30.54−0.903yes

The other 75 pairs of these two laboratories are different strains, at distance 11 or more.

Table S7. Candidate bounds τ for labA–labC, Eq. (4): the first row satisfying both conditions is chosen.

τpairs with d ≤ τmedian of sdrift / σ̄≥ 3 pairsdrift ≤ κ
02−0.6020.00noyestoo few pairs
24−0.3011.41yesnomedian drifts
36−0.6020.00yesyeschosen
$$ \begin{aligned} \Delta_{\mathrm{AC}} &= \operatorname{median}\{-0.903, -0.602, -0.602, -0.602, 0.000, 0.000\} \\ &= \frac{(-0.602) + (-0.602)}{2} = -0.602 \end{aligned} $$

a factor of \(10^{-0.602} = 0.25\), and

$$ \mathrm{SE} = \sqrt{\frac{0.04531 + 0.04531 + 0.09062\times 3}{6}} = 0.2458, $$ $$ w = \frac{1}{\mathrm{SE}^2} = 16.55. $$
Log-ratios of the labA–labC pairs against genomic distance, with the candidate bounds τ0.000−0.301−0.602−0.90301234genomic distance d (alleles)log-ratio sτ = 0: n < 3τ = 2: driftsτ = 3: chosen
Figure S1. The labA–labC pairs up to distance 4. Dashes mark the median of the pairs with d ≤ τ for each candidate τ; τ = 2 is rejected because its median (−0.301) drifts from the median of the identical pairs (−0.602). The shaded band holds the pairs used.

labB – labC

Three strains (S10, S11, S12) are identical in the two laboratories, so τ = 0. Two say that labC reads four times higher than labB, one says twice as high; the median is \(\log_{10}(1/4)\). With τ = 0 and three pairs this is the most precise edge, with twice the weight of the others.

Table S8. Cross-cohort pairs of labB and labC up to distance 3, with their log-ratio s; highlighted rows enter the median.

isolate in labBisolate in labCdvalue labBvalue labCsused
S10_BS10_C014−0.602yes
S11_BS11_C028−0.602yes
S12_BS12_C012−0.301yes

The other 51 pairs of these two laboratories are different strains, at distance 10 or more.

Table S9. Candidate bounds τ for labB–labC, Eq. (4): the first row satisfying both conditions is chosen.

τpairs with d ≤ τmedian of sdrift / σ̄≥ 3 pairsdrift ≤ κ
03−0.6020.00yesyeschosen
$$ \begin{aligned} \Delta_{\mathrm{BC}} &= \operatorname{median}\{-0.602, -0.602, -0.301\} = -0.602 \end{aligned} $$

a factor of \(10^{-0.602} = 0.25\), and

$$ \mathrm{SE} = \sqrt{\frac{0.04531 + 0.04531 + 0.09062\times 0}{3}} = 0.1738, $$ $$ w = \frac{1}{\mathrm{SE}^2} = 33.11. $$

S5Putting the edges together: weighted least squares

The comparison graph with the three edge values and weightsΔ = −0.301 (0.50×)τ = 1, n = 3, w = 16.6Δ = −0.602 (0.25×)τ = 3, n = 6, w = 16.6Δ = −0.602 (0.25×)τ = 0, n = 3, w = 33.1labAlabBlabC
Figure S2. The comparison graph: one node per laboratory, one edge per pair of laboratories that share strains. Line width is proportional to the weight w.

Each edge is an equation in the offsets, \(\delta_a - \delta_b \approx \Delta_{ab}\). Fixing the anchor \(\delta_A = 0\) (any laboratory can be the anchor; it only sets the reference scale):

$$ \begin{aligned} 0 - \delta_B &\approx -0.301 && (w = 16.55)\\ 0 - \delta_C &\approx -0.602 && (w = 16.55)\\ \delta_B - \delta_C &\approx -0.602 && (w = 33.11) \end{aligned} $$

Three equations, two unknowns, and they disagree: going round the triangle, the edge values should add up to zero, but

$$ \Delta_{AB} + \Delta_{BC} - \Delta_{AC} = -0.301 + (-0.602) - (-0.602) = -0.301. \tag{S1} $$

The labA–labB edge says labB reads twice as high as labA; the other two together say it reads the same. Weighted least squares finds the offsets that minimise the weighted sum of squared disagreements:

$$ S(\delta_B, \delta_C) = w_{AB}\big(\Delta_{AB} + \delta_B\big)^2 + w_{AC}\big(\Delta_{AC} + \delta_C\big)^2 + w_{BC}\big(\Delta_{BC} - \delta_B + \delta_C\big)^2. \tag{S2} $$

Setting both derivatives to zero gives the normal equations \((A^{\top} W A)\,\delta = A^{\top} W \Delta\):

$$ \begin{bmatrix} w_{AB} + w_{BC} & -w_{BC} \\ -w_{BC} & w_{AC} + w_{BC} \end{bmatrix} \begin{bmatrix} \delta_B \\ \delta_C \end{bmatrix} = \begin{bmatrix} -w_{AB}\Delta_{AB} + w_{BC}\Delta_{BC} \\ -w_{AC}\Delta_{AC} - w_{BC}\Delta_{BC} \end{bmatrix} $$ $$ \begin{bmatrix} 49.66 & -33.11 \\ -33.11 & 49.66 \end{bmatrix} \begin{bmatrix} \delta_B \\ \delta_C \end{bmatrix} = \begin{bmatrix} -14.949 \\ 29.897 \end{bmatrix}. \tag{S3} $$

The matrix on the left is the weighted graph Laplacian with the anchor removed. Its determinant is \(49.66^2 - 33.11^2 = 1370.0\), and inverting it,

$$ (A^{\top} W A)^{-1} = \begin{bmatrix} 0.03625 & 0.02417 \\ 0.02417 & 0.03625 \end{bmatrix}, \qquad \begin{bmatrix} \hat\delta_B \\ \hat\delta_C \end{bmatrix} = \begin{bmatrix} 0.1806 \\ 0.7225 \end{bmatrix}. $$

As factors: labB reads \(10^{0.1806} = 1.52\times\) and labC \(10^{0.7225} = 5.28\times\) higher than labA. (Exactly, \(\hat\delta_B = 0.6\log_{10}2\) and \(\hat\delta_C = 2.4\log_{10}2\).) labB ends up between the 2× of its direct edge and the 1× implied by the other two, closer to the latter because the labB–labC edge weighs twice as much.

What is left over on each edge:

Table S10. Edge values, fitted contrasts and residuals of the least-squares solution.

edgeΔfitted \(\hat\delta_a - \hat\delta_b\)residualw
labA–labB−0.301−0.181−0.12016.55
labA–labC−0.602−0.7220.12016.55
labB–labC−0.602−0.542−0.06033.11
$$ \text{weighted RMSE} = \sqrt{\frac{\sum w\,r^2}{\sum w}} = 0.0952\ \log_{10} = 0.32\ \text{dilutions}. \tag{S4} $$

A residual of a third of a dilution is large for real data; here it was put in on purpose. phenocal writes it at the end of every run, as a check on how well the laboratories agree.

S6Uncertainty

The edge values are uncertain, with standard errors SE. phenocal propagates this by a parametric bootstrap [4]: it draws every edge from \(\mathcal N(\Delta, \mathrm{SE}^2)\), solves the least squares again, repeats this 10,000 times, and reports the median of the offsets and their 2.5th–97.5th percentiles.

Because the solution is a linear function of the edge values with weights \(1/\mathrm{SE}^2\), the same answer can be written down exactly (Proposition 2 of the main text): the offsets are normal, with covariance \((A^{\top} W A)^{-1}\), the matrix inverted above. So

$$ \mathrm{sd}(\hat\delta_B) = \sqrt{0.03625} = 0.1904, \qquad \mathrm{sd}(\hat\delta_C) = \sqrt{0.03625} = 0.1904, $$ $$ 95\%:\ \hat\delta \pm 1.96\,\mathrm{sd} \;\Rightarrow\; \text{labB}: [0.64,\ 3.58], \quad \text{labC}: [2.24,\ 12.46]. $$

phenocal's bootstrap gives:

Table S11. Offsets reported by phenocal (offsets.tsv): bootstrap median and 95% interval, 10,000 draws.

cohortoffset (log10)fold95% interval
labA0.00001.00×[1.00, 1.00]
labB0.17641.50×[0.64, 3.56]
labC0.72305.28×[2.22, 12.07]

The differences from the exact values are Monte Carlo error: 10,000 random draws give each number with some noise, which shrinks with more draws (--bootstrap). With the default seed the upper bound for labC is 12.07 instead of 12.46, a rather unlucky draw: over the seeds 1 to 30 the same bound averages 12.45, with a standard deviation of 0.0042 in \(\log_{10}\) (1%). The intervals are wide because each edge rests on three to six pairs; with real collections, edges have tens or hundreds of pairs.

Why a bootstrap at all, if the exact answer is available? Because phenocal also offers a robust bootstrap with Student-\(t\) draws (--robust) and one that resamples clonal clusters (--clusters), which have no closed form.

S7Harmonised values and probabilistic labels

Each value is brought to the scale of the anchor by removing its laboratory's offset, using the reported offsets:

$$ \text{harmonised} = \frac{\text{value}}{10^{\hat\delta_c}}, \qquad\text{e.g. } \mathrm{S11\_B}: \frac{2}{10^{0.1764}} = 1.332. $$

Whether an isolate is above a threshold \(t\) (say 1.25 mg/L) depends on the offset, which is uncertain. phenocal treats the offset of laboratory \(c\) as normal, centred on \(\hat\delta_c\) with standard deviation \(s_c\) read off its 95% interval, \(s_c = (\log_{10}\mathrm{hi} - \log_{10}\mathrm{lo})/(2 \times 1.96)\), and gives the probability

$$ P(\text{harmonised} \ge t) = \Phi\!\left(\frac{y - \hat\delta_c - \log_{10} t}{s_c}\right). $$

For S11_B and \(t = 1.25\): \(s_{\mathrm{B}} = (0.5514 - (-0.1949))/3.92 = 0.1904\),

$$ P = \Phi\!\left(\frac{0.3010 - 0.1764 - 0.0969}{0.1904}\right) = \Phi(0.145) = 0.558. $$

phenocal estimates the same probability by drawing the offset 10,000 times and gets 0.562. The isolate's harmonised value, 1.33, is just above 1.25, but the offset of labB is so uncertain that the label is close to a coin toss. That is the point of probabilistic labels: they separate isolates whose class is clear from those whose class depends on the calibration.

All isolates (labels.tsv; isolates of the anchor have a certain offset, so their probabilities are 0 or 1):

Table S12. Harmonised values and probabilities P(harmonised ≥ t), Eq. (9), for the default thresholds (labels.tsv).

isolatecohortrawharmonisedP(≥ 0.75)P(≥ 1)P(≥ 1.25)P(≥ 1.5)P(≥ 2)
S1_AlabA11.0001.001.000.000.000.00
S2_AlabA0.50.5000.000.000.000.000.00
S3_AlabA22.0001.001.001.001.001.00
S4_AlabA11.0001.001.000.000.000.00
S5_AlabA0.50.5000.000.000.000.000.00
S6_AlabA22.0001.001.001.001.001.00
S7_AlabA11.0001.001.000.000.000.00
S8_AlabA11.0001.001.000.000.000.00
S9_AlabA0.50.5000.000.000.000.000.00
S1_BlabB21.3320.910.750.560.400.18
S2_BlabB10.6660.400.180.080.030.01
S3_BlabB21.3320.900.750.560.390.18
S10_BlabB10.6660.400.180.080.030.01
S11_BlabB21.3320.910.750.560.400.17
S12_BlabB10.6660.400.180.070.030.01
S4_ClabC40.7570.510.260.120.060.01
S5_ClabC20.3790.060.010.000.000.00
S6_ClabC20.3790.060.010.000.000.00
S7_ClabC10.1890.000.000.000.000.00
S8_ClabC40.7570.500.260.120.060.01
S9_ClabC40.7570.520.260.120.060.01
S10_ClabC40.7570.510.260.130.060.01
S11_ClabC81.5140.950.830.670.510.26
S12_ClabC20.3790.060.010.000.000.00

S8Run it yourself

The two files are in the repository and in the Docker image:

phenocal --phenotypes examples/worked_phenotypes.tsv \
         --profiles examples/worked_profiles.tsv \
         --anchor labA --min-support 3 --out worked \
         --trace worked.md --dashboard worked.html

worked.md repeats these calculations for your own data, with the real numbers of the run; worked.html shows them interactively.

References

  1. Silva M, Machado MP, Silva DN, Rossi M, Moran-Gilad J, Santos S, Ramirez M, Carriço JA. chewBBACA: a complete suite for gene-by-gene schema creation and strain identification. Microbial Genomics. 2018;4(3):e000166. doi:10.1099/mgen.0.000166.
  2. Seemann T. cgmlst-dists: calculate distance matrix from cgMLST allele call tables. Software. github.com/tseemann/cgmlst-dists.
  3. Mixão V, Pinto M, Sobral D, Di Pasquale A, Gomes J, Borges V. ReporTree: a surveillance-oriented tool to strengthen the linkage between pathogen genetic clusters and epidemiological data. Genome Medicine. 2023;15(1):43. doi:10.1186/s13073-023-01196-1.
  4. Efron B. Bootstrap methods: another look at the jackknife. The Annals of Statistics. 1979;7(1):1–26. doi:10.1214/aos/1176344552.