agentsclimarketplace

Vcf basics

Skill BioTender-max/awesome-bio-agent-skills/skills/bioskills/vcf-basics

View, query, and understand VCF/BCF variant files using bcftools and cyvcf2. Use when inspecting variants, extracting specific fields, or understanding VCF format structure.From its SKILL.md

Install
npx -y skills add BioTender-max/awesome-bio-agent-skills --skill vcf-basics

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.

SKILL.md

12.3 KB, ~3.5k tokens by cl100k_base, as published. Nobody here has run it

Version Compatibility

Reference examples tested with: bcftools 1.19+, numpy 1.26+

Before using code patterns, verify installed versions match. If versions differ:

  • Python: pip show <package> then help(module.function) to check signatures
  • CLI: <tool> --version then <tool> --help to confirm flags

If code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.

VCF/BCF Basics

View and query variant files using bcftools and cyvcf2.

Format Overview

FormatDescriptionUse Case
VCFText format, human-readableDebugging, small files
VCF.gzCompressed VCF (bgzip)Standard distribution
BCFBinary VCFFast processing, large files

VCF Format Structure

##fileformat=VCFv4.2
##INFO=<ID=DP,Number=1,Type=Integer,Description="Total Depth">
##FORMAT=<ID=GT,Number=1,Type=String,Description="Genotype">
##FORMAT=<ID=DP,Number=1,Type=Integer,Description="Read Depth">
#CHROM  POS     ID      REF     ALT     QUAL    FILTER  INFO    FORMAT  SAMPLE1
chr1    1000    rs123   A       G       30      PASS    DP=50   GT:DP   0/1:25

