Skip to content

D4: Add FP_GT, FP_AL, QUERY_UNK, FRAC_NA, totals, and QC ratios #197

Description

@TimD1-bot

Note

Authorship: the content below was drafted by Claude Opus 5 (an AI coding agent) and
filed via gh under @TimD1-bot, a bot account operated by @TimD1. It reflects the
agent's analysis, not a statement authored by @TimD1.

Part of #49.

Problem

The match tier computes signals vcfdist's TSVs do not report — the same signals a GA4GH quantifier
would derive from summary.vcf. Without them, a quantifier fed vcfdist's own export reports metrics
vcfdist itself does not.

Change

Added to precision-recall-summary.tsv:

Column Definition Source
FP_GT query FPs where an allele matched but the genotype did not tier am
FP_AL query FPs with only a partial match, below the credit threshold tier lm
QUERY_UNK query variants retained but not assessed BD=N
TRUTH_TOTAL, QUERY_TOTAL all variants per callset, assessed or not see below
FRAC_NA QUERY_UNK / QUERY_TOTAL derived
TRUTH_TITV, QUERY_TITV transition/transversion ratio, per callset SNP REF/ALT
TRUTH_HET_HOM, QUERY_HET_HOM het:hom ratio, per callset orig_gts + ploidies

FP_GT, FP_AL, and QUERY_UNK are counts and also go in precision-recall.tsv, whose rows are a
MIN_QUAL sweep. The ratios stay in the summary only — they are composition checks on a callset, and
recomputing them per quality threshold invites reading noise as signal.

FP_GT / FP_AL

These are the two columns hap.py exposes as FP.gt and FP.al, and the mapping is fixed by what a
quantifier reads: amFP.gt, lmFP.al. Emitting them natively is what makes a GA4GH export
checkable on these columns rather than only on totals.

They do not sum to QUERY_FP. An FP with BK=. matched nothing and belongs to neither, and under
--stringency pm a phase-error FP carries BK=gm and also lands in neither. So
FP_GT + FP_AL ≤ QUERY_FP, exactly as in hap.py's own output. The documentation must say so, since a
reader will otherwise treat the shortfall as a bug.

TRUTH_TOTAL / QUERY_TOTAL, and the trap they expose

The reconciliation QUERY_TP + QUERY_FP + QUERY_UNK = QUERY_TOTAL is stated over a column
precision-recall-summary.tsv does not have — its header is
VAR_TYPE THRESHOLD MIN_QUAL TRUTH_TP QUERY_TP TRUTH_FN QUERY_FP PREC RECALL F1_SCORE F1_QSCORE
(src/print.cpp:408), while precision-recall.tsv carries both totals (src/print.cpp:351-352). Both
are added here so the summary row reconciles on its own terms and the two files carry the same count
set.

QUERY_TOTAL must include QUERY_UNK, and is therefore not precision's denominator. Today's
query_tot is query_tp + query_fp and is used for both purposes (src/print.cpp:365, :371,
:425-427, :437). Precision must keep excluding unassessed calls — that is the entire reason
FRAC_NA is the trust signal — so widening query_tot in place would silently re-denominate precision
and undo FRAC_NA's purpose in the same stroke. The total and the denominator must be separate values.

Consequence to release-note: precision-recall.tsv's existing QUERY_TOTAL column changes meaning
wherever QUERY_UNK is nonzero, since it is tp + fp today.

QUERY_UNK / FRAC_NA

Both are needed, as a count and as a fraction. QUERY_UNK makes the row reconcile; FRAC_NA is the
trust signal, because precision excludes unassessed calls from its denominator. A run at
FRAC_NA = 0.4 reports precision over 60% of the query calls and is not comparable to one at 0.01.
The fraction is what catches a mis-specified --bed.

The two ratios

Pure QC; they affect no other figure. HET_HOM earns its place by construction: it is the direct
symptom of the record-splitting #49 removes, since a per-haplotype split reports every homozygote as
two heterozygotes. It is only meaningful once per-site counting and per-variant ploidy are both in
place.

Tests

  • A fixture with a genotype error and a below-threshold partial match places FP_GT and FP_AL in the
    right columns.
  • FP_GT + FP_AL ≤ QUERY_FP at every stringency, including both cases that make it a strict
    inequality: a BK=. FP, and under --stringency pm a phase-error FP.
  • QUERY_TP + QUERY_FP + QUERY_UNK == QUERY_TOTAL on every row, and FRAC_NA matches the ratio of
    those columns.
  • A fixture with retained-but-unevaluated variants where QUERY_TOTAL exceeds QUERY_TP + QUERY_FP
    while PREC is unchanged from the same run with those variants dropped — the guard against
    re-denominating precision.
  • HET_HOM on a known hom/het fixture, which pins the per-site counting change because it would read
    2× under per-haplotype counting.
  • Retained variants move QUERY_UNK and FRAC_NA and nothing else — the intended-effect form of
    the BD=N inertness assertion.

Dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions