A field guide to population genetics

Computational population genetics, clearly explained

Posts on genetic data, ancient DNA, and the methods used to study ancestry and human history.

Latest Posts

Latest post

Testing for Admixture with f3-Statistics in AdmixPy

f3-statistics are used to test if populations are admixed or to measure shared genetic drift between two populations relative to an outgroup. This post explains the theory behind admixture f3-statistics and shows how to run admixture f3 tests with AdmixPy. If you want to skip the theoretical part, you can jump to Running admixture f3-statistics in AdmixPy. What is an f3-statistic? For three populations, the statistic is written as: f3(A;B,C)=Ei[(pA,i−pB,i)(pA,i−pC,i)] f_3(A;B,C)=\mathbb{E}_i\left[(p_{A,i}-p_{B,i})(p_{A,i}-p_{C,i})\right] f3​(A;B,C)=Ei​[(pA,i​−pB,i​)(pA,i​−pC,i​)]Here, AAA is in the target position. Populations BBB and CCC are the reference populations. The values pA,ip_{A,i}pA,i​, pB,ip_{B,i}pB,i​, and pC,ip_{C,i}pC,i​ are the allele frequencies in populations AAA, BBB, and CCC, respectively, at SNP iii. The expectation is an average across SNPs. ...

August 7, 2026

Are Higher qpAdm P-Values Better?

Yes. For two qpAdm models with the same target, the same right groups, and the same settings, the model with the higher p-value is the better statistical fit. qpAdm calculates a covariance-weighted discrepancy between the observed and fitted f4-statistics. The p-value reflects how well the model explains the used f4-statistics. A higher p-value means the discrepancy between the observed and fitted values is less unusual under the model. This does not mean that the model with the highest p-value for a target is automatically the best one, because qpAdm results depend on the selected right groups. Uninformative right-groups can lack the power to detect a bad model, while overly restrictive ones can make a plausible model appear to fit badly. Therefore, p-values are more comparable when models for the same target are compared using the same groups and settings. Models with different numbers of sources are also comparable since p-values account for different degrees of freedom. Z-scores can be used to assess whether an additional source is justified. ...

July 30, 2026

Why the Best PCA Fit May Still Be the Wrong Admixture Model

A Vahaduo generated PCA model for Sardinians gives: 82.8% Barcin Neolithic 11.6% Loschbour 5.6% Yamnaya Distance: 3.4303% Ganj Dareh was included in the sources but gets a weight of zero. This seems to imply that Sardinians don’t have any eastern-Farmer related ancestry. When Sardinians are modelled with qpAdm using Barcin Neolithic, Loschbour, Yamnaya, and Ganj Dareh, the model fits well: 68.6% Barcin Neolithic 11.9% Loschbour 10.2% Yamnaya 9.4% Ganj Dareh Neolithic p = 0.769 When Ganj Dareh is dropped, the model fails (p=1.18×10−12p = 1.18 \times 10^{-12}p=1.18×10−12). ...

July 22, 2026

Pairwise f2 Statistics and FST in AdmixPy

This post covers how to run pairwise f2-statistics and FST in AdmixPy. They are simple to interpret, and are also useful computationally. Once f2 blocks have been computed and cached, many downstream analyses can reuse them without repeatedly reading and converting the original genotype data. What does f2 measure? The f2-statistic quantifies allele-frequency differentiation between two populations, AAA and BBB, and is defined as: f2(A,B)=E[(pA−pB)2] f_2(A, B) = E[(p_A - p_B)^2] f2​(A,B)=E[(pA​−pB​)2]where pAp_ApA​ and pBp_BpB​ are the allele frequencies of populations AAA and BBB at a SNP, and the squared allele-frequency differences are averaged across SNPs. ...

June 30, 2026

Introducing AdmixPy: f-statistics, qpAdm, and qpWave in Python

I recently published AdmixPy on GitHub, a fast implementation of f-statistics, qpAdm, and qpWave in Python that runs on Linux, macOS, and Windows. It works directly on the new AADR TGENO distribution format and is notably faster than ADMIXTOOLS 2 and simpler to set up. Supported input formats: EIGENSTRAT (.geno/.snp/.ind), packed AncestryMap (.geno/.snp/.ind), TGENO (.tgeno/.snp/.ind), and SNP-major PLINK binary (.bed/.bim/.fam). AdmixPy is implemented in Python and depends only on NumPy, SciPy, and pandas. Installation is handled through pip, and it should behave the same on every platform. ...

May 21, 2026

Convert Raw DNA Files to EIGENSTRAT for ADMIXTOOLS and Merge with AADR

Commercial raw DNA exports are not provided in the file formats normally used by ADMIXTOOLS, ADMIXTOOLS 2, AADR-based workflows, or PLINK. Files from 23andMe, AncestryDNA, FamilyTreeDNA, MyHeritage, and Living DNA are usually plain-text vendor exports, while downstream workflows often require PLINK PACKEDPED or EIGENSTRAT/PACKEDANCESTRYMAP files. EIGENSTRAT is often used loosely to refer to the .geno/.snp/.ind triplet. Strictly speaking, EIGENSTRAT is the plain-text version of that triplet; PACKEDANCESTRYMAP is the packed binary form of the same three files. ADMIXTOOLS and ADMIXTOOLS 2 work with either, but PACKEDANCESTRYMAP takes far less disk space and loads much faster, which is why it’s the practical default used here. ...

May 15, 2026