Scoring and Confidence Assignment¶
VNtyper evaluates variant calls using two complementary scoring mechanisms: the frame score (biological validity) and the depth score (signal-to-noise ratio). Together, these determine the confidence tier assigned to each call.
Frame Score¶
The frame score evaluates whether a variant shifts the MUC1 coding reading frame:
A variant is classified as a frameshift when (len(ALT) - len(REF)) % 3 != 0. Only frameshift variants cause ADTKD-MUC1; in-frame indels preserve the reading frame and do not produce the pathogenic truncated neo-protein.
Frameshift patterns in MUC1
The pipeline classifies frameshifts by modular direction:
- Insertion frameshifts: direction > 0 and
frameshift_amount == 1(3n+1 bp insertion) - Deletion frameshifts: direction < 0 and
frameshift_amount == 2(3n+2 bp deletion)
These patterns match the established pathogenic mutation signatures in the MUC1 VNTR. Matching variants receive is_valid_frameshift = True.
Why insertions and deletions use different remainders (Issue #181)
An insertion of 3n+1 bases and a deletion of 3n+2 bases are frame-equivalent: both shift the reading frame by +1 (mod 3). This specific shift creates the toxic MUC1-fs neo-protein (exemplified by canonical dupC). The opposite pair (an insertion of 3n+2 bases or a deletion of 3n+1 bases) shifts into the alternate frame, which has not been identified as pathogenic in patients. Rejecting a (3n+1)-bp deletion is a deliberate biological filter, not accidental asymmetry.
Haplo Count¶
After frame scoring, VNtyper computes haplo_count: the frequency with which the identical variant call (POS, REF, ALT) occurs across Kestrel's reconstructed haplotype assemblies. Higher counts indicate stronger assembly support and serve as a tie-breaker during variant selection.
Depth Score¶
The depth score measures the signal strength of a variant relative to its locus:
Definitions:
- Estimated Depth (Alternate Variant): The number of k-mer paths supporting the alternate allele.
- Estimated Depth (Variant Active Region): Total k-mer depth across the active region containing the call.
Division-by-zero occurrences evaluate to NaN. Higher depth scores indicate higher relative variant abundance.
Confidence Assignment¶
Confidence tiers are assigned based on depth score, alternate allele depth, and active region depth. Thresholds are derived from Saei et al., iScience 26, 107171 (2023).
Threshold Configuration¶
Defined in kestrel_config.json:
| Parameter | Config Key | Value |
|---|---|---|
| Reporting floor | reporting_floor | 0.00469 |
| Depth score (low) | depth_score_thresholds.low | 0.00469 |
| Depth score (high) | depth_score_thresholds.high | 0.00515 |
| Alt depth (low) | alt_depth_thresholds.low | 20 |
| Alt depth (mid_low) | alt_depth_thresholds.mid_low | 21 |
| Alt depth (mid_high) | alt_depth_thresholds.mid_high | 100 |
| Region depth threshold | var_active_region_threshold | 200 |
Three calibrated parameters share the base value 0.00469:
confidence_assignment.reporting_floor(0.00469): Minimum noise floor; variants below this are labeledNegative.confidence_assignment.depth_score_thresholds.low(0.00469): Lower boundary of theLow_Precisionband ([low, high]).alt_filtering.gg_depth_score_threshold(0.00469): Minimum depth score required forALT == "GG"variants.
Confidence Levels¶
| Level | Criteria | Evidence assessment |
|---|---|---|
| High_Precision* | Alt depth >= 100 and Depth Score > 0.00515 | Robust variant call with extensive k-mer support. |
| High_Precision | Alt depth >= 21 and < 100, and Depth Score > 0.00515 | High-confidence call meeting both calibrated cutoffs. |
| Low_Precision | Depth Score in closed band 0.00469 <= Depth Score <= 0.00515, at any alt depth; or alt depth <= 20 with Depth Score > 0.00515 | Marginal signal; requires orthogonal validation. |
| Negative | Depth Score < 0.00469, or undefined (active region depth 0 -> NaN) | Subthreshold noise; candidate is discarded. |
The High tiers require a strict inequality (>) against 0.00515. The mid-band demotion runs last and defines a closed interval at its upper limit, ensuring that a Depth Score of exactly 0.00515 receives Low_Precision (Issue #184).
Decision Table¶
Depth Score is alt depth / region depth, making the metrics interdependent:
| Depth Score | Alt depth <= 20 | Alt depth 21 to 99 | Alt depth >= 100 |
|---|---|---|---|
| undefined (region depth 0) | Negative | Negative | Negative |
| < 0.00469 | Negative | Negative | Negative |
| = 0.00469 exactly | Low_Precision | Low_Precision | Low_Precision |
| 0.00469 to 0.00515 (interior) | Low_Precision | Low_Precision | Low_Precision |
| = 0.00515 exactly | Low_Precision | Low_Precision | Low_Precision |
| > 0.00515 | Low_Precision | High_Precision | High_Precision* |
The active-region depth threshold does not alter outcomes across this table.
Alt depth interval between 20 and 21
Because alt_depth_thresholds.low is 20 and mid_low is 21, fractional values between them lack an explicit integer rule. Alternate depths come from Kestrel's Sample field as k-mer-path depths and are always whole numbers in practice, so this gap is unreachable in production. However, confidence_assignment.py performs no integer cast, so nothing in the module itself enforces it.
Active region depth threshold does not cap confidence labels
An active-region depth <= 200 demotes candidates to Low_Precision, but this demotion runs first and is superseded by High_Precision rules whenever applicable. A variant with an alt depth of 50 and a total active-region k-mer depth of 150 is reported as High_Precision today, not Low_Precision, exactly the same label it would receive at a total active-region k-mer depth of 5000.
When alt depth is >= 21 and region depth is <= 200, the resulting Depth Score is at least 21/200 = 0.105 (twenty times the high threshold), causing rule 2 or rule 3 to fire. Where the demotion is not overwritten, the variant would be Low_Precision or Negative regardless.
This precedence was confirmed on Issue #183: the sequential last-wins assignment is intentional, and the legacy v1.3 absolute cap (region depth <= 200) is deprecated.
Assignment Logic¶
Confidence evaluation follows an ordered first-match table in vntyper/scripts/confidence_rules.py:
| Order | Condition | Confidence Label | Rationale |
|---|---|---|---|
| 0 | Depth_Score is NaN or < 0.00469 | Negative | Outer reporting floor: eliminates subthreshold candidates. |
| 1 | 0.00469 <= Depth_Score <= 0.00515 | Low_Precision | Mid-band demotion (Issue #184): covers closed calibration band across all alternate depths. |
| 2 | Depth_Score > 0.00515 and alt depth >= 100 | High_Precision* | High depth meeting high-confidence threshold. |
| 3 | Depth_Score > 0.00515 and 21 <= alt depth < 100 | High_Precision | Standard depth meeting high-confidence threshold. |
| 4 | Depth_Score > 0.00515 and alt depth <= 20 | Low_Precision | Shallow alternate depth demotion. |
| 5 | Depth_Score > 0.00515 and 20 < alt depth < 21 and region depth <= 200 | Low_Precision | Fractional depth edge case with shallow active region. |
| 6 | Otherwise | Negative | Fallback for fractional depth gap with deep active region. |
Non-Negative variants set depth_confidence_pass = True, permitting downstream selection.
Changes in Issue #184¶
Before Issue #184, the mid-band rule evaluated the open interval 0.00469 < Depth Score < 0.00515. It now covers the closed interval [0.00469, 0.00515]. The only score affected is exactly 0.00515, which is demoted to Low_Precision:
| Alt depth at Depth Score = 0.00515 | Before #184 | After #184 |
|---|---|---|
| <= 20 | Low_Precision | Low_Precision (unchanged) |
| 21 to 99 | High_Precision | Low_Precision |
| >= 100 | High_Precision* | Low_Precision |
Under integer depths, Depth Score == 0.00515 requires alt = 103k and region = 20000k (103/20000 is exact in IEEE 754 float). The minimum reachable alternate depth is 103, meaning the only tier shift on real data is High_Precision* to Low_Precision.
Sample-Level Confidence Grade¶
VNtyper derives an overall sample-level confidence grade during screening summary generation. The grade synthesizes caller execution state, calling status, subthreshold signals, coverage metrics, and cross-caller concordance.
Vocabulary and Pre-Emption Order¶
Evaluated against confidence_grade_rules in report_config.json, defaulting to "not-established":
First match wins. The chip shows each grade in words from chip_labels.
| Grade (chip) | Evaluated Conditions | Definition |
|---|---|---|
not-established (No result) | Execution failure (kestrel_execution or advntr_execution == "failed"), Kestrel not performed, or an unestablished result token | Genotyping incomplete; outcome undetermined. |
no-finding-limited (No finding, inconclusive) | Negative or subthreshold Kestrel result, no adVNTR call, coverage QC FAIL or NOT_EVALUATED | A result without a call that cannot rule a variant out. |
no-finding (No finding) | Negative or subthreshold Kestrel result, no adVNTR call, coverage QC PASS or REDUCED | A result without a call at adequate or reduced depth; a reduced depth adds a note. |
finding-limited (Finding, with caveats) | A flagged Kestrel call; an unflagged Kestrel call with coverage NOT_EVALUATED or an uncovered percentage above thresholds.percent_vntr_uncovered (50% by default); an adVNTR-only call with coverage QC FAIL or NOT_EVALUATED | A call whose evidence is qualified by a flag or by missing coverage. Mean depth alone never limits a Kestrel call. |
finding-corroborated (Finding, corroborated) | Unflagged Kestrel call and positive cross-caller concordance (cross_match_is_positive == True) | Called by Kestrel and matched by adVNTR. |
finding (Finding) | Any other unflagged Kestrel call, or an adVNTR-only call with passing coverage | A call. |
All outputs are for research use only.
Reference¶
Saei H. et al., iScience 26, 107171 (2023).