vcf-variant-filtering — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited vcf-variant-filtering (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Raw VCF files produced by variant callers (GATK HaplotypeCaller, bcftools mpileup, DeepVariant, etc.) contain a mixture of true variants and artifacts from sequencing errors, alignment issues, and low-coverage regions. Computing summary statistics -- Ts/Tv ratio, variant counts, allele frequency distributions -- on unfiltered data yields unreliable results because false-positive calls disproportionately inflate transversion counts and depress the Ts/Tv ratio. This guide covers how to detect whether a VCF is raw, how to apply appropriate quality filters, when filtering is not appropriate, and how to interpret the resulting statistics correctly.
The QUAL column in a VCF file represents the Phred-scaled probability that the variant site is polymorphic. A QUAL score of 30 means a 1-in-1000 chance the call is wrong; a QUAL of 20 means 1-in-100. Variant callers assign QUAL scores based on read evidence, base qualities, and mapping qualities. Low-QUAL variants (below 20-30) are enriched for sequencing errors and alignment artifacts. Filtering on QUAL is the simplest and most widely used first-pass quality control step.
Common QUAL thresholds and their interpretation:
| QUAL Score | Error Probability | Typical Use |
|---|---|---|
| 10 | 1 in 10 | Very permissive; rarely appropriate for final calls |
| 20 | 1 in 100 | Lenient filtering; useful for somatic calling with supporting evidence |
| 30 | 1 in 1,000 | Standard default for germline variant filtering |
| 50 | 1 in 100,000 | Stringent; used in clinical or high-confidence applications |
| 100+ | Extremely low | Highly supported variants; may over-filter low-coverage regions |
For GATK-based workflows, VQSR or hard filtering on INFO annotations (QD, FS, MQ, etc.) may supplement or replace QUAL filtering. GATK's recommended hard filters for SNPs include QD < 2.0, FS > 60.0, MQ < 40.0, MQRankSum < -12.5, and ReadPosRankSum < -8.0.
The transition-to-transversion (Ts/Tv) ratio is a key quality metric for variant call sets. Transitions (A<->G, C<->T) are chemically favored over transversions (all other substitutions) due to the molecular structure of nucleotide bases. Expected Ts/Tv values serve as benchmarks:
A Ts/Tv ratio significantly below the expected range indicates contamination by false-positive transversion calls, which are the hallmark of sequencing errors. After proper quality filtering, the Ts/Tv ratio should rise to the expected range for the assay type.
A "raw" VCF is the direct output of a variant caller before any quality filtering has been applied. A "filtered" VCF has had quality thresholds applied, either by hard filtering (QUAL, DP, QD, etc.) or by model-based filtering (GATK VQSR, CNN). Distinguishing between the two is critical because computing statistics on raw data without disclosure leads to incorrect conclusions.
Indicators that a VCF is raw or unfiltered:
sample_raw_variants.vcf). (missing) for all recordsIndicators that a VCF has already been filtered:
PASS, LowQual, VQSRTrancheSNP99.90to100.00)##FILTER= and ##bcftools_viewCommand= lines)The FILTER column in VCF format has specific semantics defined by the VCF specification:
. (dot) -- filter status has not been applied; the variant is unassessedPASS -- the variant passed all filtersA common misconception is that . means the variant passed. In reality, . means no filter has been evaluated, so the variant's quality is unknown. Another misconception is that PASS in every row means the file is unfiltered -- some callers (e.g., DeepVariant) mark all emitted variants as PASS because they only output high-confidence calls.
To inspect the FILTER column programmatically:
# Count occurrences of each FILTER value
bcftools query -f '%FILTER\n' input.vcf | sort | uniq -c | sort -rn | head
# Check if any non-'.' FILTER values exist
bcftools query -f '%FILTER\n' input.vcf | grep -v '^\.$' | headUnderstanding the FILTER column is the first step in any VCF quality assessment. Always inspect it before deciding whether additional filtering is needed.
Is the VCF raw or unfiltered?
├── Yes (FILTER='.', many low-QUAL variants)
│ ├── Does the user ask for RAW statistics specifically?
│ │ ├── Yes → Do NOT filter; compute on data as-is, note it is raw
│ │ └── No → Apply QUAL>=30 filter before computing statistics
│ └── Does the user specify a custom threshold?
│ ├── Yes → Use their threshold
│ └── No → Default to QUAL>=30
├── No (FILTER has PASS/other values, filtered header present)
│ ├── Does the user ask for additional filtering?
│ │ ├── Yes → Apply requested filter
│ │ └── No → Compute statistics on existing filtered set
│ └── Does the Ts/Tv ratio look suspicious despite filtering?
│ └── Yes → Investigate; may need stricter thresholds
└── Uncertain
└── Inspect FILTER column and QUAL distribution to determine status| Scenario | Action | Rationale |
|---|---|---|
| Raw VCF, general statistics requested | Apply QUAL>=30 before computing | Low-QUAL variants are enriched for errors and bias all statistics |
| Raw VCF, user explicitly asks for raw stats | Compute as-is, report that data is unfiltered | Respect the user's intent; they may be evaluating caller performance |
| Raw VCF, user specifies threshold (e.g., QUAL>=50) | Use the user-specified threshold | User has domain-specific requirements |
| Pre-filtered VCF (FILTER=PASS present) | Compute without additional QUAL filter | Double-filtering may remove true variants unnecessarily |
| VCF with mixed FILTER values | Filter to PASS-only with bcftools view -f PASS | Non-PASS variants were flagged for a reason |
| VQSR-filtered VCF | Respect existing tranche filtering | VQSR is a more sophisticated filter than simple QUAL thresholds |
| Small panel or targeted sequencing | Consider lower QUAL threshold (e.g., 20) | Small panels have different error profiles and fewer variants |
bcftools query -f '%FILTER\n' input.vcf | sort | uniq -c | sort -rn | head to get a quick overview. # Preferred: use bcftools for Ts/Tv computation
bcftools stats input.vcf | grep ^TSTV
# Preferred: use bcftools for variant counts
bcftools stats input.vcf | grep ^SN # Filter and compute statistics in one pipeline
bcftools view -i 'QUAL>=30' input.vcf | bcftools stats - > filtered_stats.txt. for all records, apply QUAL>=30 filtering first.. (unassessed), PASS (passed all filters), and named filters (failed). Use bcftools query -f '%FILTER\n' | sort | uniq -c to inspect.bcftools stats for Ts/Tv computation. It is well-tested, fast, and handles all VCF edge cases correctly.##bcftools_viewCommand, ##GATKCommandLine) and the FILTER column for non-dot values before adding more filters. # View header for caller and filter information
bcftools view -h input.vcf | grep -E '##(FILTER|source|GATKCommandLine|bcftools)'
# Check FILTER column values
bcftools query -f '%FILTER\n' input.vcf | sort | uniq -c | sort -rn | head # Check QUAL distribution
bcftools query -f '%QUAL\n' input.vcf | awk '{if($1<30) low++; else high++} END {print "QUAL<30:", low+0, "QUAL>=30:", high+0}'. and many low-QUAL variants exist, proceed to Step 3. # Apply QUAL>=30 filter
bcftools view -i 'QUAL>=30' input.vcf -Oz -o filtered.vcf.gz
bcftools index filtered.vcf.gz # Ts/Tv ratio from filtered VCF
bcftools view -i 'QUAL>=30' input.vcf | bcftools stats - | grep ^TSTV | cut -f5
# Full summary numbers
bcftools view -i 'QUAL>=30' input.vcf | bcftools stats - | grep ^SN
# Per-sample statistics
bcftools stats -s - input.vcf | grep ^PSC # Count variants before filtering
bcftools view -H input.vcf | wc -l
# Count variants after filtering
bcftools view -i 'QUAL>=30' input.vcf | bcftools view -H | wc -l
# Compare Ts/Tv before and after
echo "=== Before filtering ==="
bcftools stats input.vcf | grep ^TSTV
echo "=== After QUAL>=30 filtering ==="
bcftools view -i 'QUAL>=30' input.vcf | bcftools stats - | grep ^TSTVbcftools query -f '%FILTER\n' and examine the QUAL distribution. This takes seconds and prevents hours of wasted analysis on unreliable data.bcftools view and bcftools stats in a pipe rather than writing intermediate files. This is faster, uses less disk, and reduces the chance of analyzing the wrong file.bcftools-variant-manipulation -- Detailed bcftools usage for subsetting, merging, annotating, and manipulating VCF files beyond simple filteringgatk-variant-calling -- Upstream variant calling with GATK HaplotypeCaller, including VQSR and hard filtering configurationsamtools-bam-processing -- BAM file quality control and processing steps that precede variant calling and affect downstream VCF quality~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.