估计阅读时长: 16 分钟

用 GCModeller 做泛基因组分析(一):算法原理与香农信息熵

面向读者:具有一定 R 语言编程基础的生物信息学初学者、实验室里的微生物研究者。
本篇讲清楚两件事:GCModeller 的泛基因组分析是怎么算的,以及贯穿整个分析体系的"香农信息熵"到底在度量什么。
全文不依赖任何编程知识,算法原理一律用公式和自然语言伪代码讲解。实战操作请看系列第二篇。


1. 什么是泛基因组?

把大肠杆菌的两个菌株的基因组摆在一起看,你会发现一个 surprising 的事实:这两个"同一个物种"的基因组,基因组成可能相差 20% 以上。K-12 实验室菌株约 4600 个基因,而某些致病菌株可以超过 5900 个基因,其中相当一部分在 K-12 里根本不存在。

单一"参考基因组"无法代表一个物种的全部遗传潜力。于是有了泛基因组(pan-genome)的概念:

泛基因组 = 该物种全部个体基因组的并集,由三部分构成:

  • 核心基因组(core genome):所有个体都有的基因,通常是管家基因、中心代谢、遗传信息处理相关功能;
  • 附属基因组(accessory genome):只在部分个体中存在的基因,包括"壳基因"(中等频率出现)和"云基因"(只在极少数个体中出现),往往与血清型、致病性、耐药性、环境适应直接相关。

泛基因组分析要回答的核心问题就是:哪些基因是 everyone 都有的?哪些是部分人有的?哪些是个别人独有的?某个基因组离"平均"有多远?

2. GCModeller 泛基因组分析流水线总览

