Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Detect signatures of natural selection using Fst, Tajima's D, iHS, XP-EHH, and other selection statistics. Calculate population differentiation, test for departures from neutrality, and identify selective sweeps with scikit-allel and vcftools. Use when computing selection signatures like Fst or Tajima's D.
.claude/skills/bio-population-genetics-selection-statistics/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 100% | 50 |
| gemini-3.1-pro-preview | 100% | 1 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 6% | 0% |
| case-02 | ✗→✓ | ▲ Improved | -12% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 61% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 62% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 81% | 0% |
<!--
#
#
-->
Detect natural selection signatures using diversity statistics and extended haplotype homozygosity.
pythonimport allel import numpy as np callset = allel.read_vcf('data.vcf.gz') gt = allel.GenotypeArray(callset['calldata/GT']) pos = callset['variants/POS'] subpops = {'pop1': [0, 1, 2, 3, 4], 'pop2': [5, 6, 7, 8, 9]} ac_subpops = gt.count_alleles_subpops(subpops) num, den = allel.hudson_fst(ac_subpops['pop1'], ac_subpops['pop2']) fst_per_snp = num / den print(f'Mean Fst: {np.nanmean(fst_per_snp):.4f}')
pythonfst_windowed, windows, n_snps = allel.windowed_hudson_fst( pos, ac_subpops['pop1'], ac_subpops['pop2'], size=100000, step=50000) import matplotlib.pyplot as plt plt.figure(figsize=(14, 4)) plt.plot(windows[:, 0], fst_windowed) plt.xlabel('Position') plt.ylabel('Fst') plt.savefig('fst_windows.png')
bash# Calculate Fst between populations vcftools --vcf data.vcf --weir-fst-pop pop1.txt --weir-fst-pop pop2.txt --out fst_result # With window vcftools --vcf data.vcf --weir-fst-pop pop1.txt --weir-fst-pop pop2.txt \ --fst-window-size 100000 --fst-window-step 50000 --out fst_windowed
pythonimport allel import numpy as np callset = allel.read_vcf('data.vcf.gz') gt = allel.GenotypeArray(callset['calldata/GT']) pos = callset['variants/POS'] ac = gt.count_alleles() D, windows, counts = allel.windowed_tajima_d(pos, ac, size=100000, step=50000) plt.figure(figsize=(14, 4)) plt.plot(windows[:, 0], D) plt.axhline(y=0, color='r', linestyle='--') plt.xlabel('Position') plt.ylabel("Tajima's D") plt.savefig('tajima_d.png')
| D Value | Interpretation | |---------|---------------| | D < -2 | Recent selective sweep or population expansion | | D ≈ 0 | Neutral evolution | | D > 2 | Balancing selection or population bottleneck |
bashvcftools --vcf data.vcf --TajimaD 100000 --out tajima # Output: tajima.Tajima.D (CHROM, BIN_START, N_SNPS, TajimaD)
Detects ongoing selective sweeps.
pythonimport allel import numpy as np callset = allel.read_vcf('data.vcf.gz') gt = allel.GenotypeArray(callset['calldata/GT']) pos = callset['variants/POS'] h = gt.to_haplotypes() ac = h.count_alleles() flt = (ac[:, 0] > 1) & (ac[:, 1] > 1) h_flt = h.compress(flt, axis=0) pos_flt = pos[flt] ac_flt = ac.compress(flt, axis=0) ihs = allel.ihs(h_flt, pos_flt, include_edges=True) ihs_std = allel.standardize_by_allele_count(ihs, ac_flt[:, 1]) significant_ihs = np.abs(ihs_std[0]) > 2 print(f'Significant iHS hits: {significant_ihs.sum()}')
pythonimport matplotlib.pyplot as plt plt.figure(figsize=(14, 4)) plt.scatter(pos_flt, ihs_std[0], s=1) plt.axhline(y=2, color='r', linestyle='--') plt.axhline(y=-2, color='r', linestyle='--') plt.xlabel('Position') plt.ylabel('Standardized iHS') plt.savefig('ihs.png')
Detects completed sweeps by comparing populations.
pythonimport allel import numpy as np h = gt.to_haplotypes() h_pop1 = h.take(pop1_hap_idx, axis=1) h_pop2 = h.take(pop2_hap_idx, axis=1) xpehh = allel.xpehh(h_pop1, h_pop2, pos, include_edges=True) significant = np.abs(xpehh) > 2 print(f'Significant XP-EHH hits: {significant.sum()}')
Alternative to iHS, less sensitive to recombination rate variation.
pythonnsl = allel.nsl(h_flt) nsl_std = allel.standardize_by_allele_count(nsl, ac_flt[:, 1])
Detect soft sweeps.
pythonh1, h12, h123, h2_h1 = allel.garud_h(h) h12_windowed = allel.moving_garud_h(h, size=100)
Combine multiple statistics:
pythonimport numpy as np from scipy import stats def composite_score(fst, tajD, ihs_abs): fst_rank = stats.rankdata(fst) / len(fst) tajD_rank = stats.rankdata(-tajD) / len(tajD) # Low Tajima's D ihs_rank = stats.rankdata(ihs_abs) / len(ihs_abs) return (fst_rank + tajD_rank + ihs_rank) / 3 css = composite_score(fst_per_snp, tajD_values, np.abs(ihs_values))
pythonimport allel import numpy as np import matplotlib.pyplot as plt callset = allel.read_vcf('data.vcf.gz') gt = allel.GenotypeArray(callset['calldata/GT']) pos = callset['variants/POS'] ac = gt.count_alleles() flt = ac.is_segregating() & (ac.max_allele() == 1) gt = gt.compress(flt, axis=0) pos = pos[flt] ac = ac.compress(flt, axis=0) window_size = 100000 window_step = 50000 tajD, tajD_windows, _ = allel.windowed_tajima_d(pos, ac, size=window_size, step=window_step) pi, pi_windows, _, _ = allel.windowed_diversity(pos, ac, size=window_size, step=window_step) fig, axes = plt.subplots(2, 1, figsize=(14, 8), sharex=True) axes[0].plot(tajD_windows[:, 0], tajD) axes[0].axhline(0, color='r', linestyle='--') axes[0].set_ylabel("Tajima's D") axes[1].plot(pi_windows[:, 0], pi) axes[1].set_ylabel('Pi') axes[1].set_xlabel('Position') plt.tight_layout() plt.savefig('selection_scan.png', dpi=150)
<!-- 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 | 26,281 | 32,107 | +22% | 1 | 1 | 0% | 5,639 | 5,997 | +6% | 0 | 0 | — |
case-02 | fail→pass | 27,971 | 13,537 | -52% | 1 | 1 | 0% | 6,030 | 5,280 | -12% | 0 | 0 | — |
case-03 | pass→pass | 10,548 | 7,578 | -28% | 1 | 1 | 0% | 2,058 | 3,734 | +81% | 0 | 0 | — |
case-08 | pass→pass | 7,025 | 2,670 | -62% | 1 | 1 | 0% | 1,306 | 2,685 | +106% | 0 | 0 | — |
case-14 | pass→pass | 10,065 | 6,972 | -31% | 1 | 1 | 0% | 1,716 | 3,534 | +106% | 0 | 0 | — |
case-15 | pass→pass | 12,736 | 7,046 | -45% | 1 | 1 | 0% | 2,662 | 3,856 | +45% | 0 | 0 | — |
case-20 | pass→pass | 11,071 | 7,535 | -32% | 1 | 1 | 0% | 2,284 | 3,724 | +63% | 0 | 0 | — |
case-21 | pass→pass | 12,171 | 11,577 | -5% | 1 | 1 | 0% | 2,412 | 4,505 | +87% | 0 | 0 | — |
case-22 | pass→pass | 12,360 | 9,313 | -25% | 1 | 1 | 0% | 2,314 | 3,992 | +73% | 0 | 0 | — |
case-23 | pass→pass | 13,122 | 14,263 | +9% | 1 | 1 | 0% | 2,483 | 4,830 | +95% | 0 | 0 | — |
case-04 | pass→pass | 11,016 | 4,028 | -63% | 1 | 1 | 0% | 1,130 | 3,029 | +168% | 0 | 0 | — |
case-05 | pass→pass | 12,730 | 13,893 | +9% | 1 | 1 | 0% | 2,359 | 4,632 | +96% | 0 | 0 | — |
case-06 | pass→pass | 9,605 | 5,669 | -41% | 1 | 1 | 0% | 1,870 | 3,310 | +77% | 0 | 0 | — |
case-07 | pass→pass | 10,328 | 3,812 | -63% | 1 | 1 | 0% | 1,798 | 2,915 | +62% | 0 | 0 | — |
case-09 | pass→pass | 14,554 | 7,143 | -51% | 1 | 1 | 0% | 3,172 | 3,768 | +19% | 0 | 0 | — |
case-10 | pass→pass | 16,140 | 7,798 | -52% | 1 | 1 | 0% | 3,004 | 3,676 | +22% | 0 | 0 | — |
case-11 | fail→pass | 8,458 | 2,002 | -76% | 1 | 1 | 0% | 1,581 | 2,544 | +61% | 0 | 0 | — |
case-12 | fail→pass | 12,450 | 8,999 | -28% | 1 | 1 | 0% | 2,431 | 3,940 | +62% | 0 | 0 | — |
case-13 | pass→pass | 9,386 | 5,354 | -43% | 1 | 1 | 0% | 1,895 | 3,217 | +70% | 0 | 0 | — |
case-16 | pass→pass | 8,710 | 4,132 | -53% | 1 | 1 | 0% | 1,736 | 2,972 | +71% | 0 | 0 | — |
case-17 | pass→pass | 8,715 | 5,414 | -38% | 1 | 1 | 0% | 1,613 | 3,140 | +95% | 0 | 0 | — |
case-18 | pass→pass | 14,817 | 5,445 | -63% | 1 | 1 | 0% | 2,934 | 3,325 | +13% | 0 | 0 | — |
case-19 | pass→pass | 11,877 | 9,206 | -22% | 1 | 1 | 0% | 2,061 | 3,890 | +89% | 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. 23 cases were attempted. The headline lift of +17 percentage points is the difference between those two pass rates over the 23 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/24/2026 | +36% |
Other measured skills in the registry, with their headline benchmark lift.