You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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: am → FP.gt, lm → FP.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
"D4: One decision per site, in both the TSVs and summary.vcf" — the counts.
"D4: Add --stringency {lm,am,gm,pm}" — the pm strict-inequality case.
Note
Authorship: the content below was drafted by Claude Opus 5 (an AI coding agent) and
filed via
ghunder @TimD1-bot, a bot account operated by @TimD1. It reflects theagent'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 metricsvcfdist itself does not.
Change
Added to
precision-recall-summary.tsv:FP_GTamFP_ALlmQUERY_UNKBD=NTRUTH_TOTAL,QUERY_TOTALFRAC_NAQUERY_UNK / QUERY_TOTALTRUTH_TITV,QUERY_TITVTRUTH_HET_HOM,QUERY_HET_HOMorig_gts+ploidiesFP_GT,FP_AL, andQUERY_UNKare counts and also go inprecision-recall.tsv, whose rows are aMIN_QUALsweep. The ratios stay in the summary only — they are composition checks on a callset, andrecomputing them per quality threshold invites reading noise as signal.
FP_GT/FP_ALThese are the two columns hap.py exposes as
FP.gtandFP.al, and the mapping is fixed by what aquantifier reads:
am→FP.gt,lm→FP.al. Emitting them natively is what makes a GA4GH exportcheckable on these columns rather than only on totals.
They do not sum to
QUERY_FP. An FP withBK=.matched nothing and belongs to neither, and under--stringency pma phase-error FP carriesBK=gmand also lands in neither. SoFP_GT + FP_AL ≤ QUERY_FP, exactly as in hap.py's own output. The documentation must say so, since areader will otherwise treat the shortfall as a bug.
TRUTH_TOTAL/QUERY_TOTAL, and the trap they exposeThe reconciliation
QUERY_TP + QUERY_FP + QUERY_UNK = QUERY_TOTALis stated over a columnprecision-recall-summary.tsvdoes not have — its header isVAR_TYPE THRESHOLD MIN_QUAL TRUTH_TP QUERY_TP TRUTH_FN QUERY_FP PREC RECALL F1_SCORE F1_QSCORE(
src/print.cpp:408), whileprecision-recall.tsvcarries both totals (src/print.cpp:351-352). Bothare added here so the summary row reconciles on its own terms and the two files carry the same count
set.
QUERY_TOTALmust includeQUERY_UNK, and is therefore not precision's denominator. Today'squery_totisquery_tp + query_fpand is used for both purposes (src/print.cpp:365,:371,:425-427,:437). Precision must keep excluding unassessed calls — that is the entire reasonFRAC_NAis the trust signal — so wideningquery_totin place would silently re-denominate precisionand 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 existingQUERY_TOTALcolumn changes meaningwherever
QUERY_UNKis nonzero, since it istp + fptoday.QUERY_UNK/FRAC_NABoth are needed, as a count and as a fraction.
QUERY_UNKmakes the row reconcile;FRAC_NAis thetrust signal, because precision excludes unassessed calls from its denominator. A run at
FRAC_NA = 0.4reports precision over 60% of the query calls and is not comparable to one at0.01.The fraction is what catches a mis-specified
--bed.The two ratios
Pure QC; they affect no other figure.
HET_HOMearns its place by construction: it is the directsymptom 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
FP_GTandFP_ALin theright columns.
FP_GT + FP_AL ≤ QUERY_FPat every stringency, including both cases that make it a strictinequality: a
BK=.FP, and under--stringency pma phase-error FP.QUERY_TP + QUERY_FP + QUERY_UNK == QUERY_TOTALon every row, andFRAC_NAmatches the ratio ofthose columns.
QUERY_TOTALexceedsQUERY_TP + QUERY_FPwhile
PRECis unchanged from the same run with those variants dropped — the guard againstre-denominating precision.
HET_HOMon a known hom/het fixture, which pins the per-site counting change because it would read2× under per-haplotype counting.
QUERY_UNKandFRAC_NAand nothing else — the intended-effect form ofthe
BD=Ninertness assertion.Dependencies
pmstrict-inequality case.QUERY_UNKto countand it would be structurally zero, misreporting "nothing was skipped" as "skipping is not tracked".