群体遗传结构分析 — ADMIXTURE¶
一句话概述:ADMIXTURE 通过最大似然法将每个个体的基因组拆分成来自 K 个祖先群体的比例,画出经典的"彩色柱状图"揭示群体混合历史。
核心知识点速查表¶
| 概念 | 白话解释 |
|---|---|
| ADMIXTURE | 群体结构分析软件,类似 STRUCTURE 但速度快几十倍 |
| K 值 | 假设的祖先群体数目(K=2 表示假设有 2 个祖先群体) |
| CV error | 交叉验证误差,越小说明 K 值越合理 |
| LD pruning | 去除连锁不平衡的 SNP,保证 SNP 独立 |
| Q matrix | 每个个体属于各祖先群体的比例矩阵 |
| PCA | 主成分分析,与 ADMIXTURE 互补验证群体结构 |
| pong | ADMIXTURE 结果可视化工具,自动对齐多次运行 |
| PopCluster | 2025 年新工具,支持百万级样本并行分析 |
一、什么是群体遗传结构?(白话版)¶
想象你面前有一堆颜料珠子(每个人的基因组),这些珠子其实是几种原色混合出来的。ADMIXTURE 就是帮你找出"有几种原色"(K 值)以及"每颗珠子里各原色的比例"(祖先成分)。
比如:小明的基因组 = 60% 东亚祖先 + 30% 东南亚祖先 + 10% 南亚祖先。
二、分析流程¶
整体流程图¶
第 1 步:环境准备与安装¶
# 安装 ADMIXTURE(下载二进制文件)
wget https://dalexander.github.io/admixture/binaries/admixture_linux-1.3.0.tar.gz # 下载压缩包
tar -xzf admixture_linux-1.3.0.tar.gz # 解压
chmod +x admixture # 添加执行权限
export PATH=$PATH:$(pwd) # 添加到环境变量
# 安装 PLINK(用于格式转换和LD pruning)
conda install -c bioconda plink # 通过conda安装plink
第 2 步:数据格式转换(VCF → PLINK bed)¶
# 将 VCF 转换为 PLINK 的 bed/bim/fam 格式
plink --vcf input.vcf.gz \ # 输入VCF文件
--make-bed \ # 生成bed格式
--allow-extra-chr \ # 允许非标准染色体名
--set-missing-var-ids @:# \ # 给没有ID的SNP设置ID(染色体:位置)
--out mydata # 输出文件前缀
# 输出文件说明:
# mydata.bed — 基因型的二进制文件(核心数据)
# mydata.bim — 每个SNP的信息(染色体、位置、等位基因)
# mydata.fam — 每个样本的信息(家系、性别、表型)
第 3 步:LD pruning(去掉连锁的 SNP)¶
# 为什么要 LD pruning?
# ADMIXTURE 假设 SNP 之间是独立的,连锁的 SNP 会让结果偏差
# 第一步:标记要保留/删除的 SNP
plink --bfile mydata \ # 输入bed文件
--indep-pairwise 50 10 0.1 \ # 窗口50个SNP,步长10,r²阈值0.1
--out mydata_pruned # 输出前缀
# 第二步:只保留独立的 SNP
plink --bfile mydata \ # 输入原始bed文件
--extract mydata_pruned.prune.in \ # 只保留通过筛选的SNP
--make-bed \ # 生成新的bed文件
--out mydata_ldpruned # 输出前缀
# 检查保留了多少 SNP
wc -l mydata_ldpruned.bim # 统计SNP数量
# 一般保留几万到几十万个SNP就够了
第 4 步:运行 ADMIXTURE(遍历 K 值)¶
# 对 K=1 到 K=10 分别运行 ADMIXTURE
for K in $(seq 1 10); do
admixture --cv \ # 启用交叉验证(计算CV error)
mydata_ldpruned.bed \ # 输入文件
$K \ # 当前K值
-j4 \ # 使用4个线程
| tee log_K${K}.out # 同时输出到屏幕和日志文件
done
# 输出文件说明(以K=3为例):
# mydata_ldpruned.3.Q — Q矩阵,每行是一个个体,3列分别是3个祖先群体的比例
# mydata_ldpruned.3.P — P矩阵,每行是一个SNP在各祖先群体中的等位基因频率
第 5 步:选择最佳 K 值¶
# 提取所有K值的CV error
grep -h "CV" log_K*.out | awk '{print $3,$4}' > cv_errors.txt
# 输出格式:(K=1): 0.54321
# 输出格式:(K=2): 0.43210
# ...
# 用R画CV error曲线
cat << 'EOF' > plot_cv.R
# 读取CV error数据
cv <- read.table("cv_errors.txt") # 读取文件
cv$K <- as.numeric(gsub("[^0-9]", "", cv$V1)) # 提取K值
cv$error <- as.numeric(cv$V2) # 提取CV error
# 画折线图
pdf("cv_error_plot.pdf", width=8, height=5) # 保存为PDF
plot(cv$K, cv$error, # X轴K值,Y轴CV error
type="b", # 画线+点
xlab="K (Number of ancestral populations)", # X轴标签
ylab="Cross-validation error", # Y轴标签
main="ADMIXTURE CV Error", # 标题
pch=16, col="steelblue") # 点的样式和颜色
abline(v=cv$K[which.min(cv$error)], # 在最小CV error处画竖线
col="red", lty=2) # 红色虚线
dev.off() # 关闭PDF设备
EOF
Rscript plot_cv.R # 执行R脚本
# 最小 CV error 对应的 K 就是"最佳K"
# 但注意:如果多个K的CV error很接近,应该报告一个范围而不是单个K
第 6 步:可视化 ADMIXTURE 结果¶
# 方法1:用R画经典的ADMIXTURE柱状图
cat << 'EOF' > plot_admixture.R
# 读取最佳K的Q矩阵(假设K=3)
Q <- read.table("mydata_ldpruned.3.Q") # 每行一个个体,每列一个祖先群体
fam <- read.table("mydata_ldpruned.fam") # 读取样本信息
# 读取群体标签(需要自己准备)
# pop_info.txt 格式:样本ID 群体标签
pop <- read.table("pop_info.txt", header=FALSE) # 读取群体信息
# 按群体排序(让同一群体的个体排在一起)
ord <- order(pop$V2) # 按群体名排序
Q_sorted <- Q[ord, ] # 排序Q矩阵
pop_sorted <- pop[ord, ] # 排序群体信息
# 画柱状图
pdf("admixture_K3.pdf", width=15, height=5) # 创建宽PDF
barplot(t(as.matrix(Q_sorted)), # 转置后画堆叠柱状图
col=c("#E41A1C","#377EB8","#4DAF4A"), # 三种颜色对应三个祖先
border=NA, # 不画柱子边框
space=0, # 柱子之间无间距
ylab="Ancestry proportion", # Y轴标签
main="ADMIXTURE K=3") # 标题
# 在底部标注群体名
# 用 tapply 计算每个群体的中间位置
mid <- tapply(1:nrow(Q_sorted), pop_sorted$V2, mean) # 每个群体的中间位置
axis(1, at=mid, labels=names(mid), las=2, cex.axis=0.7) # 添加X轴标签
dev.off()
EOF
Rscript plot_admixture.R # 执行画图
# 方法2:用 pong 进行更专业的可视化(推荐)
# pong 可以自动对齐多次运行结果,避免颜色翻转问题
pip install pong # 安装pong
# 准备pong需要的文件列表(filemap)
# 格式:K值 运行编号 Q文件路径
echo "3 1 mydata_ldpruned.3.Q" > pong_filemap.txt
# 运行pong
pong -m pong_filemap.txt \ # 输入filemap
-i pop_info.txt \ # 群体标签文件
-n pop_order.txt # 群体排列顺序(可选)
# pong 会启动一个网页服务器,浏览器打开即可查看交互式结果
三、进阶操作¶
3.1 多次运行取稳定结果¶
# ADMIXTURE 使用随机种子,每次运行结果可能略有不同
# 建议每个K值运行5-10次,检查结果稳定性
for K in $(seq 1 10); do
for rep in $(seq 1 5); do
admixture --cv \
-s ${rep}${K} \ # 设置不同的随机种子
mydata_ldpruned.bed \
$K \
-j4
# 重命名输出文件,避免覆盖
mv mydata_ldpruned.${K}.Q mydata_ldpruned.K${K}.rep${rep}.Q # 重命名Q矩阵
mv mydata_ldpruned.${K}.P mydata_ldpruned.K${K}.rep${rep}.P # 重命名P矩阵
done
done
3.2 有监督模式(supervised mode)¶
# 如果你知道部分个体的群体归属(参考群体),可以用有监督模式
# 在 .fam 文件的第6列(表型列)标注群体编号
# 已知群体标为 1, 2, 3...;未知个体标为 -
admixture --supervised \ # 启用有监督模式
mydata_ldpruned.bed \ # 输入文件
3 # K值等于已知群体数
3.3 evaladmix 评估拟合质量¶
# evaladmix 检查 ADMIXTURE 结果是否很好地拟合了数据
# 如果某些群体对之间的残差很大,说明模型拟合不好
# 安装 evaladmix
git clone https://github.com/GenisGE/evalAdmix.git # 克隆仓库
cd evalAdmix && make # 编译
# 运行评估
./evalAdmix -plink mydata_ldpruned \ # 输入PLINK文件前缀
-fname mydata_ldpruned.3.P \ # P矩阵
-qname mydata_ldpruned.3.Q \ # Q矩阵
-o evaladmix_K3.corres # 输出相关性残差矩阵
四、常见报错与解决¶
| 报错信息 | 原因 | 解决方法 |
|---|---|---|
Error: Invalid bed file | bed/bim/fam 文件不匹配 | 重新用 plink --make-bed 生成 |
SNPs with unknown positions | bim 文件中有 0 染色体 | 用 --allow-extra-chr 参数 |
Segmentation fault | 内存不足或数据量太大 | 先做 LD pruning 减少 SNP 数量 |
NaN in Q matrix | SNP 太少或样本太少 | 增加 SNP 数量,检查数据质量 |
| CV error 在 K 增大时持续下降 | 样本结构复杂或数据噪声 | 结合 PCA 和生物学知识选择 K |
| 多次运行结果不一致 | 随机种子不同导致 | 多次运行 + 用 pong/CLUMPAK 对齐 |
Too few SNPs | LD pruning 后 SNP 太少 | 放宽 r² 阈值(如 0.1→0.2) |
五、面试高频问题¶
Q1:ADMIXTURE 和 STRUCTURE 有什么区别?
ADMIXTURE 用最大似然法,STRUCTURE 用贝叶斯 MCMC。ADMIXTURE 快几十倍,适合大数据;STRUCTURE 可以处理小数据但非常慢。结果解读方式类似。
Q2:怎么选择最佳 K 值?
看 CV error 最低点。但注意:(1) 如果多个 K 的 CV error 接近,报告范围而非单个 K;(2) K 值不等于"真实群体数",它受抽样、LD 结构、缺失值影响;(3) 结合 PCA、D-statistics 等交叉验证。
Q3:为什么要做 LD pruning?
ADMIXTURE 假设 SNP 之间独立。如果不做 LD pruning,高度连锁的 SNP 会让某些基因组区域的权重过大,导致结果偏向这些区域的结构信号。
Q4:ADMIXTURE 能处理低深度测序数据吗?
标准 ADMIXTURE 需要确定的基因型(hard call)。低深度数据应该用 ngsAdmix 或 PCAngsd,它们基于基因型似然值(genotype likelihoods)而不是确定基因型。
六、速查表¶
# === ADMIXTURE 完整流程速查 ===
# 1. VCF → PLINK
plink --vcf input.vcf.gz --make-bed --allow-extra-chr --out mydata
# 2. LD pruning
plink --bfile mydata --indep-pairwise 50 10 0.1 --out pruned
plink --bfile mydata --extract pruned.prune.in --make-bed --out data_ld
# 3. 运行 ADMIXTURE(K=1~10)
for K in $(seq 1 10); do admixture --cv data_ld.bed $K -j4 | tee log_K${K}.out; done
# 4. 提取 CV error
grep "CV" log_K*.out
# 5. 可视化
# R: barplot(t(Q), col=colors, border=NA, space=0)
# pong: pong -m filemap.txt -i pop_info.txt
参考资料:ADMIXTURE v1.3.0 官方文档、Speciation Genomics Tutorial、PopCluster (2025, Molecular Ecology Resources)、CD Genomics Workflow Guide