跳转至

623_宏基因组基因聚类

一句话概述: 基因聚类是宏基因组分析的关键去冗余步骤——CD-HIT经典但慢,MMseqs2/Linclust速度快2300倍且精度相当,它们将百万级预测基因按相似度合并成非冗余基因集(gene catalog),大幅减少下游分析的计算量。

核心知识点速查表

概念白话解释
Gene clustering基因聚类——把高度相似的基因合并成一组,只保留代表序列
Non-redundant gene catalog非冗余基因集——去掉重复后的基因目录,每个基因只出现一次
Representative sequence代表序列——每个聚类中最长或质量最好的那条序列
Identity threshold相似度阈值——序列相似度≥多少才归为一类(通常95%)
Coverage覆盖度——较短序列被比对覆盖的比例
CD-HIT经典基因聚类工具,用贪心增量算法
MMseqs2超快序列搜索和聚类工具(比CD-HIT快几百倍)
LinclustMMseqs2中的线性时间聚类算法(速度最快)
Centroid质心/代表序列——聚类中代表整个组的那条序列

一、安装

# === CD-HIT安装 ===
conda install -c bioconda cd-hit  # conda安装
cd-hit -h  # 验证安装

# === MMseqs2安装 ===
conda install -c bioconda mmseqs2  # conda安装
mmseqs version  # 查看版本(2026年最新为v16+)

# === 源码编译MMseqs2(可选,追求最新版)===
git clone https://github.com/soedinglab/MMseqs2.git  # 克隆仓库
cd MMseqs2 && mkdir build && cd build  # 创建编译目录
cmake -DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_INSTALL_PREFIX=. ..  # 配置
make -j8 && make install  # 编译安装

二、CD-HIT基因聚类

2.1 核苷酸序列聚类(cd-hit-est)

# === 宏基因组预测基因聚类 ===
cd-hit-est \
    -i predicted_genes.fna \      # 输入:所有样本预测的基因核苷酸序列
    -o nr_genes_95.fna \          # 输出:非冗余基因集
    -c 0.95 \                     # 相似度阈值95%(同种水平)
    -aS 0.9 \                     # 较短序列被覆盖≥90%
    -n 10 \                       # word size(c≥0.95时用10)
    -G 0 \                        # 局部比对模式(宏基因组推荐)
    -M 0 \                        # 不限制内存
    -T 16 \                       # 16线程
    -d 0                          # 不截断序列名

# === 输出文件说明 ===
# nr_genes_95.fna       — 代表序列(非冗余基因集)
# nr_genes_95.fna.clstr — 聚类信息文件

# === 查看聚类统计 ===
grep -c "^>" nr_genes_95.fna  # 非冗余基因数
grep -c "^>Cluster" nr_genes_95.fna.clstr  # 聚类数

2.2 蛋白序列聚类(cd-hit)

# === 蛋白序列聚类 ===
cd-hit \
    -i predicted_proteins.faa \   # 输入:蛋白序列
    -o nr_proteins_90.faa \       # 输出:非冗余蛋白集
    -c 0.90 \                     # 相似度阈值90%
    -n 5 \                        # word size(c≥0.7时用5)
    -G 0 \                        # 局部比对
    -aS 0.8 \                     # 较短序列覆盖≥80%
    -M 0 \                        # 不限制内存
    -T 16 \                       # 16线程
    -d 0                          # 不截断序列名

2.3 CD-HIT word size选择

# word size (-n) 必须与相似度阈值 (-c) 匹配:
# c ≥ 0.95 → n = 10-11(核苷酸),n = 5(蛋白)
# c ≥ 0.90 → n = 8-9(核苷酸),n = 5(蛋白)
# c ≥ 0.85 → n = 7(核苷酸),n = 5(蛋白)
# c ≥ 0.80 → n = 5-6(核苷酸),n = 5(蛋白)
# c ≥ 0.70 → n = 4-5(核苷酸),n = 4(蛋白)
# c ≥ 0.60 → 核苷酸不支持,n = 4(蛋白)
# c ≥ 0.50 → 核苷酸不支持,n = 3(蛋白)
# c ≥ 0.40 → 核苷酸不支持,n = 2(蛋白)

2.4 解析聚类结果

import re  # 正则表达式
from collections import Counter  # 计数器

