614_群体遗传学PCA_ADMIXTURE¶
一句话概述: PCA(主成分分析)和ADMIXTURE是群体遗传学中分析群体结构的两大支柱工具——PCA把高维基因型数据降维成二维散点图直观展示群体分层,ADMIXTURE估算每个个体的祖先成分比例。
核心知识点速查表¶
| 概念 | 白话解释 |
|---|---|
| PCA | 把上万个SNP降维到几个主成分,在二维图上展示个体的遗传关系 |
| ADMIXTURE | 假设有K个祖先群体,估算每个个体来自各群体的比例 |
| K值 | 假设的祖先群体数目 |
| CV error | 交叉验证误差,K值选择的标准(越小越好) |
| LD pruning | 连锁不平衡修剪,去除高度相关的SNP |
| MAF | 最小等位基因频率过滤 |
| Hardy-Weinberg | 哈迪-温伯格平衡检验 |
| Eigenvalue | 特征值,衡量每个主成分解释的变异比例 |
一、数据准备与质控¶
1.1 安装工具¶
# 安装PLINK(数据处理+PCA)
conda install -c bioconda plink # PLINK 1.9
conda install -c bioconda plink2 # PLINK 2.0(更快)
# 安装ADMIXTURE
conda install -c bioconda admixture # ADMIXTURE
# 安装smartpca(EIGENSOFT的PCA工具)
conda install -c bioconda eigensoft # 可选
1.2 VCF转PLINK格式¶
# 将VCF文件转换为PLINK的bed/bim/fam格式
plink --vcf input.vcf.gz \ # 输入VCF
--make-bed \ # 输出二进制格式
--out dataset \ # 输出前缀
--allow-extra-chr # 允许非标准染色体名
1.3 质量控制(QC)¶
# === 第1步:过滤个体(样本水平)===
plink --bfile dataset \
--mind 0.1 \ # 去除缺失率>10%的个体
--make-bed \
--out dataset_qc1
# === 第2步:过滤位点(SNP水平)===
plink --bfile dataset_qc1 \
--geno 0.05 \ # 去除缺失率>5%的SNP
--maf 0.05 \ # 去除MAF<5%的SNP
--hwe 1e-6 \ # 去除严重偏离HWE的SNP
--make-bed \
--out dataset_qc2
# === 第3步:LD修剪(PCA和ADMIXTURE必须做!)===
# 先标记高LD的SNP
plink --bfile dataset_qc2 \
--indep-pairwise 50 5 0.2 \ # 窗口50SNP,步长5,r²>0.2则去除
--out ld_pruned
# 然后只保留独立的SNP
plink --bfile dataset_qc2 \
--extract ld_pruned.prune.in \ # 保留这些SNP
--make-bed \
--out dataset_final
echo "QC前SNP数: $(wc -l < dataset.bim)" # QC前
echo "QC后SNP数: $(wc -l < dataset_final.bim)" # QC后
1.4 去除亲缘关系¶
# 计算个体间的IBS(相同等位基因比例)
plink --bfile dataset_final \
--genome \ # 计算亲缘系数
--out relatedness
# 去除一级亲属(PI_HAT > 0.25)
awk '$10 > 0.25 {print $1, $2}' relatedness.genome > related_individuals.txt
# 手动选择要去除的个体,保留表型信息更完整的那个
二、PCA分析¶
2.1 用PLINK做PCA¶
# === 运行PCA ===
plink --bfile dataset_final \
--pca 20 \ # 计算前20个主成分
--out pca_result # 输出前缀
# 输出文件:
# pca_result.eigenval — 特征值(每个PC解释的变异量)
# pca_result.eigenvec — 特征向量(每个个体在各PC上的坐标)
2.2 R语言可视化PCA¶
library(ggplot2) # 加载ggplot2
# === 读取PCA结果 ===
eigenvec <- read.table("pca_result.eigenvec", header = FALSE) # 读取坐标
eigenval <- read.table("pca_result.eigenval", header = FALSE) # 读取特征值
# 设置列名
colnames(eigenvec) <- c("FID", "IID", paste0("PC", 1:20)) # 命名列
# 读取群体信息
pop_info <- read.table("population_info.txt", header = TRUE) # 群体信息
# 格式:FID IID Population
data <- merge(eigenvec, pop_info, by = c("FID", "IID")) # 合并
# 计算各PC解释的方差比例
pve <- eigenval$V1 / sum(eigenval$V1) * 100 # 方差解释比例(%)
# === 画PCA散点图 ===
ggplot(data, aes(x = PC1, y = PC2, color = Population)) +
geom_point(size = 2, alpha = 0.7) + # 散点
labs(
title = "Population Structure (PCA)",
x = paste0("PC1 (", round(pve[1], 1), "%)"), # 加方差比例
y = paste0("PC2 (", round(pve[2], 1), "%)"),
color = "Population"
) +
theme_minimal() + # 简洁主题
theme(legend.position = "right") # 图例在右边
ggsave("pca_plot.pdf", width = 10, height = 7) # 保存
# === 画特征值碎石图(确定有多少个有意义的PC)===
scree_df <- data.frame(PC = 1:20, PVE = pve[1:20])
ggplot(scree_df, aes(x = PC, y = PVE)) +
geom_bar(stat = "identity", fill = "steelblue") + # 柱状图
geom_line(color = "red") + # 折线
labs(x = "Principal Component", y = "Variance Explained (%)") +
theme_minimal()
ggsave("scree_plot.pdf", width = 8, height = 5) # 保存
三、ADMIXTURE分析¶
3.1 运行ADMIXTURE¶
# === 对不同K值运行ADMIXTURE ===
# K从1到10各跑一次,找最佳K值
for K in $(seq 1 10); do
admixture \
--cv \ # 计算交叉验证误差(关键!)
-j8 \ # 8个线程
dataset_final.bed \ # 输入文件(PLINK格式)
${K} \ # K值
2>&1 | tee log_K${K}.txt # 同时输出到屏幕和文件
done
# === 提取CV error ===
grep -h "CV" log_K*.txt | awk '{print $3, $4}' | \
sed 's/(K=//;s/)://' > cv_errors.txt # 提取CV误差
# 查看结果
cat cv_errors.txt
# 选择CV error最低的K值
3.2 可视化ADMIXTURE结果¶
library(ggplot2) # 加载ggplot2
library(reshape2) # 数据重塑
# === 1. 画CV error图,确定最佳K ===
cv <- read.table("cv_errors.txt", header = FALSE) # 读取CV误差
colnames(cv) <- c("K", "CV_error") # 命名列
ggplot(cv, aes(x = K, y = CV_error)) +
geom_line(color = "blue") + # 折线
geom_point(color = "red", size = 3) + # 点
scale_x_continuous(breaks = 1:10) + # x轴刻度
labs(
title = "Cross-Validation Error",
x = "K (Number of ancestral populations)",
y = "CV Error"
) +
theme_minimal()
ggsave("cv_error_plot.pdf", width = 8, height = 5) # 保存
# === 2. 画ADMIXTURE柱状图(以最佳K=3为例)===
best_K <- 3 # 假设最佳K是3
# 读取Q矩阵
q_matrix <- read.table(
paste0("dataset_final.", best_K, ".Q"), # 读取Q文件
header = FALSE
)
colnames(q_matrix) <- paste0("Ancestry_", 1:best_K) # 命名列
# 添加个体信息
fam <- read.table("dataset_final.fam", header = FALSE) # 读取fam文件
q_matrix$IID <- fam$V2 # 个体ID
q_matrix$Population <- pop_info$Population[match(fam$V2, pop_info$IID)]
# 按群体排序
q_matrix <- q_matrix[order(q_matrix$Population), ]
q_matrix$Index <- 1:nrow(q_matrix) # 添加索引
# 长格式转换
q_long <- melt(q_matrix,
id.vars = c("IID", "Population", "Index"), # 保留的列
variable.name = "Ancestry", # 变量名
value.name = "Proportion" # 值名
)
# 画ADMIXTURE柱状图
ggplot(q_long, aes(x = Index, y = Proportion, fill = Ancestry)) +
geom_bar(stat = "identity", width = 1) + # 堆叠柱状图
facet_grid(~Population, scales = "free_x", space = "free_x") + # 按群体分面
labs(
title = paste0("ADMIXTURE (K=", best_K, ")"),
x = "Individuals",
y = "Ancestry Proportion"
) +
theme_minimal() +
theme(
axis.text.x = element_blank(), # 隐藏x轴文字
axis.ticks.x = element_blank(), # 隐藏x轴刻度
strip.text = element_text(angle = 45, hjust = 1) # 群体标签斜45度
) +
scale_fill_brewer(palette = "Set2") # 配色方案
ggsave("admixture_plot.pdf", width = 14, height = 4) # 保存
四、完整分析管线脚本¶
#!/bin/bash
# 群体结构分析完整流程
INPUT="raw_data" # 原始PLINK文件前缀
OUTPUT="pop_structure" # 输出目录
THREADS=16 # 线程数
mkdir -p ${OUTPUT}
# === QC ===
plink --bfile ${INPUT} \
--mind 0.1 --geno 0.05 --maf 0.05 --hwe 1e-6 \
--make-bed --out ${OUTPUT}/qc
# === LD pruning ===
plink --bfile ${OUTPUT}/qc \
--indep-pairwise 50 5 0.2 \
--out ${OUTPUT}/ld
plink --bfile ${OUTPUT}/qc \
--extract ${OUTPUT}/ld.prune.in \
--make-bed --out ${OUTPUT}/final
# === PCA ===
plink --bfile ${OUTPUT}/final --pca 20 --out ${OUTPUT}/pca
# === ADMIXTURE (K=1~10) ===
cd ${OUTPUT}
for K in $(seq 1 10); do
admixture --cv -j${THREADS} final.bed ${K} 2>&1 | tee log_K${K}.txt
done
grep -h "CV" log_K*.txt > cv_errors.txt
cd ..
echo "分析完成!使用R脚本进行可视化"
五、常见报错与解决¶
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
Error: 0 SNPs remaining | QC过滤太严格 | 放宽MAF或geno阈值 |
Segmentation fault (ADMIXTURE) | 内存不足或数据格式错误 | 检查bed/bim/fam一致性 |
No convergence (ADMIXTURE) | K值太大或数据量不够 | 减小K值或增加样本 |
| PCA结果出现离群点 | 可能是亲属或不同物种样本 | 检查并去除离群个体 |
Duplicate individual | 重复样本 | 检查fam文件去除重复 |
六、面试高频题¶
Q1:PCA和ADMIXTURE有什么区别?¶
答: PCA是无假设的降维方法,把高维SNP数据投影到低维空间,通过散点图直观展示个体聚类关系,不需要预设群体数目。ADMIXTURE是基于模型的方法,假设有K个祖先群体,用最大似然估计每个个体来自各群体的祖先比例。PCA看"全貌",ADMIXTURE看"成分"。两个方法互补使用,PCA帮助判断群体数目,ADMIXTURE量化祖先组成。
Q2:为什么PCA和ADMIXTURE前要做LD pruning?¶
答: LD(连锁不平衡)使得物理位置相近的SNP高度相关,提供的是冗余信息。不做LD pruning会导致:PCA中某些基因组区域被过度代表,扭曲PC的含义;ADMIXTURE的似然面变得更复杂,收敛困难。LD pruning保留独立的SNP(通常r² < 0.2),确保每个SNP提供独立的进化信息。
Q3:如何选择ADMIXTURE的最佳K值?¶
答: 主要用交叉验证(CV)误差:对K从1到10各跑一次ADMIXTURE(加--cv参数),选CV error最低的K值。但要注意:(1) CV error可能没有明显最低点,这时选"拐点"处的K;(2) 多个K值的CV error差异很小时,选较小的K(简约原则);(3) 结合PCA散点图中看到的聚类数目来验证。
Q4:PCA结果如何解读?¶
答: (1) PC1解释最大的遗传变异方向,通常对应最大的群体分化;(2) 同一群体的个体应该聚在一起;(3) 离群点可能是混血个体、亲属或数据错误;(4) 特征值碎石图看有多少个有意义的PC(拐点之前的);(5) 注意PCA对不等样本量敏感——样本多的群体会"拉扯"PC方向。
参考资料:ADMIXTURE: Alexander et al., Genome Res 2009 | PLINK: Purcell et al., AJHG 2007 | speciationgenomics.github.io | Lawson et al., Nat Commun 2018