Filter a VCF by depth and quality with bcftools
Drop low-confidence variants from a VCF by hard-filtering on QUAL and INFO/DP with bcftools, then compare record counts before and after.
Prerequisites
- bcftools installed (`bcftools view`, `bcftools index`)
- A bgzipped VCF (`calls.vcf.gz`, ideally already indexed)
1Drop low-QUAL and low-depth sites
The `-e` flag EXCLUDES any site matching the expression, so this removes variants with QUAL below 20 or total depth (INFO/DP) below 10. `-Oz` writes a bgzipped VCF; index it afterwards so downstream tools can random-access the result.
bcftools view -e 'QUAL<20 || INFO/DP<10' calls.vcf.gz -Oz -o filtered.vcf.gz
bcftools index filtered.vcf.gzExpected output
# both commands run silently on success
# writes filtered.vcf.gz, then filtered.vcf.gz.csi2Compare variant counts before and after
`-H` prints only data lines (no header), so piping to `wc -l` counts variant records. The difference between the two numbers is how many low-confidence sites the filter dropped.
bcftools view -H calls.vcf.gz | wc -l
bcftools view -H filtered.vcf.gz | wc -lExpected output
48213
41190Troubleshooting
Error: the tag "DP" is not defined in the VCF header
Qualify the tag as `INFO/DP` or `FORMAT/DP` depending on where the field lives. List what is actually available with `bcftools view -h calls.vcf.gz | grep -E 'INFO|FORMAT'`.
You want to keep failing sites and just mark them, not delete them
Use a soft filter instead: `bcftools filter -s LowQual -e 'QUAL<20' calls.vcf.gz -Oz -o tagged.vcf.gz` writes `LowQual` into the FILTER column and keeps every record.