# 解析CD-HIT聚类文件
def parse_cdhit_clstr(clstr_file):
    """解析.clstr文件,返回每个聚类的成员列表"""
    clusters = {}  # 存储聚类信息
    current_cluster = None  # 当前聚类ID

    with open(clstr_file) as f:  # 打开聚类文件
        for line in f:  # 逐行读取
            if line.startswith(">Cluster"):  # 新聚类开始
                current_cluster = line.strip()  # 记录聚类名
                clusters[current_cluster] = []  # 初始化成员列表
            else:
                # 提取序列名
                match = re.search(r'>(\S+)\.\.\.', line)  # 正则匹配序列名
                if match:
                    seq_name = match.group(1)  # 获取序列名
                    is_rep = line.strip().endswith("*")  # 星号标记代表序列
                    clusters[current_cluster].append(
                        (seq_name, is_rep)
                    )

    return clusters  # 返回聚类字典

# 使用
clusters = parse_cdhit_clstr("nr_genes_95.fna.clstr")
sizes = [len(v) for v in clusters.values()]  # 每个聚类的大小

print(f"总聚类数: {len(clusters)}")  # 打印聚类数
print(f"最大聚类: {max(sizes)} 条序列")  # 最大聚类
print(f"单序列聚类: {sizes.count(1)} 个")  # 单例数
print(f"平均聚类大小: {sum(sizes)/len(sizes):.1f}")  # 平均大小

三、MMseqs2基因聚类

3.1 easy-cluster(推荐入门用法)

# === MMseqs2 easy-cluster:一条命令完成聚类 ===
mmseqs easy-cluster \
    predicted_genes.fna \         # 输入序列文件
    mmseqs_cluster \              # 输出前缀
    tmp/ \                        # 临时目录
    --min-seq-id 0.95 \           # 相似度阈值95%
    -c 0.9 \                      # 覆盖度阈值90%
    --cov-mode 1 \                # 覆盖模式:1=较短序列覆盖
    --threads 16                  # 16线程

# === 输出文件 ===
# mmseqs_cluster_rep_seq.fasta  — 代表序列
# mmseqs_cluster_cluster.tsv   — 聚类成员关系(制表符分隔)
# mmseqs_cluster_all_seqs.fasta — 所有序列(按聚类排序)

3.2 easy-linclust(线性时间聚类,最快)

# === Linclust:线性时间聚类,适合超大数据集 ===
mmseqs easy-linclust \
    predicted_genes.fna \         # 输入序列文件
    linclust_result \             # 输出前缀
    tmp/ \                        # 临时目录
    --min-seq-id 0.95 \           # 相似度阈值95%
    -c 0.9 \                      # 覆盖度阈值90%
    --cov-mode 1 \                # 覆盖模式
    --threads 16                  # 16线程

# Linclust vs easy-cluster:
# Linclust是O(n)时间复杂度——序列数翻倍,时间也只翻倍
# easy-cluster是O(n*k)——更准确但更慢
# 数据量>1亿条序列时Linclust是唯一选择

3.3 分步运行(更灵活)

# === 分步运行MMseqs2聚类 ===

# 第1步:创建数据库
mmseqs createdb predicted_genes.fna queryDB  # 创建MMseqs2数据库

# 第2步:聚类
mmseqs cluster \
    queryDB \                     # 输入数据库
    clusterDB \                   # 聚类结果数据库
    tmp/ \                        # 临时目录
    --min-seq-id 0.95 \           # 相似度阈值
    -c 0.9 \                      # 覆盖度
    --cov-mode 1 \                # 覆盖模式
    --threads 16                  # 线程数

# 第3步:提取代表序列
mmseqs createsubdb clusterDB queryDB repDB  # 创建代表序列子数据库
mmseqs convert2fasta repDB rep_seqs.fasta   # 转为FASTA格式

# 第4步:输出聚类成员关系
mmseqs createtsv queryDB queryDB clusterDB cluster.tsv  # 输出TSV

3.4 覆盖模式详解

# --cov-mode 参数说明:
# 0 = 双向覆盖:query和target都要满足覆盖度阈值
# 1 = target覆盖:较短序列被覆盖≥阈值(最常用)
# 2 = query覆盖:query被覆盖≥阈值
# 3 = target序列长度被覆盖≥阈值
# 5 = 较短序列被覆盖≥阈值(类似CD-HIT的-aS)

# 宏基因组推荐:--cov-mode 1 -c 0.9
# 含义:较短的基因序列至少90%被较长的代表序列覆盖

四、三工具性能对比

