基因型计数与等位基因计数归纳总结

说实话,做了这么多年群体遗传分析,我见过最多的问题就出在最基础的地方——基因型计数和等位基因计数搞混了。这两个概念是你做一切频率计算的地基。地基不稳,后面什么PCA、群体结构、选择信号分析全是白搭。今天咱们就把这块彻底捋清楚。

1. 先搞清楚这两个计数到底在数什么

基因型计数,数的是人。每个样本携带什么基因型,数一数每种基因型有多少个样本。比如一个SNP位点有AA、AT、TT三种基因型,你手上有100个样本,数出来AA是30个,AT是50个,TT是20个——这就是基因型计数。

等位基因计数,数的是拷贝。每个样本贡献两个等位基因拷贝(二倍体生物嘛),你数的是A这个碱基出现了多少次,T这个碱基出现了多少次。说白了,就是把基因型计数转换成等位基因层面的数量。

我刚开始带团队的时候,有个新人拿着数据跑过来问我:「老师,为什么我算的等位基因频率总和不是1?」我一看,他直接把基因型计数的比例当成了等位基因频率。嗯,这是最常见的入门坑。

一句话总结:基因型数样本,等位基因数拷贝。两者差一个「乘以2」的步骤。

2. 从基因型到等位基因,其实就是一道算术题

推导公式特别简单。你想想看,每个纯合子贡献2个相同的等位基因,每个杂合子贡献1个A和1个a。那么:

等位基因A的计数 = 纯合子AA的个体数 × 2 + 杂合子Aa的个体数 × 1

等位基因a的计数 = 纯合子aa的个体数 × 2 + 杂合子Aa的个体数 × 1

总等位基因数 = 样本总数 × 2(二倍体)。然后用A的计数除以总计数,就是A的频率。

举个例子。假设一个位点有200个样本:

基因型 计数(个体数) 贡献A拷贝数 贡献a拷贝数
AA 60 60 × 2 = 120 0
Aa 80 80 × 1 = 80 80 × 1 = 80
aa 60 0 60 × 2 = 120
合计 200 200 200

等位基因A的频率 = 200 / (200+200) = 0.5。就这么简单。

3. 实战中数据长什么样?我拿真实项目示范一下

下面这段代码是我实际项目里提取计数信息的简化版。VCF文件解析后,基因型计数字段通常是GT:DP:GQ这种格式,我会先统计每个位点的基因型分布:

# 伪代码:从基因型计数推导等位基因计数
# 假设已从VCF解析出每个样本的基因型

genotype_counts = {"AA": 60, "Aa": 80, "aa": 60}

# 等位基因计数
A_count = genotype_counts["AA"] * 2 + genotype_counts["Aa"]
a_count = genotype_counts["aa"] * 2 + genotype_counts["Aa"]

total_alleles = A_count + a_count

A_freq = A_count / total_alleles
a_freq = a_count / total_alleles

print(f"A等位基因计数: {A_count}")
print(f"a等位基因计数: {a_count}")
print(f"A频率: {A_freq:.4f}")
print(f"a频率: {a_freq:.4f}")

输出结果:

A等位基因计数: 200
a等位基因计数: 200
A频率: 0.5000
a频率: 0.5000

你看,代码逻辑跟手算完全一致。我在项目里习惯把这个步骤写成独立函数,这样后面做批量位点分析时直接调用就行。

4. 等位基因计数与基因型计数的关系图谱

下面这张图把整个推导路径串了一遍。我每次培训新人的时候都会先画这张图,它能把两个计数的关系可视化出来,比纯文字好理解得多。

基因型计数 → 等位基因计数 完整推导流程 ① 样本群体(N个个体) ② 每个位点的基因型鉴定 ③ 基因型计数:AA, Aa, aa 各有多少个体 ④ A计数 = AA×2 + Aa×1 ⑤ a计数 = aa×2 + Aa×1 ⑥ A频率 = A计数 ÷ 总等位基因数 ⑦ a频率 = a计数 ÷ 总等位基因数 总等位基因数 = 样本个体数 × 2(二倍体生物)

这张图从左到右看,就是标准分析流水线。我个人习惯在每个位点跑完统计后,先检查基因型计数之和是否等于样本总数,这是数据完整性的第一道防线。

