Explaining what a qpAdm report contains is one thing; reading an actual one, line by line, is another. This post walks through an illustrative report for one era in the format an Ancestrify qpAdm analysis publishes. Every figure below is invented to be internally consistent and to clear the publish bar; it is not a customer's result. The interactive version, with example data, is on the demo.
The era is Hunter-Gatherer and Neolithic Farmer. The target is a living person whose raw file merged to about 214,000 markers against AADR v66. The report's Reading view is where all of this lives; the Atlas view shows the same model as a map and a story.
The model line#
Era: Hunter-Gatherer and Neolithic Farmer
Model: Anatolia_N + Yamnaya_Samara + WHG
Target: <your sample> Panel: AADR v66 Run: 7c1e... Date: 2026-08-30
Three sources. The order is the order the analyst listed them, not a ranking. The panel version is stated because a model is only reproducible against the panel it was run on. The run id is the handle for the exact ADMIXTOOLS 2 run behind the numbers.
What the line does not say: it makes no claim of descent from these three populations. Each is a sampled proxy for an unsampled ancestral population, chosen because it is the closest available stand-in in time and place.
The weights table#
| # | Source | Panel label | n | Share | Weight | SE | Z | 95% CI |
|---|---|---|---|---|---|---|---|---|
| 01 | Anatolian Neolithic Farmer | Anatolia_N | 24 | 48.7% | 0.487 | 0.028 | 17.4 | 0.432 to 0.542 |
| 02 | Western Steppe Herder | Yamnaya_Samara | 10 | 33.4% | 0.334 | 0.031 | 10.8 | 0.273 to 0.395 |
| 03 | Western Hunter-Gatherer | WHG | 8 | 17.9% | 0.179 | 0.024 | 7.46 | 0.132 to 0.226 |
Read the columns right to left, because the rightmost ones decide whether the leftmost mean anything.
95% CI. The weight plus or minus 1.96 standard errors. The steppe share is not "33%"; it is "somewhere between 27% and 40%". None of the three intervals reaches zero, which is what the publish bar is designed to guarantee.
Z. Weight divided by SE. Every source sits well above 3; the smallest, WHG at 7.46, is seven and a half standard errors from nothing. A source at Z = 1.5 would be a source the data cannot certify, whatever its share said.
SE. All three are below 0.10, and in fact below 0.035, which is what a file of this coverage supports. A sparser file would carry SEs of 0.06 to 0.12 on the same model.
Weight and share. The same number twice: raw proportion, and rounded percentage. They sum to 1.000 because qpAdm constrains them to.
n. How many individuals the panel population contains. Eight WHG individuals is a thinner reference than 24 Anatolian farmers, and the record says so.
Panel label. The exact AADR population, beside the catalog name, so the model can be reproduced and so a curated name never hides which samples were used. The labels are explained in the AADR explainer, and each catalog population has a page in the ancestry directory.
The fit block#
p-value: 0.164 chi-square: 11.72 dof: 8 f4 rank: 2
min SNPs per f4: 121,880 merged SNPs: 214,306 jackknife blocks: 711
p = 0.164. Above 0.05, so the model is admissible: the data do not contradict it. Not "true", not "confirmed". A model at p = 0.6 against the same right set would not be stronger evidence; a model at p = 0.6 against a weaker right set would be weaker.
Chi-square 11.72 on 8 degrees of freedom. The p-value is derived from these. Degrees of freedom for k sources fitted at rank r against n right populations are (k minus r) times (n minus 1 minus r): three sources at rank 2 against eleven outgroups gives 1 times 8 = 8. A chi-square of 11.7 on 8 degrees of freedom is close to what chance alone produces, which is what p = 0.164 says in one number.
f4 rank 2. Three sources require rank k minus 1 = 2. The rank test below shows what happened at ranks 1 and 0.
Min SNPs per f4: 121,880. Each f4-statistic is computed on the markers available for that particular quartet, which differ because ancient samples have gaps in different places. The record reports the smallest count, the contrast with the least data behind it.
Merged SNPs: 214,306. The coverage of the file against the panel. This number, not the tier, sets the floor on every SE in the table.
Jackknife blocks: 711. The genome cut into blocks of about 5 centimorgans, the model refitted leaving each out in turn, and the spread of those refits is the SE. Printed so a reader can see the errors were computed the standard way.
The right set#
| # | Right population | n |
|---|---|---|
| 1 | Mbuti.DG | 4 |
| 2 | Ust_Ishim.DG | 1 |
| 3 | Kostenki14 | 1 |
| 4 | MA1 | 1 |
| 5 | Han.DG | 4 |
| 6 | Papuan.DG | 14 |
| 7 | Onge.DG | 2 |
| 8 | Karitiana.DG | 3 |
| 9 | Iran_N | 5 |
| 10 | Levant_N | 12 |
| 11 | EHG | 3 |
Eleven populations, listed in run order, each with its sample count. The first eight are the classic distant outgroups; the last three are era-appropriate additions that let the test tell a source with Iranian-related, Levantine-related or Eastern hunter-gatherer ancestry from one without. Mbuti.DG is first because the first right population is the base every f4-statistic is taken against.
This is the section that makes the model checkable. A p-value is only meaningful against the outgroups it was computed with; publish the weights without this list and nobody can evaluate them. The reasoning behind the choice is in How to choose sources and right populations.
The nested-model table#
| Sources removed | Anatolia_N | Yamnaya_Samara | WHG | p-value | Feasible |
|---|---|---|---|---|---|
| none (full model) | 0.487 | 0.334 | 0.179 | 0.164 | yes |
| WHG | 0.612 | 0.388 | dropped | 0.0004 | yes |
| Yamnaya_Samara | 0.803 | dropped | 0.197 | 1.1e-19 | yes |
| Anatolia_N | dropped | 0.417 | 0.583 | 6.3e-22 | yes |
Every simpler model is rejected, which is the evidence that each source earns its place. Remove WHG and the model fails at p = 0.0004; remove either of the other two and it fails by an enormous margin. Had any row passed, that row's model is the one that should have been published, and the analyst treats the table exactly that way before proposing a model.
"Feasible" means every refitted weight stayed between zero and one. A "no" in that column is a model that could only fit by pushing a weight past 100%, which is the method's way of saying the removed source was carrying something real.
The rank test#
| Rank | Chi-square | dof | p-value |
|---|---|---|---|
| 2 | 11.72 | 8 | 0.164 |
| 1 | 208.4 | 18 | 2.1e-34 |
| 0 | 2911.0 | 30 | 0 |
The same question asked differently. Rank 2 is the published three-source model. Rank 1 would be any two-source model, and it fails at p = 10 to the minus 34. Rank 0, a single source, is off the scale. Three sources was the minimum, not a choice.
Warnings#
Note: SNP counts vary across f4 contrasts; the reported count is the minimum.
ADMIXTOOLS 2 prints its own diagnostics and the record keeps them verbatim. This one appears on almost every honest run and is the point explained under "min SNPs per f4" above. A warning present on every run is information, not a defect.
Reading your model#
Beside the record sits a written paragraph from the analyst who built the model. In this illustrative report it would read something like:
Your genome in this era resolves into three sources: a Neolithic farmer population sampled in Anatolia around 6500 BC, an early Bronze Age herder population from the Samara steppe, and a Mesolithic hunter-gatherer population of western Europe. Roughly half of the model is the farmer source, a third the steppe source and the remainder the hunter-gatherer source, which is the pattern seen across much of central and western Europe today. The two-source model without the hunter-gatherer source was rejected, so that share is required, not decorative. None of these labels is a place where anyone in your family lived: each is a reference group the model tests against, standing in for an ancestral population that was never sampled directly.
Every explanation Ancestrify publishes carries that last sentence in some form, because a table of weights answers "what" without answering "why", and a label alone invites the wrong reading.
The Reading view and the plain-text record#
Everything above is on screen in the report's Reading view and downloads as a plain-text file, one era or all eras, from the "Keep the record" bar, free at every tier. There is no PDF. The file is the same figures in the same order, with a header (target, era, panel, SNP counts, run id, date), the explanation, the fit block, the weights table with intervals, the right set with counts, the nested table, the rank test and the warnings. It is plain text so that it can be pasted to another analyst or kept beside the EIGENSTRAT bundle. Every field is defined in The model record explained.
What you can check yourself#
Without running anything:
- Do the weights sum to 1.000? 0.487 + 0.334 + 0.179 = 1.000.
- Is each Z the weight over its SE? 0.487 / 0.028 = 17.4. 0.334 / 0.031 = 10.8. 0.179 / 0.024 = 7.46.
- Is each CI the weight plus or minus 1.96 SE? 0.487 minus 1.96 times 0.028 = 0.432.
- Do the degrees of freedom match the counts? (3 minus 2) times (11 minus 1 minus 2) = 8.
- Does the rank-2 row of the rank test match the fit block? 11.72, 8, 0.164. It should, because it is the same fit.
- Is any right population a parent or close relative of a source? EHG is on the right and Yamnaya_Samara on the left. EHG is a component of Yamnaya, which is a real concern in the general case; the analyst's explanation should say why it was kept, typically because the model was also run with EHG removed and the weights did not move. If a report does not address it, ask.
- Does every source clear the bar? p > 0.05, every |Z| > 3, every SE < 0.10. Yes, yes, yes.
Reproducing it#
Two routes. The Model Lab unlock (10 EUR, one time) lets you rerun this exact model, or any variation of it, on your own merged sample inside the report, up to 100 runs per rolling 24 hours. The same unlock includes the EIGENSTRAT bundle (.geno/.snp/.ind) the report was computed from, for running ADMIXTOOLS 2 on your own machine:
library(admixtools)
f2 <- f2_from_geno("path/to/bundle/prefix")
left <- c("Target", "Anatolia_N", "Yamnaya_Samara", "WHG")
right <- c("Mbuti.DG", "Ust_Ishim.DG", "Kostenki14", "MA1", "Han.DG",
"Papuan.DG", "Onge.DG", "Karitiana.DG", "Iran_N", "Levant_N", "EHG")
res <- qpadm(f2, left, right, target = "Target")
res$weights
res$rankdrop
res$popdropweights is the table above, rankdrop the rank test, popdrop the nested-model table. The
figures should agree with the record to jackknife precision. The walkthrough is in
Run your own qpAdm models. Before buying anything, the free
AdmixTools 2 Lab runs the same function over the public panel.
A report you can read line by line and reproduce is the product. The qpAdm analysis starts at 29.99 EUR, every model is built and checked by a person, and the bar it is published against is the same at every tier.
References#
- Haak, W. et al. (2015). Massive migration from the steppe was a source for Indo-European languages in Europe. Nature, 522, 207 to 211.
- Harney, É., Patterson, N., Reich, D. & Wakeley, J. (2021). Assessing the performance of qpAdm: a statistical tool for studying population admixture. Genetics, 217(4), iyaa045.
- Maier, R. et al. (2023). On the limits of fitting complex models of population history to f-statistics. eLife, 12, e85492.
- Mallick, S. et al. (2024). The Allen Ancient DNA Resource (AADR): a curated compendium of ancient human genomes. Scientific Data, 11, 182.



