跳转至

宏基因组组装后分析(Post-Assembly Analysis)

一句话概述

宏基因组组装后分析涵盖从contigs出发的ORF预测(Prodigal)、基因聚类去冗余(CD-HIT)、功能注释(eggNOG/KEGG/dbCAN)和基因丰度定量(Salmon/CoverM),构建非冗余基因集并量化各样本的功能潜力。


核心知识点表格

知识点说明
ORF预测从组装contigs上预测蛋白编码基因(Open Reading Frame)
Prodigal原核生物基因预测的标准工具,宏基因组使用-p meta模式
CD-HIT基于序列相似性的快速聚类去冗余工具
非冗余基因集(NR gene catalog)去除冗余后的代表性基因集合,用于统一定量
Salmon/Bowtie2基因丰度定量工具(pseudo-alignment或传统比对)
TPM/RPKM/CPM丰度归一化方法
eggNOG/KEGG/GO功能注释数据库
dbCAN碳水化合物活性酶(CAZyme)专项数据库
功能丰度表KO/COG × 样本的定量矩阵,下游差异分析输入
Gene catalog规模人肠道参考基因集(IGC)约990万基因

各步骤详解

第一步:ORF预测(Prodigal)

白话解释: 组装得到的contigs是长DNA片段,里面有很多编码蛋白的区域(基因)。Prodigal用数学模型(基于密码子使用频率和核糖体结合位点特征)自动找出这些基因的位置和方向。

技术细节: - Prodigal使用动态规划算法,基于GC含量自适应训练模型 - -p meta模式:对每个contig独立训练(适合宏基因组的GC多样性) - -p single模式:所有序列用同一模型(适合单基因组) - 输出:GFF(坐标)、FAA(蛋白序列)、FNA(核苷酸序列) - 对于短contig(<500bp),预测精度下降 - Prodigal-short/Pyrodigal是优化版本 - 注意:Prodigal 3.x中-p meta已更名为-p anon(anonymous mode),但仍兼容旧语法-p meta

代码示例:

# 基本运行
prodigal -i assembled_contigs.fa \
         -o genes.gff \          # GFF格式基因坐标
         -a proteins.faa \       # 蛋白质序列
         -d genes.fna \          # 核苷酸CDS序列
         -p meta \               # 宏基因组模式(必须!)
         -f gff \                # 输出格式
         -q                      # 静默模式

# 统计预测结果
echo "Predicted genes: $(grep -c '>' proteins.faa)"
echo "Complete genes: $(grep -c 'partial=00' genes.gff)"
echo "Partial genes (5'): $(grep -c 'partial=10' genes.gff)"
echo "Partial genes (3'): $(grep -c 'partial=01' genes.gff)"
echo "Partial genes (both): $(grep -c 'partial=11' genes.gff)"

# 过滤短基因(可选,<100aa通常不可靠)
seqkit seq -m 100 proteins.faa > proteins_filtered.faa

# 多样本并行处理
for sample in sample1 sample2 sample3; do
  prodigal -i ${sample}_contigs.fa \
           -a ${sample}_proteins.faa \
           -d ${sample}_genes.fna \
           -p meta -f gff -q \
           -o ${sample}_genes.gff
done

# 合并所有样本的预测基因(添加样本前缀防止ID冲突)
for sample in sample1 sample2 sample3; do
  sed "s/^>/>${sample}_/" ${sample}_genes.fna >> all_genes.fna
  sed "s/^>/>${sample}_/" ${sample}_proteins.faa >> all_proteins.faa
done

第二步:基因聚类去冗余(CD-HIT)

白话解释: 多个样本预测出来的基因有大量重复(同一物种在不同样本中都存在)。CD-HIT把序列相似的基因聚成一簇,每簇只留一条代表序列,大大减少数据量。这就是"非冗余基因集"。

技术细节: - 标准阈值:95%序列一致性 + 90%覆盖度(模拟同一基因的不同拷贝) - CD-HIT-EST用于核苷酸序列,CD-HIT用于蛋白质序列 - 对于大数据集(>1亿条),使用分步聚类或MMseqs2/Linclust替代 - 代表序列(representative)通常是每簇中最长的那条 - 输出:代表序列FASTA + 聚类关系文件(.clstr)

代码示例:

# === 核苷酸水平聚类(推荐,保留定量能力)===
cd-hit-est -i all_genes.fna \
           -o nr_genes.fna \
           -c 0.95 \          # 95%序列一致性
           -aS 0.9 \          # 90%短序列覆盖度
           -n 10 \            # word size(c>0.9时用10)
           -d 0 \             # 描述信息全部保留
           -M 64000 \         # 内存限制(MB)
           -T 16 \            # 线程数
           -g 1               # 精确聚类模式

# === 蛋白质水平聚类 ===
cd-hit -i all_proteins.faa \
       -o nr_proteins.faa \
       -c 0.95 \
       -aS 0.9 \
       -n 5 \               # word size(c>0.9时用5)
       -d 0 \
       -M 64000 \
       -T 16 \
       -g 1

# === 大数据集分步聚类策略 ===
# 先以较低阈值粗聚,再精聚
cd-hit-est -i all_genes.fna -o step1.fna -c 0.99 -aS 0.9 -n 10 -M 64000 -T 16
cd-hit-est -i step1.fna -o nr_genes.fna -c 0.95 -aS 0.9 -n 10 -M 64000 -T 16

# === 使用MMseqs2(更快,适合超大数据集)===
mmseqs easy-linclust all_genes.fna nr_genes tmp \
  --min-seq-id 0.95 \
  -c 0.9 \
  --cov-mode 1 \
  --threads 32

# 统计结果
echo "Total input genes: $(grep -c '>' all_genes.fna)"
echo "Non-redundant genes: $(grep -c '>' nr_genes.fna)"
echo "Redundancy rate: $(echo "scale=2; (1-$(grep -c '>' nr_genes.fna)/$(grep -c '>' all_genes.fna))*100" | bc)%"

# 解析聚类文件
# 获取每个cluster的成员数
grep "^>" nr_genes.fna.clstr | sed 's/.*Cluster //' | sort -n | tail

第三步:功能注释

白话解释: 拿到非冗余基因集后,需要知道每个基因是做什么的。通过比对到功能数据库(KEGG、COG、CAZy等),为每个基因贴上功能标签。

技术细节: - 蛋白质序列注释比核苷酸更灵敏 - 多数据库并行注释获得多维信息 - 注释工具选择:eggNOG-mapper(一站式)、KofamScan(KEGG专项)、dbCAN(CAZy专项)

代码示例:

# === 1. eggNOG-mapper(一站式注释)===
# 翻译非冗余核苷酸为蛋白质(如果之前是核苷酸聚类)
# 或直接使用蛋白质聚类结果
emapper.py -i nr_proteins.faa \
           -m diamond \
           --itype proteins \
           --data_dir /data/eggnog_db/ \
           --cpu 32 \
           --output nr_gene_emapper \
           --output_dir ./annotation/ \
           --override

# === 2. KofamScan(KEGG KO注释,比eggNOG的KEGG注释更准确)===
# 下载KOfam数据库
wget ftp://ftp.genome.jp/pub/db/kofam/ko_list.gz
wget ftp://ftp.genome.jp/pub/db/kofam/profiles.tar.gz
gunzip ko_list.gz; tar xzf profiles.tar.gz

# 运行
exec_annotation -o kofam_results.txt \
  --profile profiles/ \
  --ko-list ko_list \
  --cpu 32 \
  --tmp-dir /tmp/kofam/ \
  nr_proteins.faa

# 提取显著KO(带*号的)
grep "^\*" kofam_results.txt | awk '{print $2"\t"$3}' > significant_ko.tsv

# === 3. dbCAN(CAZyme注释)===
# 使用run_dbcan
run_dbcan nr_proteins.faa protein \
  --db_dir /data/dbCAN_db/ \
  --out_dir ./dbcan_results/ \
  --dia_cpu 16 \
  --hmm_cpu 16 \
  --tf_cpu 16

# 推荐取至少2种方法一致的结果
awk '$5 >= 2' dbcan_results/overview.txt > dbcan_consensus.txt

# === 4. CARD(抗性基因)===
rgi main -i nr_proteins.faa \
  -o rgi_results \
  -t protein \
  -a DIAMOND \
  --clean \
  -n 16

# === 5. VFDB(毒力因子)===
diamond blastp -d /data/VFDB/VFDB_setA_pro.dmnd \
  -q nr_proteins.faa \
  -o vfdb_results.txt \
  --outfmt 6 \
  -e 1e-5 \
  --id 40 \
  --query-cover 70 \
  -p 16

第四步:基因丰度定量

白话解释: 有了非冗余基因集,还需要知道每个基因在每个样本中有多少。方法是把每个样本的原始reads比对回非冗余基因集,统计各基因被覆盖的reads数。

技术细节: - 方法1:Salmon(pseudo-alignment,更快更准,推荐) - 方法2:Bowtie2/BWA比对 + featureCounts/bedtools统计 - 方法3:CoverM(直接从BAM计算覆盖度) - 归一化:TPM(Transcripts Per Million)考虑基因长度;RPKM/FPKM;CPM - 推荐TPM:在不同样本间可比

代码示例:

# ========== 方法1:Salmon(推荐)==========
# 1. 建立Salmon索引
salmon index -t nr_genes.fna -i salmon_index -p 16

# 2. 各样本定量
for sample in sample1 sample2 sample3; do
  salmon quant -i salmon_index \
    -l A \
    -1 ${sample}_clean_R1.fastq.gz \
    -2 ${sample}_clean_R2.fastq.gz \
    -o salmon_quant/${sample} \
    -p 16 \
    --meta \
    --validateMappings
done

# 3. 合并各样本的TPM/count
# 提取TPM
for sample in sample1 sample2 sample3; do
  tail -n +2 salmon_quant/${sample}/quant.sf | \
    awk -v s=${sample} 'BEGIN{OFS="\t"}{print $1, $4}' > salmon_quant/${sample}_tpm.txt
done

