Pipeline Detail
WGCNA 共表达网络分析
面向 bulk RNA-seq 表达矩阵的 WGCNA 共表达网络流程,覆盖表达矩阵预处理、软阈值选择、模块识别、模块-性状相关、hub gene 挖掘、富集分析与可视化解释。
Metadata
流程元数据
先看应用场景、输入输出和工具依赖,再进入正文命令细节。
Difficulty
中级
Scenario
转录组分析
Estimated Time
1-3 天
Tools
Inputs
Outputs
Workflow DAG
流程图
用步骤节点快速理解这个分析从原始数据到结果报告的流转关系。
表达矩阵与性状表
VST/TPM/log2 转换
低表达与低变异过滤
样本聚类与离群检测
软阈值选择
网络构建与 TOM
模块识别与合并
模块-性状相关
hub gene 筛选
富集与网络可视化
Protocol
流程文档
正文保留 Markdown 排版、代码语言标识和表格样式,适合边学边复现。
WGCNA 共表达网络分析
一、适用场景
WGCNA,全称 Weighted Gene Co-expression Network Analysis,是一种基于基因表达相关性构建加权共表达网络的方法。它的核心思想是:如果一批基因在多个样本中的表达模式高度一致,那么这些基因可能受到共同调控,或者参与同一类生物过程。
适用于:
- bulk RNA-seq、microarray、蛋白组、代谢组等连续表达矩阵。
- 寻找与表型、处理、时间、病理指标或生理指标相关的基因模块。
- 从大量表达基因中筛选模块 hub genes。
- 将差异表达分析从“单基因列表”扩展到“基因网络模块”层面。
- 多时间点、多组织、多处理组项目中的表达模式归纳。
不适合直接套用:
- 样本数过少。WGCNA 对样本数敏感,推荐至少 15 个样本,较稳定的网络通常需要 20-30 个以上样本。
- 没有连续表达变化或样本差异很小的项目。
- 只想做两组差异表达时,DESeq2/edgeR 更直接。
- 原始 count 未经过方差稳定转换时,不建议直接输入 WGCNA。
二、WGCNA 能回答什么问题
| 问题 | WGCNA 输出 | 怎么解释 |
|---|---|---|
| 哪些基因表达模式相似? | co-expression modules | 同一模块内基因具有相似表达趋势 |
| 哪些模块和性状最相关? | module-trait correlation heatmap | 相关系数越高,模块越可能与性状相关 |
| 模块里的代表表达趋势是什么? | module eigengene, ME | 可以理解为该模块的第一主成分 |
| 模块里最核心的基因是谁? | hub genes | 模块内连接度高,且与性状相关的基因 |
| 模块代表什么生物功能? | GO/KEGG/GSEA enrichment | 解释模块背后的通路和过程 |
三、关键概念图例
| 概念 | 类比 | 生信含义 |
|---|---|---|
| Gene | 网络节点 | 一个基因 |
| Edge | 连线 | 两个基因表达相关 |
| Weight | 连线粗细 | 相关性强弱经过软阈值转换后的权重 |
| Module | 颜色社区 | 一组共表达基因,例如 turquoise、blue、brown |
| Module eigengene | 模块代表曲线 | 模块整体表达趋势 |
| Trait | 外部性状 | 处理组、表型值、时间点、临床指标 |
| Hub gene | 核心节点 | 模块内连接度高且与性状相关的候选关键基因 |
示意:
Trait: drought severity
|
| corr = 0.82, p = 1e-05
v
turquoise module eigengene
|
| high module membership
v
Hub genes: NAC1, DREB2A, HSP70, ...
解释:如果 turquoise 模块和干旱程度高度正相关,且模块中的 DREB2A 同时具有较高 module membership 和 gene significance,那么它就是值得优先关注的候选 hub gene。
四、整体流程图
flowchart TD
A[RNA-seq count / TPM matrix] --> B[VST / log2(TPM+1) 转换]
B --> C[低表达和低变异基因过滤]
C --> D[样本聚类检测离群样本]
D --> E[选择 soft-threshold power]
E --> F[构建 adjacency matrix]
F --> G[计算 TOM similarity]
G --> H[层次聚类识别 modules]
H --> I[合并相似 modules]
I --> J[计算 module eigengenes]
J --> K[模块-性状相关热图]
K --> L[筛选 hub genes]
L --> M[GO/KEGG 富集和 Cytoscape 网络]
五、输入文件与目录建议
wgcna_project/
├── 00_metadata/
│ └── traits.csv
├── 01_expression/
│ ├── raw_counts.csv
│ ├── vst_matrix.csv
│ └── filtered_expr.csv
├── 02_qc/
│ ├── sample_clustering.pdf
│ └── soft_power.pdf
├── 03_wgcna/
│ ├── network.RData
│ ├── module_genes.csv
│ ├── module_trait_correlation.csv
│ └── hub_genes.csv
├── 04_enrichment/
├── 05_cytoscape/
└── report/
表达矩阵格式:
| gene_id | Ctrl_1 | Ctrl_2 | Treat_1 | Treat_2 |
|---|---|---|---|---|
| GeneA | 10.2 | 11.5 | 20.1 | 21.3 |
| GeneB | 3.2 | 2.9 | 1.1 | 1.4 |
WGCNA 的 R 输入通常要求:
行 = 样本
列 = 基因
性状表 traits.csv:
| sample_id | condition | drought_score | biomass |
|---|---|---|---|
| Ctrl_1 | Ctrl | 0 | 12.3 |
| Ctrl_2 | Ctrl | 0 | 13.1 |
| Treat_1 | Treat | 8 | 6.5 |
| Treat_2 | Treat | 9 | 5.9 |
六、表达矩阵预处理
1. 从 DESeq2 count 生成 VST 矩阵
对于 RNA-seq raw count,推荐先用 DESeq2 做 VST 或 rlog 变换,让低表达和高表达基因的方差更稳定。
library(DESeq2)
library(tidyverse)
count_data <- read.csv("01_expression/raw_counts.csv", row.names = 1, check.names = FALSE)
trait_data <- read.csv("00_metadata/traits.csv", row.names = 1)
count_data <- count_data[, rownames(trait_data)]
dds <- DESeqDataSetFromMatrix(
countData = round(as.matrix(count_data)),
colData = trait_data,
design = ~ 1
)
keep <- rowSums(counts(dds) >= 10) >= 3
dds <- dds[keep, ]
vsd <- vst(dds, blind = TRUE)
vst_matrix <- assay(vsd)
write.csv(vst_matrix, "01_expression/vst_matrix.csv")
也可以使用 TPM:
tpm <- read.csv("01_expression/tpm_matrix.csv", row.names = 1, check.names = FALSE)
log_tpm <- log2(tpm + 1)
write.csv(log_tpm, "01_expression/log2_tpm_matrix.csv")
建议:
- WGCNA 不建议直接输入 raw count。
- 如果是 DESeq2 流程,优先使用
vst。 - 如果只有 TPM/FPKM,可以使用
log2(TPM + 1)。 - 做 WGCNA 前应去掉低表达和低变异基因,减少噪音和计算量。
2. 过滤低变异基因
expr <- read.csv("01_expression/vst_matrix.csv", row.names = 1, check.names = FALSE)
gene_mad <- apply(expr, 1, mad)
top_genes <- names(sort(gene_mad, decreasing = TRUE))[1:min(8000, length(gene_mad))]
expr_filtered <- expr[top_genes, ]
write.csv(expr_filtered, "01_expression/filtered_expr.csv")
常见策略:
| 策略 | 说明 |
|---|---|
| 保留 MAD 最高的 5000-10000 个基因 | 常用、稳定、适合样本数中等项目 |
| 保留表达量前 50% 的基因 | 简单但可能保留低变异 housekeeping genes |
| 保留差异基因再做 WGCNA | 可行但有偏,会只看差异相关网络 |
七、样本聚类与离群检测
WGCNA 对离群样本非常敏感,必须先做样本聚类。
library(WGCNA)
options(stringsAsFactors = FALSE)
allowWGCNAThreads()
expr <- read.csv("01_expression/filtered_expr.csv", row.names = 1, check.names = FALSE)
datExpr <- as.data.frame(t(expr))
gsg <- goodSamplesGenes(datExpr, verbose = 3)
if (!gsg$allOK) {
datExpr <- datExpr[gsg$goodSamples, gsg$goodGenes]
}
sample_tree <- hclust(dist(datExpr), method = "average")
pdf("02_qc/sample_clustering.pdf", width = 10, height = 6)
plot(sample_tree, main = "Sample clustering to detect outliers", sub = "", xlab = "")
dev.off()
如果某个样本明显远离所有同组样本,应回看:
- FastQC/MultiQC 质量。
- mapping rate。
- library size。
- 样本标签是否混淆。
- PCA 是否也显示离群。
不要因为结果“不好看”随意删除样本,必须有 QC 证据。
八、软阈值 soft-threshold power 选择
WGCNA 不用硬阈值把相关性切成 0/1,而是用软阈值把相关性转换为网络连接权重。
powers <- c(1:10, seq(12, 30, by = 2))
sft <- pickSoftThreshold(
datExpr,
powerVector = powers,
networkType = "signed",
verbose = 5
)
pdf("02_qc/soft_power.pdf", width = 10, height = 5)
par(mfrow = c(1, 2))
plot(
sft$fitIndices[, 1],
-sign(sft$fitIndices[, 3]) * sft$fitIndices[, 2],
xlab = "Soft Threshold power",
ylab = "Scale Free Topology Model Fit, signed R^2",
type = "n",
main = "Scale independence"
)
text(
sft$fitIndices[, 1],
-sign(sft$fitIndices[, 3]) * sft$fitIndices[, 2],
labels = powers,
col = "red"
)
abline(h = 0.8, col = "red")
plot(
sft$fitIndices[, 1],
sft$fitIndices[, 5],
xlab = "Soft Threshold power",
ylab = "Mean connectivity",
type = "n",
main = "Mean connectivity"
)
text(sft$fitIndices[, 1], sft$fitIndices[, 5], labels = powers, col = "red")
dev.off()
选择原则:
| 指标 | 建议 |
|---|---|
| scale-free topology fit R² | 通常希望接近或超过 0.8 |
| mean connectivity | 不要过低,否则网络太稀疏 |
| signed network | 常用于保留正负相关方向,power 往往比 unsigned 更高 |
| 样本数较少 | 不要机械追求 R²,结合模块稳定性判断 |
示例解释:
如果 power = 12 时 R² 达到 0.82,mean connectivity 仍然不低,
可以选择 softPower = 12。
如果 R² 一直达不到 0.8,选择曲线趋于平稳处,并在报告中说明。
九、构建网络和识别模块
1. 自动网络构建
softPower <- 12
net <- blockwiseModules(
datExpr,
power = softPower,
networkType = "signed",
TOMType = "signed",
minModuleSize = 30,
reassignThreshold = 0,
mergeCutHeight = 0.25,
numericLabels = TRUE,
pamRespectsDendro = FALSE,
saveTOMs = TRUE,
saveTOMFileBase = "03_wgcna/TOM",
verbose = 3
)
moduleColors <- labels2colors(net$colors)
MEs <- net$MEs
save(datExpr, net, moduleColors, MEs, file = "03_wgcna/network.RData")
关键参数:
| 参数 | 说明 |
|---|---|
networkType = "signed" | 保留正相关方向,更适合生物解释 |
TOMType = "signed" | 基于 signed network 计算拓扑重叠 |
minModuleSize | 最小模块大小,常用 30-50 |
mergeCutHeight | 合并相似模块,0.25 表示 eigengene 相关性约 0.75 |
power | 软阈值 |
2. 模块树图
pdf("03_wgcna/gene_dendrogram_modules.pdf", width = 12, height = 6)
plotDendroAndColors(
net$dendrograms[[1]],
moduleColors[net$blockGenes[[1]]],
"Module colors",
dendroLabels = FALSE,
hang = 0.03,
addGuide = TRUE,
guideHang = 0.05
)
dev.off()
图例解释:
上方树状图:基因按照共表达相似性聚类。
下方颜色条:WGCNA 分配的模块颜色。
同一颜色中的基因表达模式更相似。
grey 模块通常表示未归入明确模块的基因。
十、模块-性状相关分析
1. 准备性状矩阵
trait_data <- read.csv("00_metadata/traits.csv", row.names = 1)
trait_data <- trait_data[rownames(datExpr), ]
trait_data$condition_binary <- ifelse(trait_data$condition == "Treat", 1, 0)
numeric_traits <- trait_data |>
dplyr::select(where(is.numeric))
2. 模块与性状相关热图
MEs0 <- moduleEigengenes(datExpr, moduleColors)$eigengenes
MEs <- orderMEs(MEs0)
module_trait_cor <- cor(MEs, numeric_traits, use = "p")
module_trait_pvalue <- corPvalueStudent(module_trait_cor, nSamples = nrow(datExpr))
text_matrix <- paste(
signif(module_trait_cor, 2),
"
(",
signif(module_trait_pvalue, 1),
")",
sep = ""
)
dim(text_matrix) <- dim(module_trait_cor)
pdf("03_wgcna/module_trait_heatmap.pdf", width = 9, height = 8)
labeledHeatmap(
Matrix = module_trait_cor,
xLabels = names(numeric_traits),
yLabels = names(MEs),
ySymbols = names(MEs),
colorLabels = FALSE,
colors = blueWhiteRed(50),
textMatrix = text_matrix,
setStdMargins = FALSE,
cex.text = 0.8,
zlim = c(-1, 1),
main = "Module-trait relationships"
)
dev.off()
write.csv(module_trait_cor, "03_wgcna/module_trait_correlation.csv")
write.csv(module_trait_pvalue, "03_wgcna/module_trait_pvalue.csv")
热图读法:
| 示例 | 解释 |
|---|---|
MEturquoise vs drought_score: 0.82 (1e-05) | turquoise 模块和干旱程度显著正相关 |
MEblue vs biomass: -0.71 (0.002) | blue 模块表达越高,biomass 越低 |
MEgrey | 未归类基因模块,一般不作为重点解释 |
十一、筛选 hub genes
hub gene 通常同时满足:
- 属于目标模块。
- 与模块 eigengene 高相关,即 module membership 高。
- 与目标性状高相关,即 gene significance 高。
- 有合理生物学功能或文献支持。
target_module <- "turquoise"
target_trait <- "drought_score"
module_genes <- moduleColors == target_module
MM <- as.data.frame(cor(datExpr, MEs, use = "p"))
MMPvalue <- as.data.frame(corPvalueStudent(as.matrix(MM), nrow(datExpr)))
GS <- as.data.frame(cor(datExpr, numeric_traits[[target_trait]], use = "p"))
GSPvalue <- as.data.frame(corPvalueStudent(as.matrix(GS), nrow(datExpr)))
gene_info <- data.frame(
gene_id = colnames(datExpr),
module = moduleColors,
module_membership = MM[[paste0("ME", target_module)]],
module_membership_pvalue = MMPvalue[[paste0("ME", target_module)]],
gene_significance = GS[, 1],
gene_significance_pvalue = GSPvalue[, 1]
)
hub_genes <- gene_info |>
dplyr::filter(
module == target_module,
abs(module_membership) >= 0.8,
abs(gene_significance) >= 0.5
) |>
dplyr::arrange(desc(abs(module_membership)), desc(abs(gene_significance)))
write.csv(gene_info, "03_wgcna/all_gene_module_membership.csv", row.names = FALSE)
write.csv(hub_genes, "03_wgcna/hub_genes_turquoise.csv", row.names = FALSE)
MM-GS 散点图
library(ggplot2)
library(ggrepel)
plot_df <- gene_info |>
dplyr::filter(module == target_module) |>
dplyr::mutate(label = ifelse(row_number() <= 10, gene_id, NA))
ggplot(plot_df, aes(abs(module_membership), abs(gene_significance))) +
geom_point(alpha = 0.7, color = target_module) +
geom_text_repel(aes(label = label), max.overlaps = 20) +
theme_bw() +
labs(
x = paste0("Module Membership in ", target_module),
y = paste0("Gene Significance for ", target_trait),
title = "Hub gene screening"
)
ggsave("03_wgcna/MM_vs_GS_turquoise.pdf", width = 7, height = 6)
图例解释:
x 轴越靠右:基因越能代表该模块。
y 轴越靠上:基因越和目标性状相关。
右上角基因:优先候选 hub genes。
十二、导出 Cytoscape 网络
如果需要给读者展示模块内部网络,可以导出 Cytoscape 文件。
load("03_wgcna/network.RData")
target_module <- "turquoise"
module_genes <- moduleColors == target_module
module_expr <- datExpr[, module_genes]
TOM <- TOMsimilarityFromExpr(
module_expr,
power = softPower,
networkType = "signed",
TOMType = "signed"
)
dimnames(TOM) <- list(colnames(module_expr), colnames(module_expr))
exportNetworkToCytoscape(
TOM,
edgeFile = "05_cytoscape/turquoise_edges.txt",
nodeFile = "05_cytoscape/turquoise_nodes.txt",
weighted = TRUE,
threshold = 0.08,
nodeNames = colnames(module_expr),
nodeAttr = target_module
)
Cytoscape 图例建议:
| 元素 | 建议含义 |
|---|---|
| 节点大小 | module membership 或 intramodular connectivity |
| 节点颜色 | gene significance 或 log2FC |
| 边粗细 | TOM weight |
| 标签 | top hub genes |
十三、模块功能富集
library(clusterProfiler)
library(org.Hs.eg.db)
target_genes <- gene_info |>
dplyr::filter(module == "turquoise") |>
dplyr::pull(gene_id)
entrez <- mapIds(
org.Hs.eg.db,
keys = target_genes,
column = "ENTREZID",
keytype = "ENSEMBL",
multiVals = "first"
) |>
na.omit()
ego <- enrichGO(
gene = entrez,
OrgDb = org.Hs.eg.db,
keyType = "ENTREZID",
ont = "BP",
pAdjustMethod = "BH",
pvalueCutoff = 0.05
)
write.csv(as.data.frame(ego), "04_enrichment/turquoise_GO_BP.csv", row.names = FALSE)
解释方式:
- 如果 turquoise 模块与干旱强度正相关,且富集到 ABA response、water deprivation、oxidative stress,可以推测该模块参与干旱响应。
- 如果 blue 模块与 biomass 负相关,且富集到 defense response,可能说明胁迫防御激活伴随生长抑制。
十四、一个完整解释例子
假设得到下面结果:
| 模块 | 相关性最高性状 | cor | p-value | 富集功能 | 代表 hub gene |
|---|---|---|---|---|---|
| turquoise | drought_score | 0.82 | 1e-05 | response to water deprivation | DREB2A, HSP70 |
| blue | biomass | -0.71 | 0.002 | defense response | WRKY33, PR1 |
| brown | chlorophyll | 0.66 | 0.006 | photosynthesis | LHCA1, RBCS |
可以这样写结论:
turquoise 模块与干旱评分显著正相关,说明该模块基因整体随干旱程度升高而上调。
该模块富集于水分胁迫和 ABA 响应通路,其中 DREB2A 同时具有较高 module membership 和 gene significance,
因此可作为后续验证的候选调控基因。
十五、常见问题与排查
| 问题 | 可能原因 | 建议 |
|---|---|---|
| 模块数量很少 | 过滤太严格、样本差异弱、mergeCutHeight 太低 | 放宽过滤,调整 minModuleSize 和 mergeCutHeight |
| grey 模块过大 | 很多基因无法归入稳定模块 | 检查表达矩阵质量和低变异基因过滤 |
| soft power 无法达到 R² 0.8 | 样本数少、离群样本、批次效应 | 检查样本聚类,不要机械追求阈值 |
| 模块和性状全不相关 | 性状编码不合理或生物信号弱 | 检查 trait 表和样本顺序 |
| hub gene 全是 housekeeping genes | 输入基因过滤不合理 | 去掉低变异/低信息基因 |
| 网络太大难以画图 | 节点太多、边阈值太低 | 只导出目标模块 top edges |
十六、主要交付物
- VST/log2 transformed expression matrix
- 样本聚类树和离群样本检查图
- soft-threshold power 选择图
- gene dendrogram and module color 图
- module eigengene 表
- module-trait correlation heatmap
- 每个模块的基因清单
- 目标模块 hub gene 表
- MM-GS scatter plot
- 模块 GO/KEGG 富集结果
- Cytoscape network edge/node 文件
- 解释性报告和候选基因清单
十七、参考资料
- WGCNA 官方教程:网络构建、模块识别、模块-性状关系和 hub gene 筛选。
- WGCNA BMC Bioinformatics 原始论文:介绍 weighted correlation network、模块检测和拓扑性质。
- DESeq2 / Bioconductor RNA-seq workflow:用于 RNA-seq count 的 VST/rlog 方差稳定转换。