Header Lines (##)

  • ##fileformat - VCF version
  • ##INFO - INFO field definitions
  • ##FORMAT - FORMAT field definitions
  • ##FILTER - Filter definitions
  • ##contig - Reference contigs
  • ##reference - Reference genome

Column Header (#CHROM)

Fixed columns: CHROM, POS, ID, REF, ALT, QUAL, FILTER, INFO, FORMAT Followed by sample columns

Data Columns

ColumnDescription
CHROMChromosome
POS1-based position of the first base in REF
IDVariant identifier (e.g., rs number) or . if novel
REFReference allele
ALTAlternate allele(s), comma-separated. * indicates an overlapping deletion at this site
QUALPhred-scaled probability that a variant exists at this site (site-level, NOT per-sample)
FILTERPASS or semicolon-separated filter names. . means filters were not applied
INFOSemicolon-separated key=value pairs (site-level annotations)
FORMATColon-separated format keys defining per-sample field order
SAMPLEColon-separated values matching FORMAT order

Critical Field Interpretation

Understanding what each field actually measures -- and what it does not -- is essential for filtering and interpretation decisions.

QUAL vs GQ: Different Questions

MetricScopeQuestion AnsweredWhen Most Useful
QUALSite-level"Is there any variant here at all?"Multi-sample calling where site confidence matters
GQSample-level"Is this specific genotype assignment correct?"Per-sample genotype confidence
QDSite-levelQUAL normalized by allele depthPreferred over raw QUAL for filtering (depth-independent)

QUAL can be high even when an individual sample's genotype is uncertain. Conversely, a sample may have a confident genotype (high GQ) at a site with moderate QUAL.

AD vs DP Discrepancy

The sum of AD (allelic depth) values is often less than DP (total depth). This is expected behavior, not an error:

  • DP counts all reads spanning the position, including uninformative reads (low base quality, ambiguous alignment)
  • AD counts only reads that confidently support a specific allele
  • INFO/DP (site-level) differs from FORMAT/DP (per-sample) -- site DP is the sum across all samples

Key INFO Annotations for Filtering

AnnotationMeaningWhat It Detects
QDQUAL / allele depthLow values suggest variant quality not supported by reads
FSFisher strand bias (phred-scaled)Variant reads predominantly on one strand (artifact)
SORStrand odds ratioSame as FS but handles high-depth sites better
MQRoot mean square mapping qualityLow values indicate reads map ambiguously (paralogous regions)
MQRankSumMQ difference: ref vs alt readsVery negative = alt reads map much worse than ref (suspicious)
ReadPosRankSumRead position: ref vs alt readsVery negative = variant only at read ends (misalignment artifact)

PL (Phred-Scaled Likelihoods)

PL encodes the relative likelihood of each possible genotype. For a biallelic site: PL = [P(0/0), P(0/1), P(1/1)]. The most likely genotype always has PL=0; GQ equals the difference between the lowest and second-lowest PL values.

For multiallelic sites with n alleles, PL contains n*(n+1)/2 values covering all possible diploid genotypes.

bcftools view

Goal: View, subset, and convert VCF/BCF files from the command line.

Approach: Use bcftools view with flags for header control, region selection, sample extraction, and format conversion.

"Show me what's in this VCF file" → Display VCF contents with optional filtering by header, region, or sample.

View VCF

bcftools view input.vcf.gz | head

View Header Only

bcftools view -h input.vcf.gz

View Without Header

bcftools view -H input.vcf.gz | head

View Specific Region

bcftools view input.vcf.gz chr1:1000000-2000000

View Specific Samples

bcftools view -s sample1,sample2 input.vcf.gz

Exclude Samples

bcftools view -s ^sample3 input.vcf.gz

bcftools query

Goal: Extract specific fields from a VCF in a custom tabular format.

Approach: Use bcftools query with format specifiers for CHROM, POS, INFO, and FORMAT fields.

"Extract positions and genotypes from my VCF" → Pull specific columns from variant records into a flat text format.

Extract specific fields in custom format.

Basic Query

bcftools query -f '%CHROM\t%POS\t%REF\t%ALT\n' input.vcf.gz

Query with INFO Fields

bcftools query -f '%CHROM\t%POS\t%INFO/DP\t%INFO/AF\n' input.vcf.gz

Query with Sample Fields

bcftools query -f '%CHROM\t%POS[\t%GT]\n' input.vcf.gz

Query Specific Samples

bcftools query -f '%CHROM\t%POS[\t%SAMPLE=%GT]\n' -s sample1,sample2 input.vcf.gz

Include Header

bcftools query -H -f '%CHROM\t%POS\t%REF\t%ALT\n' input.vcf.gz

Common Format Specifiers

SpecifierDescription
%CHROMChromosome
%POSPosition
%IDVariant ID
%REFReference allele
%ALTAlternate allele
%QUALQuality score
%FILTERFilter status
%INFO/TAGINFO field value
%TYPEVariant type (snp, indel, etc.)
[%GT]Genotype (per sample)
[%DP]Depth (per sample)
[%SAMPLE]Sample name
\nNewline
\tTab

Format Conversion

Goal: Convert between VCF, compressed VCF, and BCF formats.

Approach: Use bcftools view with output format flags (-Ov, -Oz, -Ob) and bgzip/index for compression and indexing.

VCF to BCF

bcftools view -Ob -o output.bcf input.vcf.gz

BCF to VCF

bcftools view -Ov -o output.vcf input.bcf

Compress VCF (bgzip)

bgzip input.vcf
# Creates input.vcf.gz

Index VCF/BCF

bcftools index input.vcf.gz
# Creates input.vcf.gz.csi

bcftools index -t input.vcf.gz
# Creates input.vcf.gz.tbi (tabix index)

Output Format Options

FlagFormat
-OvUncompressed VCF
-OzCompressed VCF (bgzip)
-OuUncompressed BCF
-ObCompressed BCF

Genotype Encoding

GenotypeMeaning
0/0Homozygous reference
0/1Heterozygous
1/1Homozygous alternate
1/2Heterozygous for two different ALT alleles (compound het at multiallelic site)
./.Missing genotype (no confident call)
0|1Phased heterozygous (allele before | is on haplotype 1)

Phased vs Unphased

  • / separates unphased alleles -- the two chromosomal copies are known, but which came from which parent is not
  • | separates phased alleles -- haplotype assignment is known (e.g., from read-backed phasing, trio analysis, or long-read sequencing)
  • Phasing matters for compound heterozygosity: two variants on the same gene are pathogenic only if they are on different haplotypes (in trans), not the same haplotype (in cis)

Multiallelic Genotypes

At multiallelic sites (e.g., ALT = G,T), allele indices reference the comma-separated ALT list: 0=REF, 1=first ALT, 2=second ALT. Genotype 1/2 means one copy of each ALT allele. Splitting multiallelic sites into biallelic records with bcftools norm -m- converts 1/2 into two 0/1 records, losing compound heterozygosity information -- see variant-normalization for caveats.

cyvcf2 Python Alternative

Goal: Read, query, and write VCF files programmatically in Python.

Approach: Use cyvcf2's VCF reader to iterate variants, access properties/INFO/FORMAT fields, and write filtered output with Writer.

"Parse this VCF in Python" → Open VCF with cyvcf2 and iterate variant records with attribute-style access to fields.

Open and Iterate

from cyvcf2 import VCF

vcf = VCF('input.vcf.gz')
for variant in vcf:
    print(f'{variant.CHROM}:{variant.POS} {variant.REF}>{variant.ALT[0]}')

Access Variant Properties

from cyvcf2 import VCF

for variant in VCF('input.vcf.gz'):
    print(f'Chrom: {variant.CHROM}')
    print(f'Pos: {variant.POS}')
    print(f'ID: {variant.ID}')
    print(f'Ref: {variant.REF}')
    print(f'Alt: {variant.ALT}')  # List
    print(f'Qual: {variant.QUAL}')
    print(f'Filter: {variant.FILTER}')
    print(f'Type: {variant.var_type}')  # snp, indel, etc.
    break

Access INFO Fields

from cyvcf2 import VCF

for variant in VCF('input.vcf.gz'):
    dp = variant.INFO.get('DP')
    af = variant.INFO.get('AF')
    print(f'{variant.CHROM}:{variant.POS} DP={dp} AF={af}')

Access Genotypes

from cyvcf2 import VCF

vcf = VCF('input.vcf.gz')
samples = vcf.samples  # List of sample names

for variant in vcf:
    gts = variant.gt_types  # 0=HOM_REF, 1=HET, 2=UNKNOWN, 3=HOM_ALT
    for sample, gt in zip(samples, gts):
        gt_str = ['HOM_REF', 'HET', 'UNKNOWN', 'HOM_ALT'][gt]
        print(f'{sample}: {gt_str}')
    break

Access Sample Fields

from cyvcf2 import VCF

for variant in VCF('input.vcf.gz'):
    depths = variant.format('DP')  # numpy array
    gqs = variant.format('GQ')     # Genotype quality
    print(f'Depths: {depths}')

Fetch Region

from cyvcf2 import VCF

vcf = VCF('input.vcf.gz')
for variant in vcf('chr1:1000000-2000000'):
    print(f'{variant.CHROM}:{variant.POS}')

Get Header Info

from cyvcf2 import VCF

vcf = VCF('input.vcf.gz')
print(f'Samples: {vcf.samples}')
print(f'Contigs: {vcf.seqnames}')

# INFO field definitions
for info in vcf.header_iter():
    if info['HeaderType'] == 'INFO':
        print(f'{info["ID"]}: {info["Description"]}')

Write VCF

from cyvcf2 import VCF, Writer

vcf = VCF('input.vcf.gz')
writer = Writer('output.vcf', vcf)

for variant in vcf:
    if variant.QUAL > 30:
        writer.write_record(variant)

writer.close()
vcf.close()

Quick Reference

Taskbcftoolscyvcf2
View VCFbcftools view file.vcf.gzVCF('file.vcf.gz')
View headerbcftools view -h file.vcf.gzvcf.header_iter()
Get regionbcftools view file.vcf.gz chr1:1-1000vcf('chr1:1-1000')
Query fieldsbcftools query -f '%CHROM\t%POS\n'Loop with properties
Count variantsbcftools view -H file.vcf.gz | wc -lsum(1 for _ in vcf)
VCF to BCFbcftools view -Ob -o out.bcf in.vcf.gzUse Writer

Common Errors

ErrorCauseSolution
no BGZF EOF markerNot bgzippedUse bgzip not gzip
index requiredMissing index for region queryRun bcftools index
sample not foundWrong sample nameCheck with bcftools query -l

Related Skills

  • variant-calling - Generate VCF from alignments
  • filtering-best-practices - Filter variants by quality/criteria
  • vcf-manipulation - Merge, concat, intersect VCFs
  • alignment-files/pileup-generation - Generate pileup for calling

What ships with it: 2 files

3.6 KB alongside SKILL.md, 1 of them executable

examples/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.