# 合并成矩阵
paste salmon_quant/*_tpm.txt | \
  awk 'BEGIN{OFS="\t"}{printf $1; for(i=2;i<=NF;i+=2) printf "\t"$i; print ""}' > gene_tpm_matrix.tsv

# ========== 方法2:Bowtie2 + SAMtools ==========
# 1. 建索引
bowtie2-build --threads 16 nr_genes.fna bt2_index

# 2. 比对
for sample in sample1 sample2 sample3; do
  bowtie2 -x bt2_index \
    -1 ${sample}_clean_R1.fastq.gz \
    -2 ${sample}_clean_R2.fastq.gz \
    -S ${sample}_mapped.sam \
    --no-unal \
    -p 16 \
    --very-sensitive

  samtools view -bS -@ 8 ${sample}_mapped.sam | \
    samtools sort -@ 8 -o ${sample}_mapped_sorted.bam
  samtools index ${sample}_mapped_sorted.bam
  rm ${sample}_mapped.sam
done

# 3. 统计每个基因的count
# 使用featureCounts(需要GTF/SAF格式的注释)
# 先将GFF转SAF
awk -F'\t' 'BEGIN{OFS="\t"; print "GeneID\tChr\tStart\tEnd\tStrand"} 
  /^[^#]/ && $3=="CDS" {
    match($9, /ID=([^;]+)/, arr); 
    print arr[1], $1, $4, $5, $7
  }' genes.gff > genes.saf

featureCounts -a genes.saf -F SAF \
  -o gene_counts.txt \
  -T 16 \
  -p --countReadPairs \
  sample1_mapped_sorted.bam \
  sample2_mapped_sorted.bam \
  sample3_mapped_sorted.bam

# ========== 方法3:CoverM ==========
# 计算基因覆盖度
coverm genome \
  --bam-files sample1.bam sample2.bam sample3.bam \
  --genome-fasta-files nr_genes.fna \
  --methods rpkm count covered_bases \
  --threads 16 \
  > coverm_results.tsv

第五步:构建功能丰度表

白话解释: 把基因丰度和功能注释合并:每个基因有一个丰度值,也有功能标签(如KO号)。把同一个KO号的所有基因丰度加起来,就得到该KO在该样本中的"功能丰度"。

代码示例:

# R语言构建KO丰度表
library(tidyverse)

# 读取基因丰度矩阵(TPM)
gene_tpm <- read_tsv("gene_tpm_matrix.tsv")

# 读取KO注释
ko_annotation <- read_tsv("significant_ko.tsv",
                          col_names = c("gene_id", "KO"))

# 合并
gene_ko_tpm <- gene_tpm %>%
  inner_join(ko_annotation, by = c("gene" = "gene_id"))

# 按KO汇总丰度(同一KO的基因TPM求和)
ko_abundance <- gene_ko_tpm %>%
  group_by(KO) %>%
  summarise(across(starts_with("sample"), sum, na.rm = TRUE)) %>%
  ungroup()

# 保存KO丰度表
write_tsv(ko_abundance, "ko_abundance_matrix.tsv")

# 同理构建COG丰度表
cog_annotation <- read_tsv("annotation/nr_gene_emapper.emapper.annotations",
                            comment = "##") %>%
  select(query, COG_category) %>%
  filter(COG_category != "-")

# KEGG Module完整性评估
# 读取KEGG Module定义(哪些KO组成一个Module)
# 计算每个样本中每个Module的完整度
# Python版本
import pandas as pd
import numpy as np

# 读取
gene_tpm = pd.read_csv("gene_tpm_matrix.tsv", sep="\t", index_col=0)
ko_anno = pd.read_csv("significant_ko.tsv", sep="\t", names=["gene_id", "KO"])

# 合并并汇总
merged = gene_tpm.merge(ko_anno, left_index=True, right_on="gene_id")
ko_abundance = merged.groupby("KO").sum(numeric_only=True)
ko_abundance.to_csv("ko_abundance_matrix.tsv", sep="\t")

# KEGG Pathway丰度
# 从KO映射到Pathway(使用KEGG REST API或本地文件)

第六步:下游差异分析

白话解释: 有了功能丰度表(KO × 样本矩阵),就可以做组间差异比较——哪些功能通路在疾病组vs健康组中丰度显著不同。

代码示例:

# 使用DESeq2对功能丰度做差异分析
library(DESeq2)

# 将TPM转为近似count(如果用Salmon count输出则直接用)
# 或使用ALDEx2/MaAsLin2(更适合组成性数据)
library(ALDEx2)

# KO丰度矩阵(行=KO,列=样本)
ko_matrix <- as.matrix(read.csv("ko_count_matrix.csv", row.names = 1))
conditions <- c(rep("Healthy", 5), rep("Disease", 5))

# ALDEx2差异分析
aldex_result <- aldex(ko_matrix, conditions, mc.samples = 128,
                      test = "t", effect = TRUE, denom = "all")

# 筛选差异KO
sig_ko <- aldex_result[aldex_result$we.eBH < 0.05 & abs(aldex_result$effect) > 0.5, ]

# KEGG pathway富集分析
library(clusterProfiler)
# 将差异KO映射到pathway做富集

实战命令(可复制)

# ===== 完整宏基因组组装后分析流水线 =====

# 假设已有各样本的组装结果:sample*_contigs.fa
THREADS=32

# 1. 批量ORF预测
for s in sample1 sample2 sample3 sample4 sample5; do
  prodigal -i ${s}_contigs.fa -a ${s}_prot.faa -d ${s}_gene.fna \
    -p meta -f gff -q -o ${s}.gff &
done
wait

# 2. 合并所有基因(加样本前缀)
> all_genes.fna
for s in sample1 sample2 sample3 sample4 sample5; do
  sed "s/^>/>${s}__/" ${s}_gene.fna >> all_genes.fna
done

# 3. CD-HIT聚类去冗余
cd-hit-est -i all_genes.fna -o nr_genes.fna \
  -c 0.95 -aS 0.9 -n 10 -d 0 -M 64000 -T ${THREADS} -g 1

echo "Non-redundant gene catalog: $(grep -c '>' nr_genes.fna) genes"

# 4. 翻译为蛋白序列
seqkit translate nr_genes.fna > nr_proteins.faa

# 5. 功能注释
emapper.py -i nr_proteins.faa -m diamond --itype proteins \
  --data_dir /data/eggnog_db/ --cpu ${THREADS} \
  --output nr_emapper --output_dir ./annotation/ --override

# 6. KofamScan
exec_annotation -o kofam_results.txt \
  --profile /data/kofam/profiles/ --ko-list /data/kofam/ko_list \
  --cpu ${THREADS} nr_proteins.faa

# 7. 定量(Salmon)
salmon index -t nr_genes.fna -i salmon_idx -p ${THREADS}
for s in sample1 sample2 sample3 sample4 sample5; do
  salmon quant -i salmon_idx -l A \
    -1 ${s}_R1.fq.gz -2 ${s}_R2.fq.gz \
    -o quant/${s} -p 8 --meta --validateMappings
done

# 8. 合并定量结果
python3 -c "
import pandas as pd, os
samples = ['sample1','sample2','sample3','sample4','sample5']
dfs = []
for s in samples:
    df = pd.read_csv(f'quant/{s}/quant.sf', sep='\t', usecols=['Name','TPM'])
    df = df.rename(columns={'TPM': s})
    dfs.append(df.set_index('Name'))
result = pd.concat(dfs, axis=1)
result.to_csv('gene_tpm_matrix.tsv', sep='\t')
print(f'Gene abundance matrix: {result.shape[0]} genes x {result.shape[1]} samples')
"

面试常问点

Q1: 为什么Prodigal宏基因组必须用-p meta模式?

A: 宏基因组包含来自不同物种的contigs,各物种GC含量和密码子偏好差异大。-p meta模式对每条contig独立学习编码特征模型,而-p single用全部序列训练一个模型,会被优势物种的特征主导,对稀有物种基因预测不准。

Q2: CD-HIT的95%一致性+90%覆盖度阈值依据是什么?

A: 95%核苷酸一致性近似"同一个基因的不同个体拷贝"的序列差异水平(同种内株间差异通常<5%)。90%覆盖度确保比较的是基本完整的同一基因而非片段匹配。这是MetaHIT/HMP等大型项目的标准做法,具有广泛认可度。

Q3: 为什么推荐Salmon而非Bowtie2+featureCounts进行定量?

A: (1) Salmon的pseudo-alignment比传统比对快10-100倍;(2) Salmon内置GC bias校正和fragment length建模;(3) Salmon用EM算法处理多映射reads(在基因集中很常见),而featureCounts默认丢弃或随机分配多映射;(4) TPM计算内置。缺点:Salmon输出没有BAM文件供后续位点级分析。

Q4: TPM和RPKM/CPM的区别及选择?

A: RPKM/FPKM:reads/fragments per kilobase per million mapped reads,各样本的归一化因子不同导致跨样本不直接可比。TPM:transcripts per million,先除基因长度再归一化到百万,保证各样本总和为100万,跨样本可比。宏基因组功能定量推荐TPM。CPM不考虑基因长度,适合所有基因长度相近的场景。

Q5: 如何评估非冗余基因集的质量?

A: (1) 基因集大小是否合理(人肠道约500万-1000万;土壤可能更多);(2) 各样本reads映射率(mapping rate > 70%说明基因集代表性好);(3) 基因长度分布(中位值应>500bp);(4) 完整基因比例(partial=00的比例>40%);(5) 功能注释率(>50%能被注释)。

Q6: 宏基因组基因定量和16S扩增子定量有什么本质区别?

A: (1) 16S只定量"谁在那里"(物种组成),宏基因组定量"能做什么"(功能潜力);(2) 16S受PCR偏好和16S拷贝数影响;(3) 宏基因组可以发现新基因/新功能;(4) 宏基因组能同时获得物种+功能信息。但宏基因组成本更高、分析更复杂。

Q7: MMseqs2和CD-HIT如何选择?

A: CD-HIT:经典工具,结果被广泛引用,适合<1亿条序列的中等数据集。MMseqs2/Linclust:速度快100x+,内存效率高,适合超大数据集(>1亿条),但参数体系不同于CD-HIT。大型项目(如Earth Microbiome Project规模)推荐MMseqs2。

Q8: 如何处理组装质量对下游分析的影响?

A: (1) 过滤短contig(<500bp)再预测基因,减少碎片化假基因;(2) 评估组装指标(N50、基因完整度);(3) 对于高碎片化样本,考虑co-assembly(多样本联合组装)提升连续性;(4) 使用unassembled reads直接做read-based分析(如HUMAnN3)作为补充。


易错点

1. 合并多样本基因时ID冲突

问题: Prodigal对每个样本都从ID=1开始编号,直接合并会有大量重复ID。 正确做法: 合并前给每条序列加样本前缀:sed "s/^>/>${sample}__/"

2. CD-HIT的-n参数与-c不匹配

问题: word size(n)必须与identity threshold(c)匹配,否则可能遗漏相似序列。 正确做法(CD-HIT-EST核苷酸): c=0.90~1.0用n=8,9,10;c=0.88~0.9用n=7;c=0.85~0.88用n=6;c=0.80~0.85用n=5;c=0.75~0.80用n=4。CD-HIT蛋白质: c=0.7~1.0用n=5;c=0.6~0.7用n=4;c=0.5~0.6用n=3;c=0.4~0.5用n=2。

3. 定量时使用相对丰度做差异分析

问题: TPM本身是组成性数据(总和恒定),直接做t检验会有假阳性。 正确做法: 使用count数据+DESeq2/ALDEx2,或对TPM做CLR变换后分析。

4. 未过滤Prodigal的partial genes就进入定量

问题: 部分基因(只有5'或3'端)长度很短,定量时容易被多映射reads污染。 正确做法: 可选择只保留complete genes(partial=00),或设置最低长度阈值(如>300bp/100aa)。

5. Salmon定量时忘记--meta参数

问题: Salmon默认参数针对转录组优化,宏基因组应使用--meta调整优化策略。 正确做法: 宏基因组定量时加--meta参数。--meta的实际效果:(1) 丰度优化从均匀分布初始化(而非默认的加权组合);(2) 禁用rich equivalence classes;(3) 使用EM算法替代默认的VBEM优化(因为VBEM先验参数是针对转录组设定的)。

6. 功能丰度汇总时一个基因被计入多个KO

问题: eggNOG-mapper可能给一个基因分配多个KO(逗号分隔),直接匹配会漏。 正确做法:separate_rows()str_split()将多KO拆分为多行,再汇总。注意这会导致总丰度"膨胀",需要决定是否平均分配。

7. 忽略mapping rate检查

问题: 如果大部分reads无法映射到非冗余基因集,说明基因集代表性差。 正确做法: 检查每个样本的mapping rate(Salmon日志或samtools flagstat),应>60-70%。低于此值需要检查组装质量或考虑混合策略。


补充知识

典型基因集规模参考

环境/项目基因集大小参考
人肠道(IGC)~990万Li et al. 2014
人肠道(UHGG)~1.7亿(基因组)Almeida et al. 2021
海洋(OM-RGC)~4000万Salazar et al. 2019
土壤~1000万+项目依赖

替代工具对比

步骤标准工具替代选择
ORF预测ProdigalMetaGeneMark, FragGeneScan (超短reads)
聚类CD-HITMMseqs2/Linclust, UCLUST
注释eggNOG-mapperInterProScan, GhostKOALA, DRAM
定量SalmonBowtie2+featureCounts, CoverM, BWA+HTSeq

与read-based方法的互补

方法组装依赖工具特点
Assembly-based本教程流程可发现新基因,适合高深度样本
Read-basedHUMAnN3, MEGAN不受组装质量影响,但只能注释数据库中有的功能
Hybrid部分-组合使用,取长补短

本教程覆盖了宏基因组组装后分析的完整流程,从ORF预测到功能丰度定量,适合构建非冗余基因集并开展功能比较研究。