群体遗传多样性评估:PIC、Nei‘s He与Shannon指数计算全解析
1. 项目概述从“算数”到“洞察”的群体遗传多样性评估在群体遗传学、生态学乃至作物育种的研究中我们常常面对一堆基因型数据——可能是几十个样本在几百个分子标记位点上的基因型。直接看这些“0/1/2”或者“A/C/G/T”的编码就像面对一片数字森林难以把握其全貌。这时我们需要一些精炼的指标将这片森林的“生物多样性”浓缩成几个关键数字。多态信息含量PIC、Nei氏基因多样性指数Nei’s gene diversity, 常写作He或Nei和香农指数Shannon Index,I就是三位最得力的“森林测量员”。PIC衡量的是一个分子标记如一个SNP位点作为遗传图谱工具的信息量有多大它在育种和关联分析中至关重要帮你筛选出那些最能区分不同基因型的“强效”标记。Nei氏多样性指数则聚焦于群体水平的基因多样性它从等位基因频率出发告诉你这个群体在某个位点上潜在的遗传变异有多丰富是评估群体遗传结构、比较不同群体多样性的基石。而香农指数最初来自信息生态学被引入遗传学后它从信息熵的角度度量多样性对稀有等位基因更为敏感能揭示群体中那些“低调”的遗传变异。这三个指标听起来公式各异但其核心思想一脉相承量化“不确定性”或“变异程度”。很多新手甚至有些经验的研究者在面对具体数据时常会困惑公式里的字母具体对应我数据框里的哪一列等位基因频率怎么从基因型数据里算当位点不止两个等位基因时怎么办用R的poppr包还是adegenet用Python自己写循环还是用scikit-allel计算结果怎么整理成发表级的表格这篇内容就是为你彻底解决这些问题。我将以一个模拟的微卫星SSR或简化基因组测序如RAD-seq数据为例假设我们有一个包含3个群体、每个群体10个个体、在5个多态位点上的基因型数据。我们将手把手、一行代码一行代码地从原始基因型编码开始推导出每个指标的完整计算过程并分享在实操中如何避免常见陷阱、如何高效批处理以及如何解读结果背后的生物学意义。无论你是刚开始接触群体遗传分析的研究生还是需要快速复现分析流程的科研人员这篇内容都将是一份可直接“抄作业”的详尽指南。2. 核心概念与计算原理深度拆解在动手敲代码之前我们必须吃透这三个指数的“灵魂”。理解它们为什么被设计出来以及公式中每一个部分的生物学或数学含义这能帮助我们在结果出现异常时快速定位问题而不是做一个“调包侠”。2.1 多态信息含量PIC标记的“分辨力”测评PIC专门用于评估共显性标记如SSR、SNP在遗传连锁分析中的实用性。它的核心思想是对于一个标记位点父母本将其等位基因传递给子代的过程就像一次抽奖。PIC计算的是通过观察子代的基因型我们能够推断出父母本基因型的概率有多大。换句话说它衡量的是这个标记在亲子鉴定或遗传图谱构建中的“信息量”。对于一个有k个等位基因的位点设第i个等位基因的频率为pᵢ其计算公式为PIC 1 - Σ(pᵢ²) - Σ(2pᵢ²pⱼ²)其中第一个求和 Σ 是i从1到k第二个双重求和是i从1到k-1j从i1到k。这个公式可以拆解为三步来理解1代表信息的全集。- Σ(pᵢ²)减去子代基因型为纯合子两个等位基因相同的概率。因为纯合子无法提供亲本哪一方贡献了哪个等位基因的信息例如子代是AA父母可能都是AA或者一方是AA另一方是Aa无法准确推断。- Σ(2pᵢ²pⱼ²)再减去子代基因型为杂合子但无法区分父母本贡献的概率。具体来说是减去父母本基因型组合为“AiAj × AiAj”和“AiAj × AjAi”这种情况的概率即父母本基因型相同因为即使子代是杂合子AiAj我们也无法推断等位基因的来源。注意对于双等位基因位点如大多数SNP设两个等位基因频率为p和q(q1-p)公式可简化为PIC 1 - (p² q²) - 2p²q²。很多文献和软件默认使用这个简化版。如果你的数据是多等位基因的如SSR务必使用完整公式。PIC的取值范围是0到1。通常认为PIC 0.5 的标记具有高信息量0.25 PIC 0.5 为中度信息量PIC 0.25 则信息量较低。在育种中我们倾向于选择PIC高的标记进行图谱构建或关联分析。2.2 Nei氏基因多样性指数Nei’sHe群体的“遗传潜力”评估Nei氏基因多样性也称为期望杂合度Expected Heterozygosity是群体遗传学中最基础、最核心的多样性指标之一。它回答的问题是如果在这个群体中进行随机交配那么在后代中在某个位点上出现杂合子的概率是多少这个“期望”值反映了该位点潜在的遗传变异水平。其计算公式为He 1 - Σ(pᵢ²)其中pᵢ是第i个等位基因的频率求和覆盖所有等位基因。这个公式极其优雅1 减去所有等位基因频率的平方和。平方和 Σ(pᵢ²) 实际上代表了群体中纯合子的期望频率根据哈迪-温伯格平衡。因此He直观地表示了“非纯合子”的比例即杂合子的期望比例。实操心得He计算的是一个位点在一个群体内的多样性。当你有多群体数据时通常会为每个群体在每个位点都计算一个He值。后续可以计算群体平均He用于比较不同群体的遗传多样性。它与PIC公式的一部分相同但意义不同PIC扣除了更多“无信息”的情况而He只关心杂合子比例。2.3 香农指数Shannon Index,I兼顾“常见”与“稀有”的多样性度量香农指数源于信息论用来衡量系统的不确定性或混乱程度。在生态学中它度量物种多样性在遗传学中我们将其应用于等位基因。它的强大之处在于对稀有等位基因的存在非常敏感。即使某个等位基因频率很低只要它存在就会对香农指数产生贡献。其计算公式为I - Σ [pᵢ * ln(pᵢ)]其中pᵢ是第i个等位基因的频率ln是自然对数。如何理解可以把每个等位基因看作一个“信息符号”。某个等位基因频率越高比如p0.9它出现的不确定性就越低-0.9ln(0.9) ≈ 0.095对总体“信息熵”的贡献越小。反之一个频率为0.1的稀有等位基因其贡献-0.1ln(0.1) ≈ 0.230可能比一个常见等位基因更大。当所有等位基因频率相等时最不确定的状态香农指数达到最大。与Nei’sHe的关键区别Nei’sHe对等位基因频率的变化是二次方的更强调高频等位基因的贡献。而香农指数通过对数变换使得低频等位基因的权重相对增加。因此如果一个群体拥有较多低频的稀有等位基因即使其He不是特别高它的香农指数I也可能相对较高。这为我们洞察群体遗传结构提供了另一个维度。3. 从原始数据到等位基因频率实操第一步理论清晰后我们进入实战。一切计算始于等位基因频率。假设我们有以下简化数据集格式为常见的矩阵形式行是个体列是位点。这里用SSR数据举例基因型记录为等位基因的长度如“154/158”表示两个等位基因对于SNP数据可能是“A/A”, “A/C”, “C/C”等形式。模拟数据表3个群体PopA, PopB, PopC每个群体5个个体在2个位点Locus1, Locus2上的基因型个体ID群体Locus1Locus2Ind1_APopA154/154201/205Ind2_APopA154/158201/201Ind3_APopA158/158205/209Ind4_APopA154/154201/209Ind5_APopA154/158205/205Ind1_BPopB158/158201/201Ind2_BPopB158/162201/205Ind3_BPopB158/158205/209Ind4_BPopB162/162201/209Ind5_BPopB158/162205/205Ind1_CPopC154/162209/213Ind2_CPopC158/162201/213Ind3_CPopC162/162213/213Ind4_CPopC154/158201/209Ind5_CPopC158/162205/2133.1 数据预处理与等位基因计数首先我们需要按群体、按位点拆分数据并进行等位基因计数。以PopA 群体的 Locus1为例基因型列表154/154, 154/158, 158/158, 154/154, 154/158展开所有等位基因154, 154, 154, 158, 158, 154, 154, 154, 158, 154, 158 共5个个体 * 2 10个等位基因计数等位基因154出现次数在154/154中出现2次2个个体4次在154/158中出现1次2个个体2次总计6次等位基因158出现次数在154/158中出现1次2个个体2次在158/158中出现2次1个个体2次总计4次计算频率p(154) 6 / 10 0.6p(158) 4 / 10 0.4注意事项缺失数据处理如果某个个体的基因型为缺失如“-/-”或“NA”在计数时这个个体在该位点的两个等位基因都应被排除在外分母总等位基因数也要相应减少。例如PopA有5个个体1个缺失则有效样本等位基因数为4*28。等位基因命名一致性确保“154”和“154.0”被识别为同一个等位基因。在脚本中统一转换为字符串或整数进行比较。按群体分别计算多样性指数是群体特异性的。必须为每个目标群体单独计算等位基因频率。3.2 使用R语言进行自动化频率计算手动计算只适用于教学。实际研究中我们使用脚本。这里给出R语言的示例。# 假设数据已读入为一个数据框 df列包括IndID, Pop, Locus1, Locus2... # 基因型列以“/”分隔如“154/158” # 方法一使用基础R和tidyverse思路适用于自定义分析 library(tidyverse) calculate_allele_freq - function(genotype_vec) { # 拆分基因型展开为等位基因列表 alleles - unlist(strsplit(genotype_vec, split /)) # 移除可能的缺失值 alleles - alleles[!alleles %in% c(-, NA, )] # 计算频率表 freq_table - table(alleles) / length(alleles) return(as.data.frame(freq_table)) } # 对PopA群体的Locus1进行计算 popA_locus1_genotypes - df %% filter(Pop PopA) %% pull(Locus1) freq_result - calculate_allele_freq(popA_locus1_genotypes) print(freq_result) # 结果应显示 alleles: 154, 158 和对应的Freq: 0.6, 0.4 # 方法二使用专业包adegenet更高效适合大数据 library(adegenet) # 首先需要将数据转换为genind对象一种遗传数据标准格式 # 假设我们有一个矩阵行是个体列是位点二倍体两个等位基因用两列表示 # 更常见的做法是使用df2genind函数它可以直接处理分隔符格式。 # 注意需要先将数据整理为每个位点一列基因型用“/”分隔的格式。 genind_obj - df2genind(df[, c(Locus1, Locus2)], sep/, popdf$Pop, NA.char-) # 计算等位基因频率 freq_by_pop - seppop(genind_obj) %% lapply(function(x) tab(x, freq TRUE)) # 查看PopA在Locus1的频率 print(freq_by_pop$PopA$Locus1)adegenet会自动处理多等位基因、缺失值并按群体给出频率是后续计算指数的强大基础。4. 三大指数的详细计算流程与代码实现获得等位基因频率后我们就可以逐一计算三个指数了。我们将分别展示基于基础公式的逐步计算和利用现有R包的快捷计算。4.1 多态信息含量PIC计算我们基于2.1节的公式为PopA群体的Locus1等位基因频率p1540.6, p1580.4进行计算。手动计算验证Σ(pᵢ²) (0.6)² (0.4)² 0.36 0.16 0.52Σ(2pᵢ²pⱼ²) 2 * (0.6)² * (0.4)² 2 * 0.36 * 0.16 2 * 0.0576 0.1152PIC 1 - 0.52 - 0.1152 0.3648这个结果~0.365属于中度信息量。R函数实现通用支持多等位基因calculate_PIC - function(allele_freq_vec) { # allele_freq_vec: 一个数字向量包含某个位点所有等位基因的频率 sum_p2 - sum(allele_freq_vec^2) sum_2p2p2 - 0 k - length(allele_freq_vec) if (k 2) { for (i in 1:(k-1)) { for (j in (i1):k) { sum_2p2p2 - sum_2p2p2 2 * (allele_freq_vec[i]^2) * (allele_freq_vec[j]^2) } } } pic - 1 - sum_p2 - sum_2p2p2 return(pic) } # 使用PopA Locus1的频率 freq_vec - c(0.6, 0.4) pic_value - calculate_PIC(freq_vec) print(pic_value) # 输出 0.3648批量化计算所有群体所有位点 我们需要一个嵌套循环外层遍历群体内层遍历位点在每个位点内提取等位基因频率向量然后调用calculate_PIC函数。使用adegenet对象可以简化此过程但需注意其tab()函数返回的是等位基因计数比例矩阵需要按位点和群体提取频率向量。4.2 Nei氏基因多样性指数He计算为PopA群体的Locus1计算He。手动计算He 1 - Σ(pᵢ²) 1 - 0.52 0.48。R函数实现calculate_Nei_He - function(allele_freq_vec) { he - 1 - sum(allele_freq_vec^2) return(he) } he_value - calculate_Nei_He(c(0.6, 0.4)) print(he_value) # 输出 0.48使用R包hierfstat快速计算hierfstat包是计算群体遗传统计量的利器。library(hierfstat) # 假设 genind_obj 是之前创建的 adegenet 对象 basic_stats - basic.stats(genind_obj) # basic_stats$Hs 即为每个位点每个群体的 Neis gene diversity (Hs 即 He) print(basic_stats$Hs)basic.stats()函数会返回一个列表其中$Hs就是一个矩阵行是位点列是群体直接给出了所有需要的He值极其方便。4.3 香农指数I计算为PopA群体的Locus1计算I。手动计算I - [0.6 * ln(0.6) 0.4 * ln(0.4)] ln(0.6) ≈ -0.5108, ln(0.4) ≈ -0.9163 所以I - [0.6*(-0.5108) 0.4*(-0.9163)] - [-0.3065 - 0.3665] - [-0.673] 0.673R函数实现calculate_Shannon_I - function(allele_freq_vec) { # 注意频率为0的项按定义 p*ln(p) 为0在计算中需排除否则log(0)会报错 freq_nonzero - allele_freq_vec[allele_freq_vec 0] shannon - -sum(freq_nonzero * log(freq_nonzero)) # R中log()默认是自然对数 return(shannon) } i_value - calculate_Shannon_I(c(0.6, 0.4)) print(i_value) # 输出 0.673注意事项香农指数对等位基因频率为0的情况非常敏感。在计算时必须只对频率大于0的等位基因进行求和这是数学定义的要求lim_{p-0} p log p 0。在生态学中香农指数有时会使用以2或10为底的对数。在遗传学中自然对数ln是标准计算结果记为I。如果看到H‘的符号通常也是指自然对数的香农指数。务必在论文方法部分注明。5. 完整项目实战自动化脚本与结果整合现在我们将上述步骤整合成一个完整的、可重复的分析流程。目标输入一个包含群体信息和基因型的数据框输出一个表格包含每个群体在每个位点上的PIC、Nei‘sHe和 ShannonI。5.1 构建自动化分析流程我们假设输入数据格式与第3节的模拟数据表一致。# 加载必要的库 library(tidyverse) library(adegenet) # 1. 数据读入与格式转换 # df 是你的原始数据框 genind_obj - df2genind(df[, c(Locus1, Locus2)], sep/, popdf$Pop, NA.char-) # 2. 按群体分离数据 pop_list - seppop(genind_obj) # 3. 定义计算函数复用之前定义的 calculate_PIC - function(freq_vec) { ... } calculate_Nei_He - function(freq_vec) { ... } calculate_Shannon_I - function(freq_vec) { ... } # 4. 初始化结果存储列表 results - list() # 5. 循环计算每个群体、每个位点 locus_names - locNames(genind_obj) # 获取位点名 pop_names - names(pop_list) for(pop in pop_names) { cat(Processing population:, pop, \n) pop_obj - pop_list[[pop]] # 获取该群体所有位点的等位基因频率列表 # tab(..., freqTRUE) 返回一个矩阵行是等位基因列是位点 freq_table - tab(pop_obj, freq TRUE) # 为每个位点计算指数 pop_result - data.frame( Locus locus_names, PIC NA_real_, Nei_He NA_real_, Shannon_I NA_real_ ) for(i in seq_along(locus_names)) { locus - locus_names[i] # 提取该位点的等位基因频率向量并移除NA对应不存在的等位基因 freq_vec - freq_table[, i] freq_vec - freq_vec[!is.na(freq_vec) freq_vec 0] if(length(freq_vec) 0) { pop_result$PIC[i] - calculate_PIC(freq_vec) pop_result$Nei_He[i] - calculate_Nei_He(freq_vec) pop_result$Shannon_I[i] - calculate_Shannon_I(freq_vec) } else { # 如果该位点在该群体全部缺失则赋值为NA pop_result$PIC[i] - NA pop_result$Nei_He[i] - NA pop_result$Shannon_I[i] - NA } } results[[pop]] - pop_result } # 6. 整合结果 # 例如将结果合并为一个大的数据框并添加群体列 final_result - bind_rows(results, .id Population) print(final_result)5.2 结果解读与可视化计算出的final_result数据框是核心结果。通常我们还会计算每个指数的平均值按位点平均或按群体平均以进行概括性比较。# 计算每个群体三个指数的平均值跨位点 summary_by_pop - final_result %% group_by(Population) %% summarise( Mean_PIC mean(PIC, na.rm TRUE), Mean_Nei_He mean(Nei_He, na.rm TRUE), Mean_Shannon_I mean(Shannon_I, na.rm TRUE) ) print(summary_by_pop) # 可视化绘制群体多样性比较条形图 library(ggplot2) summary_by_pop_long - summary_by_pop %% pivot_longer(cols -Population, names_to Index, values_to Value) ggplot(summary_by_pop_long, aes(x Population, y Value, fill Index)) geom_bar(stat identity, position position_dodge()) labs(title 群体遗传多样性指数比较, y 指数平均值, x 群体) theme_minimal()解读示例 假设PopA、PopB、PopC的结果显示PopB的Mean_Nei_He和Mean_PIC最高说明其遗传多样性最丰富标记的信息量也最大。PopC的Mean_Shannon_I可能与PopA相近甚至略高但如果其Nei_He较低则提示PopC中可能含有更多低频的稀有等位基因。这种差异可能源于群体历史如瓶颈效应、奠基者效应、选择压力或迁移事件。6. 常见问题、陷阱与高级技巧在实际操作中你几乎一定会遇到下面这些问题。6.1 数据输入与格式的坑问题1基因型编码不统一。数据中混用“A/T”、“A|T”、“A T”等多种分隔符。解决在导入数据前使用文本编辑器或sed/awk命令或R中的gsub()函数将所有分隔符统一为一种如“/”。问题2等位基因命名含特殊字符。如“CT12”或“-”这可能在拆分时造成混乱。解决在分析前清洗等位基因名称移除括号、空格等非字母数字字符或将片段长度转换为纯数字。问题3二倍体与单倍体数据混淆。计算期望杂合度He和PIC是针对二倍体群体的。如果你的数据是单倍体如叶绿体、线粒体DNA或单倍型则应使用单倍型多样性Haplotype Diversity,Hd其计算公式与Nei‘sHe在形式上一致但解释不同。解决明确数据类型。对于单倍体数据使用pegas包中的hap.div()函数计算Hd。6.2 计算过程中的关键检查点检查点1等位基因频率和是否为1。由于浮点数计算精度问题sum(freq_vec)可能不等于1而是0.999999或1.000001。这通常不影响结果但若偏差较大如0.01需检查是否有等位基因被遗漏或重复计数。检查点2PIC值大于1或为负数。理论上PIC范围是[0,1)。如果算出负数首先检查等位基因频率向量是否包含0并确认在计算Σ(2pᵢ²pⱼ²)时循环是否正确特别是当等位基因数k1时该项应为0。如果大于1几乎肯定是计算错误。检查点3Shannon指数出现NaN。这是因为对0取了对数。确保你的calculate_Shannon_I函数中包含了freq_nonzero - allele_freq_vec[allele_freq_vec 0]这一过滤步骤。6.3 高效处理与性能优化技巧1利用矩阵运算替代循环。当位点和群体数量巨大如上千个SNP上百个群体时多层for循环会非常慢。可以尝试将频率数据整理成三维数组群体 x 位点 x 等位基因然后使用apply族函数进行向量化计算。技巧2使用专业软件/包进行大规模计算。对于全基因组SNP数据如VCF文件推荐使用PLINK、vcftools或GCTA等软件先计算等位基因频率再导入R进行指数计算。或者在R中使用SNPRelate、scikit-allelPython等专门为大数据优化的包。技巧3并行计算。如果必须使用循环可以考虑使用foreach包配合doParallel包进行并行计算将不同群体的计算任务分配到多个CPU核心上。6.4 结果报告与生物学解释报告什么在论文中通常以表格形式报告每个位点在每个群体的三个指数值并附上平均值和标准误SE。对于大量位点可以报告所有位点的平均值。如何解释PIC重点关注用于后续分析的标记子集。在GWAS或连锁图谱构建中筛选PIC 0.3或0.4的标记。Nei‘sHe这是衡量遗传多样性的黄金标准。比较不同群体、物种或不同基因组区域的He值。低的He可能暗示近交、瓶颈效应或强烈的选择清扫。Shannon‘sI当关注稀有等位基因的贡献时结合I和He一起看。如果I相对高于He表明稀有等位基因较多如果两者都低则多样性全面匮乏。注意置信区间由于抽样误差我们只测了部分个体计算出的指数是一个点估计。可以通过重抽样如 bootstrap的方法计算每个指数值的95%置信区间这在比较群体间差异是否显著时非常有用。hierfstat包的boot.ppfis等函数可以帮助完成这项工作。计算群体遗传多样性指数远不止是套用公式。从数据清洗、频率计算到指数解读和生物学推断每一步都需要谨慎和深入的理解。本文提供的代码和思路是一个坚实的起点但请记住最好的分析流程永远是那个最适合你具体数据和研究问题的流程。在实际操作中多验证、多思考、多与领域内的标准方法对照你的分析结果才会更加可靠和有力。