Skip to main content

ugbio_mrd

This module includes MRD (Minimal Residual Disease) python scripts and utils for bioinformatics pipelines.

Statistical Detection

MRD detection is based on a Binomial test comparing the observed supporting read count at patient signature loci against a background noise model derived from synthetic (db_control) signatures.

ctDNA VAF estimation

Given:

  • N = total coverage = reads covering signature loci (signature_size × mean_coverage)
  • K = supporting reads passing the SNVQ quality threshold
  • P = SNVQ recall = fraction of true-positive reads passing the threshold (estimated from training data)
  • T = total signal = K / P (supporting reads corrected for recall)

$$\text{ctDNA VAF} = \frac{T}{N} = \frac{K/P}{N}$$

Detection p-value: P(X ≥ K | Binom(N × P, p_err)) where N × P (= corrected_coverage) is the effective Binomial trial count and p_err is the background error rate estimated from synthetic controls via Jeffreys prior.

Personal LOD

The personal Limit of Detection (LOD) is the minimum tumor fraction (TF) at which a sample would be detected with ≥ 95% probability (recall), given this patient's specific assay parameters. It is personal because it depends on the individual signature size, mean coverage, and measured noise rate.

Derivation:

  1. Detection threshold n_th: the smallest read count where the null hypothesis is rejected at FPR = 5%:

$$n_{th} = \min{k : P(X \geq k \mid \mathrm{Binom}(N,, p_{err})) < 0.05}$$

  1. LOD at configurable target recall (default 95%) and FPR (default 5%): the smallest TF such that a true positive sample crosses the threshold at the target recall rate:

$$\mathrm{LOD} = \min{\mathrm{TF} : P(X \geq n_{th} \mid \mathrm{Binom}(N,, p_{err} + \mathrm{TF})) \geq 0.95}$$

where $N_{\text{eff}} = N \times P = \text{signature_size} \times \text{mean_coverage} \times \text{SNVQ_recall}$.

The LOD decreases (improves) with larger signature size, higher coverage, or lower noise rate. It is None when no threshold satisfies the FPR constraint (e.g., signature too small or coverage too low).

Locus Filters

Two optional pre-detection filters remove noisy loci before the Binomial test is run. Both default to enabled (CLI defaults); pass None programmatically to disable.

Noisy-Loci Filter (--thresh-noise-lq-reads)

Removes loci where the number of low-quality reads (failing read_filter_query) meets or exceeds the threshold. Targets loci systematically affected by assay noise rather than true ctDNA signal.

Multi-Read Locus Filter (--thresh-multi-read-pvalue)

Removes loci whose per-locus read count is a significant outlier under a Poisson null model. The primary target is germline or mosaic variants leaking into the matched signature, but the test is applied identically to every (signature_type, signature) pair.

λ estimation: To avoid circular bias (germline outliers would inflate λ and mask themselves), λ is estimated only from background loci — those with ≤ _VAF_ESTIMATE_READ_CAP (= 6) reads:

$$\lambda = \frac{\text{reads at background loci}}{\text{corrected_coverage}} \times \text{mean_coverage}$$

A Jeffreys prior (0.5 / (N+1)) is applied when no background reads are observed.

Bonferroni N — per signature: Each (signature_type, signature) pair is tested independently using its own locus count as the family size N. This is the same logic used by the QC check (see below):

Signature type Bonferroni N
Matched matched signature's own locus count
Synthetic control (db_control) that replicate's own locus count
Cohort control that patient's own signature locus count

Using the matched signature_size as a shared N would under-correct large signatures and over-correct small ones.

A locus is flagged when:

$$p_i \times N < \text{thresh_multi_read_pvalue} \quad \text{and} \quad k_i \geq 2$$

The minimum-reads guard (k ≥ 2) prevents single-read loci from being removed; a single read is indistinguishable from background noise regardless of how small λ is. Flagged loci are removed from all reads of that signature type.

QC Checks

Up to six QC checks are displayed above the Assay Metrics in the report. They do not force an Indeterminate call — they are informational flags.

Check Threshold Rationale
Signature size ≥ 500 loci Too few loci reduce statistical power
Mean coverage ≥ 15× Low coverage inflates noise rate variance
Synthetic controls ≥ 20 Fewer controls make null distribution unreliable
Expected multi-read support distribution (matched) 0 outlier loci (Bonferroni-corrected p ≥ 1%) See below
Expected multi-read support distribution (synthetic controls) 0 outlier loci (Bonferroni-corrected p ≥ 1%) See below — only shown when synthetic controls are present
Expected multi-read support distribution (cohort controls) 0 outlier loci (Bonferroni-corrected p ≥ 1%) See below — only shown when cohort controls are present