GCModeller(https://github.com/xieguigang/GCModeller)是一个 .NET 平台上的基因组学分析引擎。它的泛基因组分析模块由 R# 脚本语言(GCModeller 自带的 R 语言方言)驱动,整体数据流如下:

GenBank 文件库 (*.gbff)
      │  ① 导出蛋白序列 + 基因位置表     [pack_genes.R]
      ▼
proteins.faa  +  genes.csv
      │  ② CD-HIT 式序列聚类 → 基因家族   [kmers 包]
      │  ③ 并查集合并 → 家族分组
      ▼
基因家族 (gene families)
      │  ④ 构建 PAV 矩阵 + 家族四分类     [cdhit_family.R]
      │  ⑤ 泛基因组曲线 / 遗传距离 / 共线性
      │  ⑥ 结构变异检测
      │  ⑦ 香农信息熵 + PCA + KMeans
      ▼
result.html 报告 + 一组 CSV 统计表格

分析引擎由 GCModeller 源码库中的几个核心模块组成:泛基因组分析模块(含主分析器 GenomeAnalyzer、SV 信息熵计算模块 SVDomainEntropy、统计模块 PanGenomeStats)、提供序列聚类算法底层的序列比对工具包(SequenceAlignment 模块),以及对外暴露 R# 函数接口的比较基因组学工具包(comparative_toolkit 包的 pangenome 模块)。下面按流水线顺序拆解每一步的算法。

3. 第一步:从百万条序列到基因家族

3.1 为什么不能两两比对?

一个中等规模的泛基因组项目(比如 800 个大肠杆菌基因组)大约包含 380 万条蛋白序列。如果对每两条序列都做一次序列比对,需要的比对次数是千万亿级别的——完全不可行。

CD-HIT 算法给出了一条务实的捷径,GCModeller 内置的 CD-HIT 式聚类(kmers 包的 cdhit_clusters 函数)遵循同样的思路:

长序列优先、贪心占坑:先把序列按长度从长到短排序,然后从最长的序列开始,每条序列要么加入一个已有的家族(当它与该家族代表序列的相似度足够高时),要么自立门户成为一个新家族的代表。

用自然语言伪代码描述:

输入: 全部蛋白序列集合, 相似度阈值 t (本系列实验取 0.6)

1. 把所有序列按长度从长到短排序
2. 为每条序列制作一份"k-mer 指纹"(序列切成长度为 k 的小片段,
   用这些片段的哈希集合作为这条序列的快速"草图")
3. 依次处理每条序列 S:
     a. 用 k-mer 指纹粗筛:哪些已有家族"有可能"与 S 相似?
        (指纹差异大的家族直接排除,不做昂贵的真实比对)
     b. 对粗筛出来的少数候选家族的代表序列,计算 S 与它的
        真实序列同一性 (identity = 比对上一致位点的比例)
     c. 若 identity >= t  →  S 加入该家族
        否则              →  S 成为新家族的代表序列
4. 输出: 所有家族

k-mer 指纹在这里的作用类似于图书馆的索书号:不需要逐页比对两本书,只需要比较索引卡片就能快速排除绝大多数无关的书目。这一步把"每条序列对全部家族做比对"变成了"每条序列只对极少数候选做比对",聚类才可能在普通工作站上完成。

3.2 从簇到家族:并查集

聚类得到的每个簇就是候选基因家族。在 GCModeller 的实现里,家族的维护用的是数据结构中经典的并查集(Union-Find)。可以把它想象成社交网络里的"朋友圈":开始时每个人自成一座孤岛,每当确认两个基因同源,就把他们所在的两个朋友圈合并成一个。一个包含 k 个基因的家族,只需要 k-1 次"合并"操作就能建好。

4. 第二步:PAV 矩阵——泛基因组的数据基石

有了基因家族,我们把每个家族在每个基因组中的基因个数统计出来,就得到了整个泛基因组分析中最核心的数据结构——PAV 矩阵(Presence/Absence Variation,存在/缺失变异矩阵):

基因组 1 基因组 2 …… 基因组 N
家族 A 1 1 …… 1
家族 B 1 0 …… 2
家族 C 0 0 …… 1

矩阵元素是拷贝数:0 表示该基因组没有这个家族,1 表示单拷贝,2 以上表示多拷贝。行是基因家族,列是基因组。

对每一个家族,计算它的存在比例:

$r = \frac{\text{存在该家族的基因组个数}}{\text{基因组总数 } N}$

按存在比例把所有家族分成四类(GCModeller 默认阈值,可通过 R# 接口调整):

类别 判定条件 直观理解
核心基因 core r = 100% 谁都不能没有,多为管家基因
软核心基因 soft core r ≥ 软核心阈值(默认 95%,本系列实验取 80%) 绝大多数个体都有
壳基因 shell 15% ≤ r < 80% 一部分个体有,"随缘"基因
云基因 cloud r < 15% 只有零星个体携带,多为水平转移的"过客"

此外分析器还会顺带打上两个标签:特有基因(只在 1 个基因组中出现的家族)和单拷贝直系同源(在所有基因组中都恰好只有一份的家族,做进化分析的黄金材料)。

为什么软核心阈值很重要? 100% 核心的判定极其严格:只要有一个基因组漏注释了一个基因,该家族就会跌出核心集合。软核心(比如"80% 的基因组都有")对注释噪声和个别异常基因组更宽容,在基因组数量很大时更能反映"稳定遗传的核心功能"的真实规模。

5. 泛基因组曲线:物种的基因池还在扩张吗?

泛基因组曲线回答一个问题:随着不断加入新的基因组,还会不断发现新的基因吗?

做法是蒙特卡洛模拟:把 N 个基因组的加入顺序随机打乱,逐个加入,记录每加入一个新基因组后"已发现的家族总数"(泛基因组大小)和"在已加入基因组中全部存在的家族数"(核心基因数);这样的随机顺序重复 100 次,取平均值抹平随机波动。

重复 100 次:
    随机打乱基因组的加入顺序
    家族计数器清零
    依次加入每个基因组:
        对该基因组携带的每个家族:
            若该家族此前从未见过   → 泛基因组大小 +1
            若该家族此前每个基因组都有 → 核心基因数 +1(若它在这个新
                                      基因组中也存在,核心基因数保持;
                                      否则下一次不再计入)
取 100 次模拟的平均值 → 泛基因组曲线

曲线形态本身就是结论:

  • 封闭泛基因组(closed):曲线早早走平——这个物种的基因库已被采样殆尽;
  • 开放泛基因组(open):曲线持续攀升——每加一个基因组都在冒出新基因,说明该物种的基因水平转移极其活跃。

微生物学里的经验规律(Heaps 律)告诉我们:大多数自由生活的细菌物种都是开放泛基因组。第二篇博客的大肠杆菌实测数据会印证这一点。

6. 基因组间的遗传距离:Jaccard 距离

衡量两个基因组"差多远",GCModeller 采用基于基因家族集合的 Jaccard 距离:

$d(A, B) = 1 - \frac{|F_A \cap F_B|}{|F_A \cup F_B|}$

其中 $F_A$、$F_B$ 分别是两个基因组所拥有的基因家族集合。交集越大(共有的家族越多),距离越接近 0;两个基因组各玩各的,距离趋近 1。

这个定义完全基于基因的有无,不需要序列比对,因此对泛基因组这种"基因组成差异巨大"的场景特别合适。算出全部基因组对之间的距离后,就得到了一张 N × N 的遗传距离矩阵,后续可以做聚类树、多维缩放(MDS)等下游分析,直观呈现菌株群体按血清型/致病型/生态来源的分化格局。

工程细节(供好奇者):GCModeller 把每个基因组的家族集合编码成一个二进制位图,两个基因组的交集大小用位图按位与(AND)配合 popcount 指令计算,单对基因组的距离计算只需"家族数 ÷ 64"次机器指令,几百个基因组两两比较也只是一眨眼的功夫。

7. 共线性与结构变异

共线性(collinearity)指的是:两个基因组上,一串同源基因不仅同源,而且保持相同的排列顺序。找出共线性区块,就能看清基因组的大尺度结构是保守的——大肠杆菌的不同菌株之间,染色体的基因顺序通常高度共线。

在共线性的基础上,GCModeller 逐家族扫描 PAV 矩阵和拷贝数,检测五类结构变异(SV)事件:

事件类型 判定逻辑 生物学含义
PAV_Absence 大多数基因组有、这个基因组没有 缺失事件
PAV_Presence 极少数基因组独有的家族 特有基因获得(常见于水平转移)
CNV_Gain 拷贝数 ≥ 中位拷贝数 × 2 拷贝数扩张
CNV_Loss 拷贝数 ≤ 中位拷贝数 × 0.5 拷贝数收缩
Collinearity_Break 共线性区块跨染色体 易位事件

这些 SV 事件正是下一节 SV 信息熵分析的原料。

8. 核心章节:泛基因组中的香农信息熵

信息熵是本系列的重头戏。GCModeller 在两个层面使用香农信息熵:基因组层面(一个基因组压缩成一个熵值)和基因家族层面(一个家族压缩成两个熵值)。

8.1 一分钟看懂香农信息熵

香农(Claude Shannon)1948 年给出的定义:一个离散随机变量的熵是它所有可能取值带来的"平均意外程度":

$H = -\sum_{i} p_i \log p_i$

其中 $p_i$ 是第 i 种取值出现的概率。约定 $0 \log 0 = 0$(不可能发生的事件不贡献信息)。

直觉版本:

  • 抛一枚均匀硬币(正/反各 50%),结果最难猜,熵最大;
  • 抛一枚两面都是正面的硬币,结果毫无悬念,熵为 0;
  • 取值越"摊得平"、越均匀,熵越大;越"扎堆"在少数取值上,熵越小。

(以 2 为底时熵的单位是比特;GCModeller 的实现使用自然对数,单位是 nat,数值上乘以换算系数 1/ln2 即得比特数。)

把这套度量搬进泛基因组:把"某个基因组有哪些家族"或"某个家族出现在哪些基因组"看作一个概率分布,熵就成了一把度量基因多样性的尺子。

8.2 基因组层面的熵(一):存在/缺失均衡度 —— GCModeller 的实现

设泛基因组共 N 个基因家族,某个基因组存在其中 K 个、缺失 N-K 个。以"存在/缺失"两种状态定义概率分布:

$p = \frac{K}{N}, \qquad H = -\Big(p \log p + (1-p) \log (1-p)\Big)$

这就是把 PAV 矩阵的一整列(一个基因组对全部 N 个家族的存在状态)压缩成一个数字。由于只有两种状态,这个熵在 p = 0.5 时达到最大值 $\log 2 \approx 0.693$ nat,p 越偏离 0.5(无论是趋近 0 还是趋近 1)熵越低。

生物学解读:

  1. 基因组可塑性的代理指标。大多数细菌基因组中,泛基因组家族的绝大多数是附属基因,因此任一基因组的 p 通常远小于 0.5。如果一个基因组通过水平基因转移(HGT)大量获取附属基因,K 增大、p 向 0.5 靠拢,熵值升高;反之,经历基因大量丢失的基因组熵值走低。
  2. 识别极端基因组。熵值极低的基因组往往对应生活在封闭稳定环境中的内生菌、专性寄生菌或长期实验室驯化菌株(基因组缩减);熵值偏高的基因组则常见于土壤、肠道等复杂环境中的泛化型菌株,它们需要庞大的附属基因库来应对多变的环境。
  3. 作为表型补充参与关联分析。可以把"基因组熵"本身当作一种量化表型:找出哪些家族的存在/缺失显著拉高或压低某个基因组的熵,这些家族往往就是驱动基因组分化的关键变异。

一个亲手算的例子(数字来自本系列第二篇的真实结果):801 个大肠杆菌基因组共聚出 N = 252,301 个基因家族。实验室标准株 K-12 MG1655 存在 K = 2,421 个家族:

$p = \frac{2421}{252301} \approx 0.0096, \qquad H = -(0.0096 \log 0.0096 + 0.9904 \log 0.9904) \approx 0.054 \text{ nat}$

而致病血清型 O157:H7 的一个菌株 K = 5,645,算出 H ≈ 0.107 nat——几乎翻倍。实验室驯化株与携带大量毒力因子的致病株,在熵这一把尺子上被清晰地区分了开来。

8.3 基因组层面的熵(二):进化独特性(延伸思路)

除了均衡度熵,泛基因组分析中还有第二种常见的基因组级熵定义,思想类似文本挖掘里的 TF-IDF——越罕见的东西信息量越大:

先对每个家族 i 计算它在全部 M 个基因组中的出现频率 $p_i$,其信息量定义为:

$I_i = -\log p_i$

(全物种都有的核心基因 $p_i \approx 1$,信息量趋近 0;只在 1 个基因组里出现的特有基因 $p_i = 1/M$,信息量最大。)再把基因组 j 所携带的所有家族的信息量加总:

$Hj = \sum{i \in G_j} -\log p_i$

这种"熵"度量的是基因组携带了多少罕见遗传信息:高值基因组拥有大量别人没有的基因,往往对应独特的生态功能(耐药、特殊代谢通路、宿主互作因子)或致病潜力。

GCModeller 当前默认实现的是 8.2 节的第一种定义(存在/缺失均衡度);第二种定义可以直接基于导出的 PAV 矩阵在 R 里自行计算,适合作为深入分析的手段。论文写作时务必注明采用的是哪种概率分布定义。

8.4 家族层面的熵:SV 结构变异信息熵 —— GCModeller 的实现

熵的视角还可以从"基因组"翻转到"基因家族":一个家族在群体中的变异模式,本身也携带进化信息。

先把结构变异事件组织成两个矩阵(行 = 有 SV 事件的家族,列 = 基因组):

  • CopyNumber 矩阵:元素 $c(f,g)$ = 家族 f 在基因组 g 中该 SV 的拷贝数;
  • Median 矩阵:元素 $m(f,g)$ = 该家族 SV 的中位拷贝数。

① CopyNumber 熵——以"各基因组拷贝数占比"作为离散概率分布:

$H{cn}(f) = -\sum{g} \frac{c(f,g)}{\sum{g'} c(f,g')} \log \frac{c(f,g)}{\sum{g'} c(f,g')}$

  • 高 $H_{cn}$:拷贝数在各基因组间千差万别(有的 1 份、有的 5 份、有的 10 份)→ 该家族经历了频繁的拷贝数扩张/收缩;
  • 低 $H_{cn}$:所有基因组清一色相同拷贝数 → 剂量受到严格的纯化选择。
  • 取值为 0 的基因组(无 SV 事件)自然不参与求和,不干扰结果。

② Median 熵——把每一行的取值当作离散类别统计频率:

$H{med}(f) = -\sum{v} q_v \log q_v$

在 GCModeller 当前数据模型中,同一家族的 SV 中位拷贝数是家族级常量,因此每行的取值实际上只有 {0, 中位数} 两类,$H_{med}$ 刻画的是"该家族的 SV 状态在基因组之间分布的均衡程度":

  • $H_{med}$ 越高 → 有 SV 的基因组与没有的大约各占一半(高度多态);
  • $H_{med}$ 越低 → 要么几乎人人都有、要么几乎人人没有(状态一致)。

(若上游检出流程把每个基因组各自的 SV 尺寸/断裂点都记录下来,这个熵便自然升级为"SV 结构特征的离散程度"——直接度量各基因组间断裂点与尺寸形态的多样性——而算法模块本身无需任何改动。)

③ 两个熵 → 一张散点图 → KMeans 聚类

把每个家族表示成平面上的点 $(H{cn}, H{med})$,再用 KMeans 聚类(默认 k = 4)自动划出具有不同演化特征的家族类群。为了让聚类结果稳定可靠,GCModeller 在工程实现上做了三步处理:

  1. 过滤稀疏数据:SV 出现频率低于 5% 基因组的家族不参与聚类——这种家族的熵容易被噪声主导;
  2. Z-score 标准化:两个熵的量纲可能不同,聚类前都变换成"距均值若干个标准差",防止方差大的维度霸占距离计算;
  3. 确定性簇编号:KMeans 的初始中心是随机的,聚类完成后按簇中心坐标重排簇编号,保证同一数据重跑得到完全一致的结果。

四类典型进化模式(象限解读):

模式 特征 命名 典型机制
低 $H{cn}$ + 低 $H{med}$ 拷贝数与 SV 状态都高度一致 僵化/保守型 管家基因,强纯化选择,剂量敏感
高 $H{cn}$ + 低 $H{med}$ 结构统一、剂量多变 剂量调谐型 转座子/串联重复驱动,靠拷贝数调节表达量(如抗性基因、色素 P450 家族)
高 $H{cn}$ + 高 $H{med}$ 拷贝数与结构都多变 混沌/快速进化型 正向选择/中性漂变下的"进化试验场",断裂点随机、拷贝数剧烈波动
低 $H{cn}$ + 高 $H{med}$ 剂量恒定、状态多态 结构微调型 SV 状态在群体中五五开,可能对应基因融合或结构域重排

把聚类得到的各簇家族拿去做 GO/KEGG 富集,常能发现"某类功能基因偏爱某种 SV 进化策略"的规律——这是从泛基因组数据里挖掘进化故事的富矿。

8.5 小结:熵把 0/1 海洋压缩成一条连续的尺子

  • 基因组层面:25 万 × 800 的 0/1 矩阵 → 每个基因组一个熵值 → 直接比较基因组可塑性;
  • 家族层面:两个熵值($H{cn}$,$H{med}$)→ 二维特征空间 → 自动划出保守型/剂量调谐型/混沌型/结构微调型四类进化模式。

相比"基因总数""基因组大小"这类粗放计数,香农熵同时考虑了数量的分布与稀有度,是更稳健的数学度量。

9. GCModeller 泛基因组分析 API 速查表

9.1 数据准备(seqtoolkit 包)

R# 函数 所在模块 用途
load_genbanks(files, extract_genomics) GenBank 批量读取 GenBank 库;extract_genomics=TRUE 时只保留基因组染色体序列(剔除质粒等),为泛基因组分析定制
protein_seqs(gb, title, filter_empty) GenBank 导出 CDS 的蛋白序列;title 模板控制 FASTA 命名规则
as_tabular(gb) GenBank 把注释转为基因表(基因 ID、基因组归属、坐标等)
read_genetable(file) GenBank 读取基因表文件
read.fasta / open.fasta / write.fasta bioseq.fasta FASTA 文件的读取、流式写入
cdhit_clusters(x, identities) kmers CD-HIT 式序列聚类,返回家族分组(family/sequence/clusters 三份数据)
cdhit_nr(x, identities) kmers CD-HIT 式聚类,直接返回非冗余代表序列集

9.2 分析与导出(comparative_toolkit 包的 pangenome 模块)

R# 函数 用途
family_groups(cdhit) 把聚类结果整理成"家族 → 成员基因"分组
multiple_genome_alignment(groups) 把家族分组按基因组归并,生成同源配对数据
build_context(genomes, soft_core_threshold, genome_size, uniqueByAcc, sv_cluster_count) 装载基因组注释、设定阈值,构建分析上下文
analysis(context, orth) 执行完整泛基因组分析(PAV/分类/距离/共线性/SV/曲线/熵),返回结果对象
writeBin(result, con) / readBin.pangenome(stream) 分析结果归档保存与重新载入
scatter_set(result) 取基因组统计(stats)、PCA 散点(pca)、PAV 熵(entropy)三份数据
pav_matrix(result) / pav_table(result) 导出 PAV 矩阵 / 家族明细表
genetic_distance(result) 导出基因组间 Jaccard 遗传距离矩阵
curve_data(result) 导出泛基因组曲线数据
sv_table(result) 导出结构变异事件明细表
sv_copy_number_matrix(result) / sv_median_matrix(result) 导出 SV 的 CopyNumber / Median 矩阵
sv_entropy(result, cluster_count) SV 信息熵散点数据 + KMeans 聚类结果
category_percent_matrix(result, by) 四类家族占比矩阵(by="gene" 按基因数、by="family" 按家族数两种口径)
report_html(result) 一键生成可视化 HTML 报告

所有结果表格都实现了 as.data.frame 泛型,可以无缝接入熟悉的 R 风格数据处理流程。

10. 小结

  • 泛基因组分析的本质是:序列聚类建家族 → PAV 矩阵 → 从矩阵中榨取统计量;
  • GCModeller 用 CD-HIT 式贪心聚类 + 并查集解决规模问题,用蒙特卡洛模拟画泛基因组曲线,用 Jaccard 距离刻画基因组分化;
  • 香农信息熵是贯穿这套体系的统一语言:基因组层面的熵度量可塑性,家族层面的双熵度量结构变异的进化模式;
  • 全部分析通过两条 R# 脚本即可完成——具体怎么跑、结果怎么读,请看系列第二篇:《用 GCModeller 做泛基因组分析(二):801 个大肠杆菌基因组实战》。
谢桂纲

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *

博客文章
September 2026
S M T W T F S
 12345
6789101112
13141516171819
20212223242526
27282930  
  1. […] 我们在基于前面所论述的《通过diamond软件进行blastp搜索》对大规模的基因组数据进行了代谢酶的EC number的注释以及按照文章《基因组功能注释(EC Number)的向量化嵌入》的方法,得到了一个比较大的基因组代谢酶TF-IDF嵌入丰度矩阵后,如果将这里所得到的嵌入结果矩阵中的基因组,基于Family层级的物种分类分组看作为单细胞转录数据中的细胞分群结果,能否基于单细胞数据分析方法来分析和可视化我的基因组功能嵌入的结果矩阵呢? […]

  2. […] 我们在基于前面所论述的《通过diamond软件进行blastp搜索》对大规模的基因组数据进行了代谢酶的EC number的注释以及按照文章《基因组功能注释(EC Number)的向量化嵌入》的方法,得到了一个比较大的基因组代谢酶TF-IDF嵌入丰度矩阵后,如果将这里所得到的嵌入结果矩阵中的基因组,基于Family层级的物种分类分组看作为单细胞转录数据中的细胞分群结果,能否基于单细胞数据分析方法来分析和可视化我的基因组功能嵌入的结果矩阵呢? […]

  3. […] 在前面的一篇《基因组功能注释(EC Number)的向量化嵌入》博客文章中,针对所注释得到的微生物基因组代谢信息,进行基于TF-IDF的向量化嵌入之后。为了可视化向量化嵌入的效果,通过UMAP进行降维,然后基于降维的结果进行散点图可视化。通过散点图可视化可以发现向量化的嵌入结果可以比较好的将不同物种分类来源的微生物基因组区分开来。 […]