在上一篇教程中,我们利用 iPHoP 成功为病毒找到了宿主。现在,我们手中已经掌握了病毒的身份(Taxonomy)和关系(Host-Virus link)。
接下来的核心问题是:“这些病毒在不同样本中发生了什么变化?”
为了回答这些问题,我们需要进行定量与差异分析。本篇将带你从原始测序数据出发,计算病毒丰度,并利用 R 语言进行生态统计。
要比较差异,首先得知道“有多少”。在宏病毒组中,我们通常将测序得到的 Clean Reads 比对回组装好的病毒序列(vOTUs),根据比对上的 Reads 数量来计算丰度。
Bowtie2: 目前最常用的短序列比对工具,速度快,内存占用低。用于将 Reads 比对到 vOTUs 上。SAMtools: 处理比对结果(SAM/BAM格式)的标准工具,用于排序和索引。CoverM: (强烈推荐)这是一个专门为宏基因组设计的封装工具,能自动调用 Bowtie2 并直接输出标准化的丰度表(TPM, RPKM, Count),省去了很多手写脚本的麻烦。# 推荐创建一个新的环境用于比对
mamba create -n mapping -c bioconda -c conda-forge coverm bowtie2 samtools
conda activate mapping
前提条件:
vOTUs.fasta: 你的病毒序列集合(去冗余后的)。SampleA_1.fq.gz, SampleA_2.fq.gz: 样本 A 的双端测序数据。SampleB_1.fq.gz, SampleB_2.fq.gz: 样本 B 的双端测序数据。传统方法(Bowtie2 + Samtools):
# 1. 建库
bowtie2-build vOTUs.fasta vOTUs_index
# 2. 比对 (以 SampleA 为例)
bowtie2 -x vOTUs_index -1 SampleA_1.fq.gz -2 SampleA_2.fq.gz -S SampleA.sam -p 16
# 3. 格式转换与排序
samtools view -bS SampleA.sam | samtools sort -o SampleA.sorted.bam
# 4. 统计 Reads 数 (idxstats)
samtools index SampleA.sorted.bam
samtools idxstats SampleA.sorted.bam > SampleA.counts.txt
进阶方法(CoverM,推荐):
CoverM 能够自动完成建库、比对、过滤和计算,一步到位。
# 计算 TPM (Transcripts Per Million) 和 Raw Counts
# --min-read-percent-identity 95: 要求 Read 必须 95% 相似才算比对上(严格质控)
# --min-covered-fraction 0.75: 要求病毒基因组至少被覆盖 75% 才算存在(防假阳性关键参数!)
coverm contig \
--reference vOTUs.fasta \
--coupled SampleA_1.fq.gz SampleA_2.fq.gz SampleB_1.fq.gz SampleB_2.fq.gz \
--min-read-percent-identity 95 \
--min-covered-fraction 0.75 \
--methods tpm count \
--threads 16 \
--output-file vOTUs_abundance_table.tsv
运行后,你会得到 vOTUs_abundance_table.tsv。
数据示例:

如何解读与“排雷”:
拿到了丰度表,我们转战 R 语言。这一步的目标是回答:“组间群落结构是否有显著差异?” 以及 “哪个病毒导致了这种差异?”
需要两个文件:
counts.txt: 行为 vOTU,列为样本的原始 Count 表。group.txt: 样本分组信息(如 Control vs Treatment)。# R 代码开始
library(vegan)
library(DESeq2)
library(ggplot2)
library(pheatmap)
# 1. 读取数据
counts <- read.table("vOTUs_abundance_table_counts_only.tsv", header=T, row.names=1, sep="\t")
metadata <- read.table("group.txt", header=T, row.names=1, sep="\t")
# 确保样本名称一致
counts <- counts[, rownames(metadata)]
α-多样性 (Alpha Diversity)
反映样本内部的病毒丰富度。
# 计算 Shannon 指数
alpha_div <- diversity(t(counts), index = "shannon")
# 结合分组画箱线图
plot_data <- data.frame(Sample = names(alpha_div), Shannon = alpha_div, Group = metadata$Group)
ggplot(plot_data, aes(x=Group, y=Shannon, fill=Group)) +
geom_boxplot() +
geom_jitter(width=0.2) +
theme_bw() +
ggtitle("Viral Alpha Diversity")
结果怎么看? 如果 Treatment 组的 Shannon 指数显著低于 Control 组,说明处理导致了病毒群落变得单一(可能是某种优势病毒爆发)。
β-多样性 (Beta Diversity)
反映样本间的群落结构差异。
# 计算 Bray-Curtis 距离矩阵 (通常建议先对 count 做标准化,这里简化演示)
dist_mat <- vegdist(t(counts), method = "bray")
# PCoA 分析
pcoa <- cmdscale(dist_mat, k=2, eig=TRUE)
pcoa_points <- data.frame(pcoa$points)
colnames(pcoa_points) <- c("PCoA1", "PCoA2")
pcoa_points$Group <- metadata$Group
# 绘图
ggplot(pcoa_points, aes(x=PCoA1, y=PCoA2, color=Group)) +
geom_point(size=3) +
stat_ellipse() + # 加置信椭圆
theme_bw() +
ggtitle("PCoA Analysis of Viral Communities")
结果是否正常?
这是文章中最“值钱”的结果——找到标志性病毒(Biomarker)。
# 1. 构建 DESeq 对象
dds <- DESeqDataSetFromMatrix(countData = counts,
colData = metadata,
design = ~ Group)
# 2. 运行差异分析
dds <- DESeq(dds)
# 3. 提取结果 (假设比较 Treatment vs Control)
res <- results(dds, contrast=c("Group", "Treatment", "Control"))
# 4. 筛选显著差异的 vOTUs (阈值:Padj < 0.05 且 差异倍数 > 2)
diff_vOTUs <- subset(res, padj < 0.05 & abs(log2FoldChange) > 1)
# 5. 保存结果
write.csv(as.data.frame(diff_vOTUs), "Differential_vOTUs.csv")
数据结果解析: 生成的 CSV 文件中重点看以下两列:
单纯说 "vOTU_123 显著增加" 没人在意。但如果你结合上一篇的 iPHoP 结果,说:
“在处理组中,感染 Bacteroides(拟杆菌)的 vOTU_123 显著增加,且与宿主丰度呈负相关。”
这就有很强的生物学意义了。 可视化建议(Volcano Plot with Host Info): 在画火山图时,不仅用颜色区分 上调/下调,还可以根据 Host Taxonomy 给点设置不同的形状。