Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Quality control of phasing and imputation results. Filter by INFO scores, assess accuracy, and prepare imputed data for downstream analysis. Use when filtering low-quality imputed variants or validating imputation accuracy before GWAS.
.claude/skills/bio-phasing-imputation-imputation-qc/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 26% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 55% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 81% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 70% | 0% |
<!--
#
#
-->
bash# Beagle DR2 (dosage R-squared) bcftools query -f '%CHROM\t%POS\t%ID\t%REF\t%ALT\t%INFO/DR2\t%INFO/AF\n' \ imputed.vcf.gz > info_scores.txt # Minimac R2 bcftools query -f '%CHROM\t%POS\t%ID\t%REF\t%ALT\t%INFO/R2\t%INFO/MAF\n' \ imputed.vcf.gz > info_scores.txt # IMPUTE info bcftools query -f '%CHROM\t%POS\t%ID\t%INFO\n' imputed.vcf.gz > info_scores.txt
bash# Standard threshold for GWAS bcftools view -i 'INFO/DR2 > 0.3' imputed.vcf.gz -Oz -o imputed_r2_03.vcf.gz # Strict threshold for fine-mapping bcftools view -i 'INFO/DR2 > 0.8' imputed.vcf.gz -Oz -o imputed_r2_08.vcf.gz # Combined filtering bcftools view -i 'INFO/DR2 > 0.3 && INFO/AF > 0.01 && INFO/AF < 0.99' \ imputed.vcf.gz -Oz -o imputed_filtered.vcf.gz
pythonimport pandas as pd import matplotlib.pyplot as plt # Load INFO scores info = pd.read_csv('info_scores.txt', sep='\t', names=['CHR', 'POS', 'ID', 'REF', 'ALT', 'R2', 'AF']) # Distribution plot fig, axes = plt.subplots(1, 3, figsize=(15, 4)) # Overall distribution axes[0].hist(info['R2'], bins=50, edgecolor='black') axes[0].axvline(0.3, color='red', linestyle='--', label='Threshold 0.3') axes[0].set_xlabel('INFO Score (R2)') axes[0].set_ylabel('Count') axes[0].set_title('INFO Score Distribution') axes[0].legend() # R2 by MAF info['MAF'] = info['AF'].apply(lambda x: min(x, 1-x)) info['MAF_bin'] = pd.cut(info['MAF'], bins=[0, 0.01, 0.05, 0.1, 0.5]) info.boxplot(column='R2', by='MAF_bin', ax=axes[1]) axes[1].set_xlabel('MAF Bin') axes[1].set_ylabel('INFO Score') axes[1].set_title('INFO by MAF') # Scatter axes[2].scatter(info['MAF'], info['R2'], alpha=0.1, s=1) axes[2].set_xlabel('Minor Allele Frequency') axes[2].set_ylabel('INFO Score') axes[2].set_title('INFO vs MAF') plt.tight_layout() plt.savefig('imputation_qc.png', dpi=150)
bash# Count variants by quality bcftools query -f '%INFO/DR2\n' imputed.vcf.gz | \ awk '{ if ($1 >= 0.8) high++; else if ($1 >= 0.3) med++; else low++ } END { print "High quality (R2>=0.8):", high print "Medium quality (0.3<=R2<0.8):", med print "Low quality (R2<0.3):", low }' # Variants passing filter echo "Total variants: $(bcftools view -H imputed.vcf.gz | wc -l)" echo "Passing R2>0.3: $(bcftools view -i 'INFO/DR2>0.3' imputed.vcf.gz -H | wc -l)"
bash# Extract typed variants from imputed file bcftools view -i 'INFO/TYPED=1' imputed.vcf.gz -Oz -o typed.vcf.gz # Compare imputed vs original genotypes bcftools gtcheck -g original.vcf.gz typed.vcf.gz > concordance.txt # Parse concordance grep "^CN" concordance.txt
pythonimport pandas as pd import numpy as np def imputation_qc_report(info_file, output_prefix): '''Generate comprehensive imputation QC report.''' info = pd.read_csv(info_file, sep='\t', names=['CHR', 'POS', 'ID', 'REF', 'ALT', 'R2', 'AF']) # Calculate MAF info['MAF'] = info['AF'].apply(lambda x: min(x, 1-x)) # Basic statistics stats = { 'total_variants': len(info), 'mean_r2': info['R2'].mean(), 'median_r2': info['R2'].median(), 'variants_r2_03': (info['R2'] >= 0.3).sum(), 'variants_r2_08': (info['R2'] >= 0.8).sum(), 'pct_r2_03': 100 * (info['R2'] >= 0.3).mean(), 'pct_r2_08': 100 * (info['R2'] >= 0.8).mean(), } # By MAF bin maf_bins = [(0, 0.001), (0.001, 0.01), (0.01, 0.05), (0.05, 0.5)] for low, high in maf_bins: mask = (info['MAF'] >= low) & (info['MAF'] < high) stats[f'mean_r2_maf_{low}_{high}'] = info.loc[mask, 'R2'].mean() stats[f'n_variants_maf_{low}_{high}'] = mask.sum() # By chromosome chr_stats = info.groupby('CHR').agg({ 'R2': ['mean', 'count'], 'MAF': 'mean' }).round(3) # Write reports with open(f'{output_prefix}_summary.txt', 'w') as f: for k, v in stats.items(): f.write(f'{k}: {v}\n') chr_stats.to_csv(f'{output_prefix}_by_chrom.txt', sep='\t') return stats, chr_stats stats, chr_stats = imputation_qc_report('info_scores.txt', 'imputation_qc')
pythondef compare_imputations(vcf1, vcf2, output): '''Compare INFO scores between two imputation runs.''' import subprocess # Extract INFO from both cmd1 = f"bcftools query -f '%CHROM:%POS\t%INFO/DR2\n' {vcf1}" cmd2 = f"bcftools query -f '%CHROM:%POS\t%INFO/DR2\n' {vcf2}" info1 = pd.read_csv(subprocess.Popen(cmd1, shell=True, stdout=subprocess.PIPE).stdout, sep='\t', names=['ID', 'R2_1']) info2 = pd.read_csv(subprocess.Popen(cmd2, shell=True, stdout=subprocess.PIPE).stdout, sep='\t', names=['ID', 'R2_2']) merged = info1.merge(info2, on='ID') merged['R2_diff'] = merged['R2_1'] - merged['R2_2'] # Correlation corr = merged['R2_1'].corr(merged['R2_2']) print(f'Correlation between R2 scores: {corr:.4f}') return merged
bash# Calculate HWE p-values (PLINK2) plink2 --vcf imputed.vcf.gz \ --hardy \ --out hwe_check # Filter extreme HWE deviations plink2 --vcf imputed.vcf.gz \ --hwe 1e-6 \ --make-pgen \ --out imputed_hwe_filtered
bash#!/bin/bash # Complete post-imputation QC INPUT=$1 OUTPUT=$2 # 1. Filter by INFO score bcftools view -i 'INFO/DR2 > 0.3' $INPUT -Oz -o ${OUTPUT}_r2.vcf.gz # 2. Filter by MAF bcftools view -i 'INFO/AF > 0.01 && INFO/AF < 0.99' \ ${OUTPUT}_r2.vcf.gz -Oz -o ${OUTPUT}_maf.vcf.gz # 3. Remove duplicates bcftools norm -d all ${OUTPUT}_maf.vcf.gz -Oz -o ${OUTPUT}_nodup.vcf.gz # 4. Index bcftools index ${OUTPUT}_nodup.vcf.gz # 5. Final stats echo "Input variants: $(bcftools view -H $INPUT | wc -l)" echo "After R2 filter: $(bcftools view -H ${OUTPUT}_r2.vcf.gz | wc -l)" echo "After MAF filter: $(bcftools view -H ${OUTPUT}_maf.vcf.gz | wc -l)" echo "Final variants: $(bcftools view -H ${OUTPUT}_nodup.vcf.gz | wc -l)"
| Application | R2 Threshold | MAF Threshold | Notes | |-------------|--------------|---------------|-------| | GWAS discovery | 0.3 | 0.01 | Standard | | GWAS replication | 0.5 | 0.01 | More stringent | | Fine-mapping | 0.8 | 0.001 | High accuracy needed | | Polygenic scores | 0.9 | 0.01 | Very high accuracy | | Meta-analysis | 0.5 | Study-specific | Match across studies |
<!-- AUTHOR_SIGNATURE: 9a7f3c2e-MD-BABU-MIA-2026-MSSM-SECURE -->
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 24,959 | 11,927 | -52% | 1 | 1 | 0% | 4,248 | 5,365 | +26% | 0 | 0 | — |
case-10 | pass→pass | 8,757 | 2,709 | -69% | 1 | 1 | 0% | 1,623 | 3,156 | +94% | 0 | 0 | — |
case-11 | fail→pass | 16,951 | 3,204 | -81% | 1 | 1 | 0% | 2,090 | 3,247 | +55% | 0 | 0 | — |
case-02 | fail→pass | 41,195 | 15,185 | -63% | 1 | 1 | 0% | 4,206 | 6,116 | +45% | 0 | 0 | — |
case-03 | fail→pass | 9,924 | 3,617 | -64% | 1 | 1 | 0% | 1,907 | 3,456 | +81% | 0 | 0 | — |
case-04 | pass→pass | 6,257 | 3,389 | -46% | 1 | 1 | 0% | 1,348 | 3,376 | +150% | 0 | 0 | — |
case-05 | pass→pass | 8,307 | 6,258 | -25% | 1 | 1 | 0% | 1,525 | 3,245 | +113% | 0 | 0 | — |
case-06 | pass→pass | 4,171 | 3,115 | -25% | 1 | 1 | 0% | 677 | 3,200 | +373% | 0 | 0 | — |
case-07 | fail→pass | 9,809 | 2,895 | -70% | 1 | 1 | 0% | 1,913 | 3,243 | +70% | 0 | 0 | — |
case-08 | fail→pass | 13,180 | 6,338 | -52% | 1 | 1 | 0% | 2,730 | 3,956 | +45% | 0 | 0 | — |
case-09 | pass→pass | 13,052 | 9,632 | -26% | 1 | 1 | 0% | 3,049 | 4,844 | +59% | 0 | 0 | — |
case-12 | fail→pass | 9,170 | 3,143 | -66% | 1 | 1 | 0% | 1,614 | 3,161 | +96% | 0 | 0 | — |
case-13 | fail→fail | 30,787 | 7,526 | -76% | 1 | 1 | 0% | 1,989 | 4,137 | +108% | 0 | 0 | — |
case-14 | fail→pass | 9,199 | 3,189 | -65% | 1 | 1 | 0% | 1,739 | 3,335 | +92% | 0 | 0 | — |
case-15 | pass→pass | 10,629 | 2,691 | -75% | 1 | 1 | 0% | 1,915 | 3,226 | +68% | 0 | 0 | — |
case-16 | pass→pass | 10,447 | 6,396 | -39% | 1 | 1 | 0% | 1,800 | 3,864 | +115% | 0 | 0 | — |
case-17 | fail→pass | 14,217 | 9,941 | -30% | 1 | 1 | 0% | 2,629 | 4,732 | +80% | 0 | 0 | — |
case-18 | fail→pass | 14,863 | 2,743 | -82% | 1 | 1 | 0% | 2,690 | 3,154 | +17% | 0 | 0 | — |
case-19 | fail→pass | 14,292 | 11,564 | -19% | 1 | 1 | 0% | 2,736 | 4,739 | +73% | 0 | 0 | — |
case-20 | pass→pass | 8,130 | 6,461 | -21% | 1 | 1 | 0% | 1,676 | 3,893 | +132% | 0 | 0 | — |
case-21 | pass→pass | 11,718 | 8,723 | -26% | 1 | 1 | 0% | 2,232 | 4,413 | +98% | 0 | 0 | — |
case-22 | fail→pass | 9,477 | 5,446 | -43% | 1 | 1 | 0% | 1,910 | 3,742 | +96% | 0 | 0 | — |
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. 22 cases were attempted. The headline lift of +55 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 7/26/2026 | +27% |
Other measured skills in the registry, with their headline benchmark lift.