Skip to content

How QC works

snipe qc measures the same things an alignment-based QC tool such as qualimap reports — coverage, depth (overall and per chromosome), error and mutation rates, contamination, and sex-chromosome signals — but it computes them from a k-mer sketch instead of an alignment, so it needs no aligner and no BAM. Most of those metrics flow from one structural idea — the edgemer, a k-mer pair — and from one question asked of every observation: did the K2 extension hold? This page explains how that question turns reads into QC numbers. For the exhaustive per-metric reference — names, formulas, and visibility — see the metric catalog →.

Every edgemer couples a base k-mer (K1, a canonical 51-mer by default) with a slightly longer extension (K2, k1 + 2 bases — one added on each side of K1, which sits in the centre). When snipe compares a sample against a reference, it doesn’t just ask whether K1 is present — it asks whether K1’s expected K2 extension is present too.

That extension carries the signal. A K1 that matches the reference while its K2 extension mismatches marks the exact base where the sample diverges from the reference — a change sitting in the extension window.

ACGTGACCTGAGTCAG◀ extext ▶K1 · base k-mer (centre)K2 · extends K1 by (k2−k1)/2 on each sideedgemer = (K1, K2) pair
K2 flanks K1 on both sides; when those extension bases differ from the reference's expected K2, that marks a base change localized to the extension.

Knowing where a change is doesn’t tell you what kind of change it is. snipe answers that with abundance — how many times the changed edgemer was seen.

  • A change seen exactly once is a singleton: most often a random miscalled base, i.e. a sequencing error.
  • A change seen repeatedly is a polyton: a reproducible difference, i.e. a true mutation.

Split the unmatched extensions this way and you get snipe’s two per-base rates directly:

  • sequencing_error_rate is built from the singleton unmatched extensions.
  • mutation_rate is built from the multi-occurrence (polyton) unmatched extensions.

Both are divided through by the total edgemers examined (matched plus unmatched extensions) and by the extension length (k2 - k1) so they read as per-base rates, not per-edgemer counts. A companion metric, basepair_change_rate, folds matched and unmatched extensions together into a single exact per-base change rate — the aggregate before the error/mutation split.

Visibility tiers: default, --advanced, --hidden

Section titled “Visibility tiers: default, --advanced, --hidden”

Not every number is meant for every reader. snipe qc reports metrics in three tiers so the everyday TSV stays legible while the full internals remain reachable:

  • default — always emitted. The core metrics: k-size, scale, the change/error/mutation rates, coverage, and contamination summaries.
  • --advanced — verbose additions. The intermediate edgemer counts the default rates are computed from (matched vs. unmatched extensions, distinct edgemers, per-source variance), plus experimental calibrated rates.
  • --hidden — developer-only. Raw internal counters such as distinct failed-to-extend edgemers, useful when auditing the pipeline itself.

Each successive flag is additive — --advanced widens the default set, --hidden widens it further. The tier a metric belongs to is fixed in the metric registry and listed for every metric in the catalog.

Beyond the change/error/mutation core, the default and verbose tiers group into a few families. Understanding the families is usually enough to read a report; reach for the catalog when you need a specific formula.

How much of the reference the sample actually covers, and how deeply. This family reports the fraction of reference k-mers seen, mean and median depth (including zero-coverage positions, so sparsity isn’t hidden), and corrected depth estimates that upscale from the sketch and account for the change rate. A non-repetitive variant of these metrics excludes repeat regions for cleaner, less biased depth estimates, and an ROI (region-of-interest) variant restricts the same math to a supplied target region.

Per-chromosome behavior, expanded dynamically at runtime — one mean_depth_of_chr-{N} metric appears for each chromosome present in the reference (autosomes plus X, Y, MT). On top of the per-chromosome depths sit summary signals: an autosomal coefficient of variation that flags uneven coverage or aneuploidy, and ploidy-style ratios (chrX_ploidy, chrY_coverage) that compare sex-chromosome depth to the autosomal baseline.

Whether the sample contains material the reference can’t explain. The main metric here is a contamination index built from non-reference polytons — k-mers absent from the reference yet seen repeatedly, which points to foreign material rather than random error. When you supply candidate contamination sources, a second dynamic family expands into per-source variance metrics, one block per source, so you can see which suspected organism the off-target signal aligns with.

Every one of these numbers is downstream of the edgemer stored in the native .snipesig sketch. The pairing is computed once at sketch time and travels with the signature at full fidelity, so QC is a read over pre-computed structure rather than a re-scan of reads.