5. 三个最容易翻车的地方

我见过太多次因为计数细节导致分析重来的案例了。下面这三个坑,你大概率也会遇到:

  • 样本分型率低于阈值:如果一个位点只有60%的样本有基因型数据,剩下的全是缺失,你直接拿这60%做等位基因计数——偏倚会非常大。我曾经有个全基因组关联分析(GWAS)项目,就是因为没过滤低分型率位点,前20个显著信号全是假阳性。
  • 杂合子计数翻倍漏算:有人写脚本的时候,只给纯合子乘以了2,杂合子忘了加。结果A计数和a计数都少了一半,频率倒是没变,但后面的群体遗传多样性指标全错了。
  • 多等位基因位点的处理:大部分SNP是二态的,但偶尔会遇到三态甚至四态位点。这时候用上面的二态公式会漏掉部分等位基因。我的做法是先检测等位基因种类数,超过2种就走多等位基因通用计算分支。
⚠️ 特别注意:如果遇到「单倍型」数据(如线粒体或Y染色体非重组区),每个样本只贡献1个等位基因拷贝。这时候总等位基因数 = 样本数,不是样本数×2。别搞混了。

6. 遇到大规模数据怎么办?

几十个样本手算没问题,但全基因组层面动辄几百万个位点、上万样本,这时候必须写脚本批量处理。给你看一个实际项目中的处理框架:

# 批量位点等位基因计数处理流程
for each variant in VCF:
    # 步骤1:提取该位点的所有样本基因型
    genotypes = extract_genotypes(variant)

    # 步骤2:过滤缺失(通常是 ./., 0/0 也是有效)
    valid_gts = [g for g in genotypes if g != "./."]

    if len(valid_gts) / total_samples < min_call_rate:
        mark_low_quality(variant)
        continue

    # 步骤3:基因型计数
    counts = {"AA": 0, "Aa": 0, "aa": 0}
    for gt in valid_gts:
        if gt == "0/0": counts["AA"] += 1
        elif gt == "0/1" or gt == "1/0": counts["Aa"] += 1
        elif gt == "1/1": counts["aa"] += 1

    # 步骤4:等位基因计数
    A_count = counts["AA"] * 2 + counts["Aa"]
    a_count = counts["aa"] * 2 + counts["Aa"]

    # 步骤5:计算频率并写入输出
    total = A_count + a_count
    output[variant] = {
        "A_freq": A_count / total,
        "a_freq": a_count / total,
        "n_alt": a_count,
        "n_total": total
    }

我在项目中一般会加一个质量控制(QC)模块,直接把这个流程跑完后生成一份质控报告,标注所有分型率低、等位基因计数异常的位点。你想想看,如果等位基因计数阶段就埋了雷,后面做群体结构分析时再排查,工作量会大很多。

7. 把这两个计数放进更完整的分析上下文

基因型计数和等位基因计数不只是两个数字。它们往下游走,会直接影响:

  • 哈代-温伯格平衡检验:基于等位基因频率计算期望基因型数,再与实际基因型计数做卡方检验。
  • 群体分化指数Fst:依赖每个亚群的等位基因频率。
  • 选择信号分析(如iHS、XP-EHH):需要准确的等位基因频率作为输入。
  • 多群体联合分析:每个群体独立做等位基因计数,再合并比较。

说白了,这两个计数就是整个群体遗传分析大厦的砖和瓦。我曾经在一个跨国合作项目中,因为某个位点在两个群体中的等位基因计数方向搞反了,导致Fst计算值异常,浪费了整整两周排查。从那以后,我每个位点都坚持输出计数中间结果做人工抽检。

💡 我的个人习惯:每分析1000个位点,至少抽10个位点用手算验证。如果发现有偏差,马上排查脚本逻辑。等位基因计数这种基础步骤一旦出错,后续所有分析都会跟着偏。验证虽然花时间,但比起后期返工,这点投入非常值得。

基因型计数和等位基因计数,一个是数个体,一个是数拷贝。从个体数到拷贝数,中间只隔了一个「乘以2」的换算。但就是这一步,足够让粗心的人栽跟头。记住:先看分型率,再算计数,最后做交叉验证。把这套流程养成习惯,后面的大规模分析你才能放心往下推。