Expected Multi-Read Support Distribution

These checks detect loci with a significantly higher read count than expected under the respective Poisson model. A flagged check may indicate germline variants (matched), contamination, or somatic variants leaking into control signatures.

Per-locus Poisson test:

For each locus with k observed supporting reads, the right-tail p-value is:

$$p_i = P(X \geq k_i \mid \mathrm{Poisson}(\lambda))$$

The expected rate λ differs by check:

Check λ per locus Bonferroni N
Matched signature mean_coverage × matched_vaf (measured tumor fraction) matched signature size
Synthetic controls (db_control) mean_coverage × p_err (background noise rate) each synthetic signature's own locus count
Cohort controls mean_coverage × p_err (background noise rate) each cohort signature's own locus count

Bonferroni correction for outliers:

A locus is declared an outlier when its p-value falls below the Bonferroni-corrected threshold:

$$p_i < \frac{\alpha}{N}$$

where α = 1%. The family size $N$ differs by group:

  • Matched signature: $N$ = matched signature size (number of filtered loci passing the signature filter).
  • Synthetic controls (db_control): each synthetic replicate is tested independently; $N$ = that replicate's own locus count. A locus is declared an outlier if the test fires for any single replicate. Synthetic controls are population-panel signatures drawn from unrelated samples, so their locus counts can differ substantially from the matched signature and from each other.
  • Cohort controls: same per-signature treatment; $N$ = each cohort patient's own signature size. Cohort controls are other patients' matched signatures evaluated on this patient's plasma, so their sizes can vary widely.

Using the matched signature_size as a shared $N$ for either control type would be incorrect: under-correcting large signatures and over-correcting small ones. The check is flagged (⚠️) when at least one outlier locus is found across any signature of that type.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ugbio_mrd-1.30.2.tar.gz (77.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ugbio_mrd-1.30.2-py3-none-any.whl (80.1 kB view details)

Uploaded Python 3

File details

Details for the file ugbio_mrd-1.30.2.tar.gz.

File metadata

  • Download URL: ugbio_mrd-1.30.2.tar.gz
  • Upload date:
  • Size: 77.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ugbio_mrd-1.30.2.tar.gz
Algorithm Hash digest
SHA256 3788aa6e6fbaf491ce21d3947fc2269ac6c0ffe20d48e4bb3779bdf28d922418
MD5 960183707153cd78ee4cc0e47691cda3
BLAKE2b-256 3a38f48834cdea5733ab7846d2f53351e81218bbd67b16484ee3dbf8e1e0f12f

See more details on using hashes here.

File details

Details for the file ugbio_mrd-1.30.2-py3-none-any.whl.

File metadata

  • Download URL: ugbio_mrd-1.30.2-py3-none-any.whl
  • Upload date:
  • Size: 80.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for ugbio_mrd-1.30.2-py3-none-any.whl
Algorithm Hash digest
SHA256 789a32082147b2ecd9e34451dbdb13a07c94e93e4e8e85885bb0deef88ce2c37
MD5 56536f9e348c71cad39200dbf7d49860
BLAKE2b-256 1b1ea511f1bcd9deb7ee445f7cae70d07c10b8883d9176448c25891ed3595ae2

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.30.2 This release

2 files

1.30.1

2 files

1.30.0

2 files

1.29.0

2 files

1.28.1

2 files

1.28.0

2 files

1.27.1

2 files

1.27.0

2 files

1.26.0

2 files

1.25.0

2 files

1.24.2

2 files

1.24.1

2 files

1.24.0

2 files

1.23.0

2 files

1.22.2

2 files

1.22.1

2 files

1.22.0

2 files

1.21.4

2 files

1.21.3

2 files

1.21.2

2 files

1.21.1

2 files

1.21.0

2 files

1.20.0

2 files

1.19.0

2 files

1.18.0

2 files

1.17.2

2 files

1.17.1

2 files

1.17.0

2 files

1.16.2

2 files

1.16.1

2 files

1.16.0

2 files

1.15.0

2 files

1.14.0

2 files

1.13.2

2 files

1.13.1

2 files

1.13.0

2 files

1.12.0

2 files

1.11.0

2 files

1.10.2

2 files

1.10.1

2 files

1.10.0

2 files

1.8.0

2 files

1.7.0

2 files

1.6.1

2 files

1.6.0

2 files

1.5.5

2 files

1.5.4

2 files

1.5.3

2 files

1.5.2

2 files

1.5.1

2 files

1.5.0

2 files

1.4.3

2 files

1.4.2

2 files

1.4.1

2 files

1.4.0

2 files

1.3.4

2 files

1.3.3

2 files

1.3.2

2 files

1.3.1

2 files

1.3.0

2 files

1.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page