Back to Blog
File FormatsVariant Calling

The VCF File Format Explained: From CHROM/POS to GT, DP, and GQ

A field-by-field guide to the VCF format: meta-information lines, the 8 fixed columns, and how FORMAT encodes genotype calls with GT, DP, GQ, and AD.

SSSudipta SardarJuly 20, 20268 min read
The VCF File Format Explained: From CHROM/POS to GT, DP, and GQ

Every variant caller, whether it is GATK HaplotypeCaller, DeepVariant, or bcftools call, outputs the same kind of file: a VCF. At first glance it looks like a plain tab-delimited table, but tucked inside a handful of columns is a compact encoding scheme that describes every SNP, insertion, and deletion in a sample, along with how confident the caller is in each one. This guide walks through the VCF specification section by section, so a genotype string like 0/1:45:99:20,25 stops looking like noise and starts reading like a sentence.

What Is a VCF File?

VCF stands for Variant Call Format. It was developed during the 1000 Genomes Project and is now maintained as an open specification (hts-specs, currently version 4.2/4.3) alongside the SAM/BAM specs. A VCF file is plain text, tab-delimited, with one row per variant site rather than one row per read. In practice it is almost always compressed with bgzip (.vcf.gz) and indexed with tabix or bcftools index (.tbi/.csi), so tools can jump straight to a region instead of scanning the whole file.

The Three Sections of a VCF

Every valid VCF file is built from three distinct blocks, always in this order.

1. Meta-information lines

Lines beginning with a double ## carry file metadata. The first line declares the spec version, and later lines describe every abbreviation that will show up in the INFO and FORMAT columns — its ID, expected Number of values, Type, and a Description. A parser reads these first so it knows how to interpret a tag like DP before it appears in a data row.

text
##fileformat=VCFv4.2
##source=GATK-4.5.0
##reference=GRCh38.fasta
##contig=<ID=chr7,length=159345973>
##INFO=<ID=DP,Number=1,Type=Integer,Description="Total Depth">
##INFO=<ID=AF,Number=A,Type=Float,Description="Allele Frequency">
##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">
##FORMAT=<ID=DP,Number=1,Type=Integer,Description="Read Depth">
##FORMAT=<ID=GQ,Number=1,Type=Integer,Description="Genotype Quality">
##FORMAT=<ID=AD,Number=R,Type=Integer,Description="Allelic depths">

2. The header line

A single line starting with one # names every column, tab-separated. It always contains the 8 fixed columns described below, and if the file carries per-sample genotypes it adds a FORMAT column followed by one column per sample.

text
#CHROM  POS  ID  REF  ALT  QUAL  FILTER  INFO  FORMAT  Sample1  Sample2

3. Data lines

Everything after the header is a data line — one row per variant, with values in the same order as the header. This is where the actual calls live, and it is where the rest of this guide is focused.

The 8 Fixed Columns

Every data line starts with exactly these eight columns, always in this order, regardless of whether genotype data follows.

ColumnMeaning
CHROMChromosome or contig name the variant sits on, e.g. chr7
POS1-based position of the first base of REF on that chromosome
IDVariant identifier such as a dbSNP rsID, or . if none is known
REFThe reference allele exactly as it appears in the reference genome
ALTOne or more alternate alleles, comma-separated for multi-allelic sites
QUALPhred-scaled quality score for the assertion that a variant exists at this site
FILTERPASS if the site passed all filters, . if not evaluated, or a semicolon-separated list of failed filter names
INFOSemicolon-separated key=value (or bare flag) annotations describing the site as a whole

A couple of details worth internalizing:

  • POS is 1-based. This trips people up constantly, since several neighboring formats (like BED) are 0-based instead.
  • QUAL is Phred-scaled, the same -10 * log10(P) math as base quality scores. A QUAL of 30 means roughly a 1-in-1000 chance the call is wrong; QUAL of 10 means a 1-in-10 chance. Values are uncapped, from single digits up into the thousands on strong calls.
  • INFO describes the site, not one sample. DP=56 here is depth summed across every sample, AF=0.5 is population allele frequency, AN is total alleles called — easy to confuse with the per-sample FORMAT fields covered next.

FORMAT and Sample Columns: Where Genotypes Live

If a VCF includes actual genotype calls, the fixed 8 columns are followed by a FORMAT column and then one column per sample. FORMAT is a colon-separated list of field IDs, and each sample column that follows holds colon-separated values in that exact same order.

text
FORMAT           Sample1
GT:AD:DP:GQ:PL   0/1:20,25:45:99:850,0,700

Read that pairing positionally: the first field in FORMAT (GT) matches the first value in the sample column (0/1), the second (AD) matches 20,25, and so on.

