Everything qpAdm — the method behind a decade of ancient-DNA papers
and our paid analysis — is free software: the qpadm() function of the ADMIXTOOLS 2 R
package (Maier et al. 2023). This is a working tutorial for running it yourself: the setup, the
calls, the output tables, and — the part no README covers — the arguments and protocol choices
that silently change your results.
If you want the modelling without the pipeline, that exists too: the free AdmixTools 2 Lab runs f-statistics, qpWave and qpAdm against a curated ancient panel in the browser, and the Model Lab does it on your own genome merged into AADR. This post is for the readers who want the R console.
Setup#
ADMIXTOOLS 2 installs from GitHub (uqrmaie1/admixtools); on most systems
devtools::install_github("uqrmaie1/admixtools") plus a compiler is the whole story. Data must
be EIGENSTRAT or PACKEDANCESTRYMAP genotypes — practically, that means the
AADR download, with your own genotypes
merged in if you intend to model yourself. The merge is its
own craft (position intersection, strand hygiene, build discipline);
sanity-check any consumer file first with the free file check.
The two-step workflow#
ADMIXTOOLS 2's speed comes from precomputing f2-statistics once, then reusing them across thousands of models:
library(admixtools)
extract_f2("path/to/geno_prefix", "f2_dir",
pops = my_pops, maxmiss = 0) # once, slow
f2 <- f2_from_precomp("f2_dir") # fast forever after
left <- c("Anatolia_N", "Yamnaya_Samara", "WHG")
right <- c("Mbuti.DG", "Ust_Ishim.DG", "Kostenki14", "MA1",
"Han.DG", "Papuan.DG", "Onge.DG", "Karitiana.DG",
"EHG", "Iran_N", "Levant_N")
qpadm(f2, left, right, target = "MyKit")The right set above is the standard spine plus era contrasts — not a decoration to copy blindly but the half of the model that decides whether it can be tested at all.
The sparse-data exception, immediately: precomputed f2 blocks restrict every statistic to
sites present across all populations. With low-coverage ancients that discards most of the data —
the audit's measured penalty reaches
SE 9.994 versus 0.035 at 90% missingness. The fix is
classic qpAdm's allsnps behaviour, which in ADMIXTOOLS 2 requires skipping the f2 directory and
passing the genotype prefix directly:
qpadm("path/to/geno_prefix", left, right, target = "MyKit",
allsnps = TRUE)Slower, and usually right for real ancient panels. Whichever you choose, hold it constant across every model you intend to compare.
Reading the output#
qpadm() returns the tables a published model record is
built from:
weights— per source:weight,se,z. The three-number reading: a weight whose 2-SE interval covers 0 is a source the data cannot certify; negative weights are a diagnosis, not a nuisance.rankdrop— the rank test: chi-square, dof (= outgroups − sources for the full model) andpper tested rank, plusp_nestedcomparing adjacent ranks.popdrop— every sub-model from dropping sources: its p, weights and afeasibleflag. The discipline it encodes: if a simpler model survives, publish the simpler model.
The arguments that silently change results#
allsnps— see above; the largest single lever on sparse data.fudge_twice = TRUE— applies the numerical ridge a second time, matching classic qpAdm's p-values. Set it when comparing against published numbers; either way, consistently.constrained = TRUE— forces weights non-negative. Never for screening: an unconstrained negative weight is information about a wrong or missing source, and constraining hides it.blgsize— jackknife block size, default 0.05 Morgans (5 cM). The convention behind every published SE; change it only knowingly.boot— bootstrap instead of jackknife resampling. Jackknife SEs are the ones comparable to the published literature.
Every knob above has a fuller treatment — what it does, the measured stakes, when to deviate — in the parameters reference, with the classic-vs-2 guide covering the settings that reproduce published numbers from the original software. And if your own file is not yet merged into the AADR, the data-preparation guide is the missing chapter before this one.
Batch helpers exist (qpadm_rotate(), qpadm_multi()) and they are exactly where the
false-discovery hazard lives: an unstratified rotating screen runs a measured
72–100% FDR. Enumerate to map the space; never let the
enumeration pick the winner.
The discipline the code will not enforce#
qpadm() will happily run models that violate every assumption behind the statistics. The
protocol — from the audit literature, and the difference between output and results:
- Temporal stratification: no source younger than the target. (A modern kit against ancient sources satisfies this automatically.)
- Lowest rank first: one-stream explanations before
two, two before three;
p_nestedas referee. - Never rank surviving models by p — the best p identifies the true model 48% of the time. Margins, feasibility and stability across right-set variations do the choosing.
- Composite feasibility: p above threshold and weights bounded inside (0,1) within error and trailing simpler models rejected — the criteria that cut measured FDR severalfold.
- Report the family, not the winner — state which models also passed and what they agree on; single-winner claims are the overfit the auditors keep finding.
That protocol, applied by hand at roughly four hundred runs per order against one bar (p > 0.05, every |Z| > 3, every SE < 0.10), is the entirety of what the paid analysis adds to the free software you have just installed — plus the merge, the curated panels, and a written model record at the end. Run it yourself, buy it run, or both: the method is the same, which is exactly the point.
Terms used here are defined in the glossary.
References#
- Maier, R. et al. (2023). On the limits of fitting complex models of population history to f-statistics. eLife, 12, e85492. (ADMIXTOOLS 2.)
- Harney, É., Patterson, N., Reich, D. & Wakeley, J. (2021). Assessing the performance of qpAdm. Genetics, 217(4), iyaa045.
- Flegontova, O. et al. (2025). Performance of qpAdm-based screens. Genetics, 230(1), iyaf047.
- ADMIXTOOLS 2 documentation and source: uqrmaie1.github.io/admixtools.