特性CD-HITMMseqs2 clusterMMseqs2 Linclust
算法贪心增量k-mer预筛+Smith-Waterman线性时间k-mer聚类
时间复杂度O(n*k)O(n*k)O(n)
速度(10M序列)~10小时~30分钟~5分钟
速度比1x~20x~100-2300x
灵敏度中等偏高
最大数据量~千万级~亿级~十亿级
内存使用中等
最低相似度核苷酸80%/蛋白40%无下限无下限
蛋白聚类支持支持支持
核苷酸聚类支持支持支持

五、宏基因组非冗余基因集构建实战

#!/bin/bash
# === 完整的宏基因组非冗余基因集构建流程 ===

# 变量设置
GENE_DIR="prodigal_output"          # Prodigal预测结果目录
THREADS=32                          # 线程数
IDENTITY=0.95                       # 相似度阈值
COVERAGE=0.9                        # 覆盖度阈值

# === 第1步:合并所有样本的预测基因 ===
echo "合并所有样本的预测基因..."
cat ${GENE_DIR}/*_genes.fna > all_predicted_genes.fna  # 合并核苷酸
cat ${GENE_DIR}/*_proteins.faa > all_predicted_proteins.faa  # 合并蛋白

echo "总基因数: $(grep -c '^>' all_predicted_genes.fna)"

# === 第2步:去除短序列(<100bp)===
echo "过滤短序列..."
seqkit seq -m 100 all_predicted_genes.fna > filtered_genes.fna  # 过滤<100bp
echo "过滤后基因数: $(grep -c '^>' filtered_genes.fna)"

# === 第3步:用MMseqs2 Linclust聚类 ===
echo "开始MMseqs2 Linclust聚类..."
mmseqs easy-linclust \
    filtered_genes.fna \              # 输入
    gene_catalog \                    # 输出前缀
    tmp_mmseqs/ \                     # 临时目录
    --min-seq-id ${IDENTITY} \        # 相似度95%
    -c ${COVERAGE} \                  # 覆盖度90%
    --cov-mode 1 \                    # 较短序列覆盖模式
    --threads ${THREADS}              # 线程数

# === 第4步:统计结果 ===
NR_GENES=$(grep -c '^>' gene_catalog_rep_seq.fasta)
TOTAL_GENES=$(grep -c '^>' filtered_genes.fna)
REDUCTION=$(echo "scale=1; (1 - ${NR_GENES}/${TOTAL_GENES}) * 100" | bc)

echo "=== 聚类结果 ==="
echo "输入基因数: ${TOTAL_GENES}"
echo "非冗余基因数: ${NR_GENES}"
echo "冗余率: ${REDUCTION}%"

# === 第5步:清理临时文件 ===
rm -rf tmp_mmseqs/  # 删除临时目录
echo "完成!非冗余基因集: gene_catalog_rep_seq.fasta"

5.1 基因丰度定量

# === 用非冗余基因集做丰度定量 ===

# 第1步:建立索引
bwa index gene_catalog_rep_seq.fasta  # BWA建索引

# 第2步:将每个样本的reads比对到基因集
for sample in samples/*_R1.fastq.gz; do
    name=$(basename "$sample" _R1.fastq.gz)  # 提取样本名

    # BWA比对
    bwa mem \
        -t 16 \                    # 16线程
        gene_catalog_rep_seq.fasta \  # 参考基因集
        ${sample} \                # 正向reads
        ${sample/_R1/_R2} \        # 反向reads
    | samtools sort -@ 4 -o ${name}.bam  # 排序

    # 统计比对数
    samtools idxstats ${name}.bam > ${name}_counts.tsv  # 基因计数
done

# 第3步:合并所有样本的计数
python3 -c "
import pandas as pd  # 导入pandas
import glob  # 文件匹配

# 读取所有样本的计数文件
count_files = sorted(glob.glob('*_counts.tsv'))  # 找所有计数文件
counts = {}  # 存储计数

for f in count_files:  # 遍历每个文件
    name = f.replace('_counts.tsv', '')  # 提取样本名
    df = pd.read_csv(f, sep='\t', header=None,
                     names=['gene', 'length', 'mapped', 'unmapped'])
    counts[name] = df.set_index('gene')['mapped']  # 取比对数

# 合并为矩阵
abundance = pd.DataFrame(counts)  # 创建丰度矩阵
abundance = abundance.fillna(0)  # 缺失值填0

# TPM归一化
gene_lengths = pd.read_csv(count_files[0], sep='\t', header=None,
                           names=['gene', 'length', 'mapped', 'unmapped'])
lengths = gene_lengths.set_index('gene')['length']  # 基因长度

rpk = abundance.div(lengths, axis=0) * 1e3  # reads per kilobase
tpm = rpk.div(rpk.sum(axis=0), axis=1) * 1e6  # TPM归一化

tpm.to_csv('gene_abundance_tpm.tsv', sep='\t')  # 保存TPM矩阵
print(f'基因丰度矩阵: {tpm.shape[0]} 基因 x {tpm.shape[1]} 样本')
"

六、常见报错与解决

报错信息原因解决方案
cd-hit-est: word size too large-n值与-c不匹配参照word size表选择正确值
Not enough memory序列太多内存不够用MMseqs2替代或增加-M值
mmseqs: tmp directory full临时目录空间不足指定大磁盘上的tmp目录
Sequences too short有极短序列用seqkit过滤掉<100bp序列
Different sequence types混入了蛋白/核苷酸确认输入序列类型一致
CD-HIT运行极慢数据量太大换MMseqs2 Linclust

七、常用阈值参考

应用场景工具选择相似度阈值覆盖度
宏基因组非冗余基因集MMseqs2 Linclust95%90%
蛋白功能聚类(同功能)CD-HIT / MMseqs250-70%80%
去除PCR重复cd-hit-dup100%100%
UniRef构建MMseqs250/90/100%80%
16S OTU聚类cd-hit-est97%80%
泛基因组核心基因CD-HIT90-95%80%

八、面试高频题

Q1:CD-HIT和MMseqs2的主要区别是什么?

答: CD-HIT用贪心增量算法——先将最长序列作为第一个代表,然后逐条比较剩余序列,相似度达标就归入已有聚类,否则创建新聚类。优点是实现简单、结果直观,缺点是O(n*k)时间复杂度,数据量大时极慢。MMseqs2用k-mer预筛+双向最优比对,速度快20-100倍。Linclust更进一步,用线性时间算法(O(n))处理超大数据集,在10亿条序列级别仍然可行。实际测试中,MMseqs2/Linclust在95%相似度下的聚类结果与CD-HIT高度一致,但速度提升数百倍。

Q2:为什么宏基因组要构建非冗余基因集?

答: 宏基因组通常有几十到上百个样本,每个样本可能预测出100万+基因。直接分析会有大量冗余(同一物种在不同样本中的同一基因被重复计算)。构建非冗余基因集的意义:(1) 去冗余——典型冗余率50-70%,大幅减少数据量;(2) 统一参考——所有样本的reads都比对到同一个基因集上,方便跨样本比较;(3) 提高下游分析效率——功能注释、丰度定量都只需处理非冗余集。常用标准是95%核苷酸相似度+90%覆盖度(IGC、UHGG等大型基因集都用此标准)。

Q3:聚类的覆盖度参数是什么意思?

答: 覆盖度是指较短序列被比对区域覆盖的比例。比如一条200bp的基因和一条300bp的基因比对,如果比对区域覆盖了200bp基因中的180bp,那覆盖度就是180/200=90%。设置覆盖度阈值是防止"部分相似就合并"——比如两个基因只有一个结构域相似但其他部分完全不同,不应该合并。宏基因组推荐覆盖度≥90%(--cov-mode 1 -c 0.9),这样只有真正高度相似的全长基因才会合并。

Q4:什么情况下用Linclust而不是MMseqs2 cluster?

答: Linclust适合数据量极大(>1000万条序列)且对精度要求不是特别极致的场景。Linclust是线性时间复杂度O(n),10亿条序列也能在合理时间内完成。trade-off是它可能会漏掉一些边缘相似的序列对。MMseqs2 cluster更准确(k-mer预筛后做完整比对),但O(n*k)。实际使用中,宏基因组基因集构建用Linclust足够了——结果与cluster模式差异<1%,但速度快10-100倍。如果是蛋白功能聚类(低阈值如50%)且追求高灵敏度,用cluster模式更好。


参考资料:CD-HIT: Fu et al., Bioinformatics 2012 | MMseqs2: Steinegger & Söding, Nat Biotechnol 2017 | Linclust: Steinegger & Söding, Nat Commun 2018 | Clusterize: Barrio-Hernandez et al., bioRxiv 2024 | IGC: Li et al., Nat Biotechnol 2014