GT — the genotype call

GT encodes which alleles the sample carries, as integers indexing into REF and ALT: 0 is the reference allele, 1 the first ALT allele, 2 the second, and so on for multi-allelic sites. For a diploid organism you get two integers separated by / (unphased — order carries no information) or | (phased — order is known, usually from a trio or long-read phasing).

GT valueMeaning
0/0Homozygous reference — matches the reference genome on both copies
0/1Heterozygous — one reference copy, one alternate copy
1/1Homozygous alternate — both copies carry the alternate allele
1/2Compound heterozygous — two different alternate alleles, no reference copy
./.Missing genotype — no confident call could be made at this site

DP, GQ, and AD — how much to trust the call

Three FORMAT fields do most of the work of telling you whether a genotype call is solid:

  • DP here is the per-sample read depth at this position. Do not confuse it with the site-level DP in INFO, which sums across all samples.
  • GQ is the Phred-scaled confidence in the genotype call itself, capped at 99 by most callers. GQ of 99 is about as confident as it gets; below 20 is a shaky call worth flagging.
  • AD (allele depth) splits that read depth by which allele each read supported, in REF,ALT order. AD of 20,25 on a 0/1 call means 20 reads backed the reference and 25 backed the alternate — close to the 1:1 ratio expected for a true heterozygote. A skewed ratio like 40,2 next to a 0/1 call is a red flag.

You will also see PL, the normalized Phred-scaled likelihood for each possible genotype (0/0, 0/1, 1/1 for a biallelic site). The lowest value is the called genotype, and 0 always sits there since likelihoods are normalized relative to the best call.

A Worked Example

Put it all together with a single, realistic data line:

text
#CHROM  POS        ID           REF  ALT  QUAL   FILTER  INFO                  FORMAT           Sample1
chr7    140453136  rs121913343  A    T    87.30  PASS    DP=45;AF=0.500;AN=2   GT:AD:DP:GQ:PL   0/1:20,25:45:99:850,0,700

Reading left to right: a SNP on chr7 at position 140453136, matching a known dbSNP entry, changing A to T, with site quality 87.30 and a PASS. Across all samples, 45 reads covered this position and the alternate allele frequency is 0.5. For Sample1, GT of 0/1 calls a heterozygote, backed by 20 reads for A and 25 for T, GQ of 99 — about as confident as it gets.

Reading and Filtering VCFs with bcftools

bcftools is the standard command-line tool for working with VCF and its binary sibling BCF. If you have not installed it yet, our samtools & bcftools install guide walks through setting it up in an isolated conda environment. A few commands cover most day-to-day needs:

bash
# print just the meta-information and header lines
bcftools view -h sample.vcf.gz

# pull CHROM, POS, and per-sample GT/DP/GQ into a flat table
bcftools query -f '%CHROM\t%POS\t[%GT\t%DP\t%GQ\t]\n' sample.vcf.gz

# keep only PASS-filtered variants with QUAL of at least 30
bcftools view -f PASS -e 'QUAL<30' sample.vcf.gz -Oz -o filtered.vcf.gz
bcftools index -t filtered.vcf.gz

The query -f format string is the workhorse: %CHROM and %POS pull fixed columns, while anything in square brackets — [%GT\t%DP\t%GQ\t] — repeats once per sample, exactly what you need when a file carries multiple samples.

Common Pitfalls

  • Multi-allelic sites break naive parsing. When ALT lists more than one allele (T,TA), GT values can be 2, 3, and beyond, not just 0 or 1. Many tools expect biallelic sites, so bcftools norm -m -any is commonly used to split multi-allelic records apart first.
  • Structural variants use symbolic alleles. Large deletions or insertions may appear as a symbolic tag like `<DEL>` rather than real sequence, with coordinates described by INFO fields such as END and SVTYPE.
  • Plain gzip is not bgzip. Tools expecting .vcf.gz need the BGZF block-compressed format bgzip produces, so a tabix index can seek into it. A plain gzip file will fail to index despite the identical extension.
  • A missing FILTER value is not the same as passing. A . means the site was never evaluated; PASS means it was evaluated and cleared. Do not treat the two as equivalent when filtering a callset.

Wrap-up

The VCF format looks intimidating mostly because of density, not complexity: three sections, eight fixed columns, and a colon-separated FORMAT/sample pairing repeated for every genotype. Once you can read GT for the call, DP and AD for the depth behind it, and GQ for how much to trust it, most of what you need from a VCF is legible without opening a manual. Tools like GATK4 (see our install guide) produce these files as their primary output, and bcftools remains the fastest way to filter and query them from the command line.