Install any skill in seconds. Free to start, no credit card required.
Get Started Free →View, query, and understand VCF/BCF variant files using bcftools and cyvcf2. Use when inspecting variants, extracting specific fields, or understanding VCF format structure.
.claude/skills/bio-vcf-basics/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-15 | ✗→✓ | ▲ Improved | — | — |
| case-22 | ✗→✓ | ▲ Improved | — | — |
| case-07 | ✓→✓ | = Same ✓ | — | — |
| case-04 | ✓→✓ | = Same ✓ | — | — |
| case-12 | ✗→✗ | = Same ✗ | — | — |
Reference examples tested with: bcftools 1.19+, numpy 1.26+
Before using code patterns, verify installed versions match. If versions differ:
pip show <package> then help(module.function) to check signatures<tool> --version then <tool> --help to confirm flagsIf code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
View and query variant files using bcftools and cyvcf2.
| Format | Description | Use Case | |--------|-------------|----------| | VCF | Text format, human-readable | Debugging, small files | | VCF.gz | Compressed VCF (bgzip) | Standard distribution | | BCF | Binary VCF | Fast processing, large files |
##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##fileformat - VCF version##INFO - INFO field definitions##FORMAT - FORMAT field definitions##FILTER - Filter definitions##contig - Reference contigs##reference - Reference genomeFixed columns: CHROM, POS, ID, REF, ALT, QUAL, FILTER, INFO, FORMAT Followed by sample columns
| Column | Description | |--------|-------------| | CHROM | Chromosome | | POS | 1-based position | | ID | Variant identifier (e.g., rs number) | | REF | Reference allele | | ALT | Alternate allele(s), comma-separated | | QUAL | Phred-scaled quality score | | FILTER | PASS or filter name | | INFO | Semicolon-separated key=value pairs | | FORMAT | Colon-separated format keys | | SAMPLE | Colon-separated values matching FORMAT |
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.
bashbcftools view input.vcf.gz | head
bashbcftools view -h input.vcf.gz
bashbcftools view -H input.vcf.gz | head
bashbcftools view input.vcf.gz chr1:1000000-2000000
bashbcftools view -s sample1,sample2 input.vcf.gz
bashbcftools view -s ^sample3 input.vcf.gz
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.
bashbcftools query -f '%CHROM\t%POS\t%REF\t%ALT\n' input.vcf.gz
bashbcftools query -f '%CHROM\t%POS\t%INFO/DP\t%INFO/AF\n' input.vcf.gz
bashbcftools query -f '%CHROM\t%POS[\t%GT]\n' input.vcf.gz
bashbcftools query -f '%CHROM\t%POS[\t%SAMPLE=%GT]\n' -s sample1,sample2 input.vcf.gz
bashbcftools query -H -f '%CHROM\t%POS\t%REF\t%ALT\n' input.vcf.gz
| Specifier | Description | |-----------|-------------| | %CHROM | Chromosome | | %POS | Position | | %ID | Variant ID | | %REF | Reference allele | | %ALT | Alternate allele | | %QUAL | Quality score | | %FILTER | Filter status | | %INFO/TAG | INFO field value | | %TYPE | Variant type (snp, indel, etc.) | | [%GT] | Genotype (per sample) | | [%DP] | Depth (per sample) | | [%SAMPLE] | Sample name | | \n | Newline | | \t | Tab |
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.
bashbcftools view -Ob -o output.bcf input.vcf.gz
bashbcftools view -Ov -o output.vcf input.bcf
bashbgzip input.vcf # Creates input.vcf.gz
bashbcftools index input.vcf.gz # Creates input.vcf.gz.csi bcftools index -t input.vcf.gz # Creates input.vcf.gz.tbi (tabix index)
| Flag | Format | |------|--------| | -Ov | Uncompressed VCF | | -Oz | Compressed VCF (bgzip) | | -Ou | Uncompressed BCF | | -Ob | Compressed BCF |
| Genotype | Meaning | |----------|---------| | 0/0 | Homozygous reference | | 0/1 | Heterozygous | | 1/1 | Homozygous alternate | | 1/2 | Heterozygous (two different alts) | | ./. | Missing | | 0\|1 | Phased heterozygous |
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.
pythonfrom cyvcf2 import VCF vcf = VCF('input.vcf.gz') for variant in vcf: print(f'{variant.CHROM}:{variant.POS} {variant.REF}>{variant.ALT[0]}')
pythonfrom 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
pythonfrom 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}')
pythonfrom 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
pythonfrom 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}')
pythonfrom cyvcf2 import VCF vcf = VCF('input.vcf.gz') for variant in vcf('chr1:1000000-2000000'): print(f'{variant.CHROM}:{variant.POS}')
pythonfrom 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"]}')
pythonfrom 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()
| Task | bcftools | cyvcf2 | |------|----------|--------| | View VCF | bcftools view file.vcf.gz | VCF('file.vcf.gz') | | View header | bcftools view -h file.vcf.gz | vcf.header_iter() | | Get region | bcftools view file.vcf.gz chr1:1-1000 | vcf('chr1:1-1000') | | Query fields | bcftools query -f '%CHROM\t%POS\n' | Loop with properties | | Count variants | bcftools view -H file.vcf.gz \| wc -l | sum(1 for _ in vcf) | | VCF to BCF | bcftools view -Ob -o out.bcf in.vcf.gz | Use Writer |
| Error | Cause | Solution | |-------|-------|----------| | no BGZF EOF marker | Not bgzipped | Use bgzip not gzip | | index required | Missing index for region query | Run bcftools index | | sample not found | Wrong sample name | Check with bcftools query -l |
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-25 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-23 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-24 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted. The headline lift of +8 percentage points is the difference between those two pass rates over the 25 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.