Skip to content

v4.1.0 — corpora that describe a real compartment

Choose a tag to compare

@mikessh mikessh released this 27 Sep 22:41
· 248 commits to master since this release

Four signature corpora now ship, up from two. The two new ones — synthetic-blood and
synthetic-tissue — draw every repertoire as a naive/memory mixture across three quantile
ladders measured per locus on the compartment they are named after, so a corpus spans the depth and
clone-size range the samples it will be applied to actually have.

pip install -U vdjtools
vdjtools signature --corpus synthetic-blood samples/*.tsv.gz -o vsig.tsv

No build step and no cohort sample: every receptor comes from the bundled recombination models, and a
rebuild is bit-identical at any core count.

Why

A corpus fixes the winsorization bounds, the centre, the scale and the rotation, and all four are
estimated from the repertoires it was drawn across. None extrapolates. The 4.0 corpora drew depth over
2.4x (TRD) to 11.0x (IGH), from 1,168 deep blood samples. One streaming pass over the whole
reference store — every sample with at least 100 reads in the locus — puts the real axis an order of
magnitude wider:

cohort locus samples richness p05 median p95 spread
blood TRB 34,365 74 457 3,162 43x
blood IGH 33,245 84 697 6,200 74x
tissue TRB 22,298 30 158 2,977 99x
tissue IGH 47,031 40 602 10,352 259x

Three ladders per locus, five quantiles each: clonotype richness; reads per expanded clone,
(reads - singletons) / (richness - singletons); and the singleton fraction f1, which in a
reasonably deep library is what stands in for the naive compartment and which falls with donor age
(Britanova et al. 2014,
2016). V and J usage need no ladder — the rearrangement
model produces it.

The mixture is constructed, not sampled: round(f1 x N) clonotypes get one read and the rest
share the remainder with Zipf rank-abundance frequencies on a floor of 2, so each sample hits its
drawn richness, read count and singleton fraction exactly.

Two things that had to be measured rather than assumed

Reads per expanded clone, not per clonotype. A repertoire with f1 singletons whose other
clones all carry at least 2 reads has at least 2 - f1 reads per clonotype. That is an identity, and
a pair drawn from those two marginals lands in the region it forbids about half the time on blood
TRB; drawing the read count itself does so for 21.1% of 20,000 draws. Reads per expanded clone is
>= 2 whatever f1 is, so the trio has no forbidden region and nothing is clipped back out of one.

Five quantiles, not two. The read count is a product of all three drawn quantities, so it is the
sharpest check: against blood TRB's measured median of 763 reads, a 40,000-draw simulation gives
762 at five quantiles and 846 at three; blood IGH's 1,298 comes out 1,276 against 1,490.

The three are drawn through a Gaussian copula at the cohort's measured rank correlations — for the
rotation rather than for any marginal, since a corpus's rotation is its covariance structure and
f1 against expansion size is -0.503 on blood TRB and -0.846 on blood IGK.

What it reproduces, and what it does not

500 drawn samples per cohort, against the cohort's own percentiles: f1 within 0.03 everywhere,
richness within 10% on all 14 (cohort, locus) pairs, read count within 15% on 10 of 14. The
exceptions are the four shallow tissue TR loci, where the read count runs 20-52% high (tissue TRB 467
against 308) — a product of three heavy-tailed factors has a median above the product of their
medians unless the joint tails match exactly, and no marginals-plus-copula draw does that. The drawn
p95 of the read count is also ~29% below the cohort's (blood TRB 3,755 against 5,274), so the
fitted depth ceiling is that much tighter. Full table in the
docs.

On four TCGA tumour-tissue repertoires the corpora rank as their names say — median |z| over the PC
columns 0.246 through synthetic-tissue, 0.318 through synthetic-blood, 0.351 through memory,
0.674 through naive.

Also

  • --depth-spread on both corpus commands, and --size auto as the default. On a synthetic-*
    corpus --depth-spread is refused rather than silently ignored, because that band is measured;
    --size rescales the whole ladder about its median instead.
  • synthesize takes a corpus name, not a regime. corpus_plan resolves a name for both halves and
    corpus_meta writes both manifests, so a vsig_<name>/rsig_<name> pair cannot disagree about the
    cohort, the ladders, the correlations or the germline it was drawn from.
  • The manifest gains cohort, richness_band, expanded_count_band, singleton_frac_band,
    rank_corr, depth_spread and depth_spread_requested; the rsig half records depth for the
    first time.
  • naive and memory are unchanged and still rebuild bit-for-bit, checked byte by byte against
    4.0.1.

The geometry half is mirpy-lib 4.1.0; run both against the
same corpus name and join on sample_id (947 + 904 -> 1,852 columns).

Full changelog: https://github.com/antigenomics/vdjtools/blob/master/CHANGELOG.md