跳转至

基因共表达网络分析(WGCNA)

一句话概述

WGCNA(Weighted Gene Co-expression Network Analysis)通过构建基因间加权相关网络、识别共表达模块、关联模块与表型性状,系统发现生物学功能相关的基因集群和潜在hub基因。


核心知识点表格

知识点说明
共表达网络基因间表达量的相关性构建的网络
软阈值(soft threshold)用幂函数加权相关性使网络近似无标度拓扑
无标度网络(scale-free)节点连通度服从幂律分布(少数hub基因连接极多)
TOM (Topological Overlap Matrix)考虑共享邻居的相似性度量,比相关系数更稳健
模块(Module)高度共表达的基因簇,用颜色命名
模块特征基因(Module Eigengene, ME)模块第一主成分,代表模块整体表达模式
Hub基因模块内连通度最高的基因,通常是关键调控因子
GS (Gene Significance)基因表达与性状的相关系数
MM (Module Membership)基因表达与模块特征基因的相关系数
性状关联将模块与临床/表型信息关联,找功能相关模块

各步骤详解

第一步:数据准备与预处理

白话解释: WGCNA需要一个基因表达矩阵(行=样本,列=基因),样本量不能太少(至少15个,推荐>25个)。数据需要先做标准的归一化和过滤,去除低表达基因和离群样本。

技术细节: - 输入:归一化后的表达矩阵(如RNA-seq的VST/rlog数据或log2(TPM+1)) - 样本量要求:最少15个样本,理想>25个 - 基因过滤:去除低表达和低变异基因(保留top 25-50%变异系数的基因,或MAD top 5000-10000) - 异常样本检测:层次聚类+cutreeStatic - 不要使用差异表达基因作为输入(WGCNA需要无偏的表达谱)

代码示例:

library(WGCNA)
options(stringsAsFactors = FALSE)
enableWGCNAThreads(nThreads = 8)

# 读取表达矩阵(行=样本,列=基因)
# 如果原始矩阵是基因×样本,需要转置
expr_data <- read.csv("expression_matrix.csv", row.names = 1)
datExpr <- as.data.frame(t(expr_data))  # 转置:行=样本,列=基因

# 读取性状/临床信息
traitData <- read.csv("clinical_traits.csv", row.names = 1)

# === 基因过滤 ===
# 方法1:去除低表达基因(至少在一定比例样本中有表达)
# 方法2:保留变异最大的前N个基因
library(matrixStats)
gene_var <- colVars(as.matrix(datExpr))
names(gene_var) <- colnames(datExpr)
top_genes <- names(sort(gene_var, decreasing = TRUE))[1:5000]  # top 5000变异基因
datExpr <- datExpr[, top_genes]

# === 样本质控 ===
# 检查缺失值
gsg <- goodSamplesGenes(datExpr, verbose = 3)
if (!gsg$allOK) {
  datExpr <- datExpr[gsg$goodSamples, gsg$goodGenes]
}

# 样本层次聚类检测离群
sampleTree <- hclust(dist(datExpr), method = "average")
plot(sampleTree, main = "Sample Dendrogram", sub = "", xlab = "", cex.lab = 1.5)

# 去除离群样本(手动设定切割高度)
abline(h = 15, col = "red")  # 根据图形调整高度
clust <- cutreeStatic(sampleTree, cutHeight = 15, minSize = 10)
keepSamples <- (clust == 1)
datExpr <- datExpr[keepSamples, ]
traitData <- traitData[keepSamples, ]

cat("Samples:", nrow(datExpr), " Genes:", ncol(datExpr), "\n")

第二步:软阈值选择

白话解释: WGCNA用"软阈值"(power β)把基因间的相关系数提升为幂次方,让强相关更强、弱相关更弱。选择β的标准是让网络的连通度分布近似"无标度"(power-law)——自然中的生物网络大多是无标度的。

技术细节: - 软阈值公式:a_ij = |cor(x_i, x_j)|^β - 选择标准:R² > 0.85(Scale-free topology fit)且mean connectivity不能太低 - 通常范围:无符号/signed hybrid网络β=6左右(合理范围<15),有符号(signed)网络β=12左右(合理范围<30) - 如果R²怎么都达不到0.85,说明数据可能不适合WGCNA或需要进一步清理

代码示例:

# 选择软阈值
powers <- c(c(1:10), seq(12, 20, by = 2))

sft <- pickSoftThreshold(datExpr,
                         powerVector = powers,
                         networkType = "signed",  # 推荐signed网络
                         verbose = 5)

# 可视化
par(mfrow = c(1, 2))

# Scale-free topology fit (R²)
plot(sft$fitIndices[, 1], -sign(sft$fitIndices[, 3]) * sft$fitIndices[, 2],
     xlab = "Soft Threshold (power)",
     ylab = "Scale Free Topology Model Fit (R²)",
     type = "n",
     main = "Scale Independence")
text(sft$fitIndices[, 1], -sign(sft$fitIndices[, 3]) * sft$fitIndices[, 2],
     labels = powers, cex = 0.9, col = "red")
abline(h = 0.85, col = "red")

# Mean connectivity
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, cex = 0.9, col = "red")

# 选择power(通常选R²首次>0.85的最小power)
softPower <- sft$powerEstimate
cat("Selected soft threshold:", softPower, "\n")
# 如果自动选择不理想,手动设置(如6或8)
if (is.na(softPower)) softPower <- 8

第三步:网络构建与模块检测

白话解释: 有了软阈值后,WGCNA计算所有基因对之间的TOM(拓扑重叠矩阵),然后对TOM做层次聚类,切割聚类树得到"模块"——每个模块是一群高度共表达的基因,用颜色标记。

技术细节: - 一步法:blockwiseModules()(适合大数据集,自动分块) - 分步法:手动计算adjacency→TOM→聚类→切割(更灵活) - TOM考虑了共享邻居信息,比纯相关系数更稳健 - Dynamic Tree Cut用于自适应切割聚类树 - mergeCutHeight控制相似模块合并的阈值

代码示例:

# === 一步法(推荐初学者)===
net <- blockwiseModules(datExpr,
  power = softPower,
  networkType = "signed",
  TOMType = "signed",
  minModuleSize = 30,        # 最小模块基因数
  reassignThreshold = 0,
  mergeCutHeight = 0.25,     # 模块合并阈值(越小合并越少)
  numericLabels = TRUE,
  pamRespectsDendro = FALSE,
  saveTOMs = TRUE,
  saveTOMFileBase = "TOM",
  verbose = 3,
  maxBlockSize = 20000       # 内存限制相关
)

# 模块颜色
moduleColors <- labels2colors(net$colors)
table(moduleColors)

# 可视化模块聚类树
plotDendroAndColors(net$dendrograms[[1]],
                   moduleColors[net$blockGenes[[1]]],
                   "Module Colors",
                   dendroLabels = FALSE,
                   hang = 0.03,
                   addGuide = TRUE,
                   guideHang = 0.05)

# === 分步法(更灵活)===
# 1. 计算邻接矩阵
adjacency <- adjacency(datExpr, power = softPower, type = "signed")

# 2. 计算TOM
TOM <- TOMsimilarity(adjacency, TOMType = "signed")
dissTOM <- 1 - TOM

# 3. 层次聚类
geneTree <- hclust(as.dist(dissTOM), method = "average")

# 4. Dynamic Tree Cut
dynamicMods <- cutreeDynamic(dendro = geneTree,
                              distM = dissTOM,
                              deepSplit = 2,
                              pamRespectsDendro = FALSE,
                              minClusterSize = 30)

# 5. 合并相似模块
MEList <- moduleEigengenes(datExpr, colors = labels2colors(dynamicMods))
MEs <- MEList$eigengenes
MEDiss <- 1 - cor(MEs)
METree <- hclust(as.dist(MEDiss), method = "average")
plot(METree, main = "Clustering of module eigengenes")
abline(h = 0.25, col = "red")

merge <- mergeCloseModules(datExpr, labels2colors(dynamicMods), cutHeight = 0.25)
moduleColors <- merge$colors
MEs <- merge$newMEs

第四步:模块-性状关联

白话解释: 每个模块有一个"代表"——模块特征基因(ME),它是该模块所有基因表达量的第一主成分。把ME和你关心的表型/临床信息(如疾病状态、年龄、生存时间)做相关分析,找出与特定性状显著相关的模块。

代码示例:

# 准备性状数据(数值化)
# 分类变量需要编码为0/1
nSamples <- nrow(datExpr)

# 计算模块特征基因
MEs0 <- moduleEigengenes(datExpr, moduleColors)$eigengenes
MEs <- orderMEs(MEs0)

# 计算模块-性状相关性
moduleTraitCor <- cor(MEs, traitData, use = "p")
moduleTraitPvalue <- corPvalueStudent(moduleTraitCor, nSamples)

# 热图可视化
textMatrix <- paste(signif(moduleTraitCor, 2), "\n(",
                    signif(moduleTraitPvalue, 1), ")", sep = "")
dim(textMatrix) <- dim(moduleTraitCor)

par(mar = c(6, 8.5, 3, 3))
labeledHeatmap(Matrix = moduleTraitCor,
               xLabels = names(traitData),
               yLabels = names(MEs),
               ySymbols = names(MEs),
               colorLabels = FALSE,
               colors = blueWhiteRed(50),
               textMatrix = textMatrix,
               setStdMargins = FALSE,
               cex.text = 0.5,
               zlim = c(-1, 1),
               main = "Module-Trait Relationships")

# 找显著关联的模块
sig_modules <- which(moduleTraitPvalue[, "Disease"] < 0.05)
cat("Significant modules for Disease:\n")
print(names(MEs)[sig_modules])

第五步:Hub基因识别

白话解释: Hub基因是模块内最重要的基因——它和模块内其他基因的关联性最强(高MM),同时和目标性状的关联也强(高GS)。这些hub基因通常是模块功能的关键驱动者。

技术细节: - MM (Module Membership):基因表达与模块ME的相关性 - GS (Gene Significance):基因表达与感兴趣性状的相关性 - Hub基因标准:|MM| > 0.8 且 |GS| > 0.2(或自定义阈值) - kME等同于MM(模块内连通度) - Hub基因可导出到Cytoscape可视化网络

代码示例:

# 计算Gene Significance和Module Membership
# 以"Disease"性状为例
trait_of_interest <- as.data.frame(traitData$Disease)
names(trait_of_interest) <- "Disease"

# GS: 基因与性状的相关性
GS <- as.data.frame(cor(datExpr, trait_of_interest, use = "p"))
names(GS) <- paste("GS.", names(trait_of_interest), sep = "")
GSPvalue <- as.data.frame(corPvalueStudent(as.matrix(GS), nSamples))

# MM: 基因与各模块ME的相关性
MM <- as.data.frame(cor(datExpr, MEs, use = "p"))
names(MM) <- paste("MM", substring(names(MEs), 3), sep = "")
MMPvalue <- as.data.frame(corPvalueStudent(as.matrix(MM), nSamples))

# 选择感兴趣的模块(如与Disease最相关的blue模块)
target_module <- "blue"

# 模块内基因
module_genes <- colnames(datExpr)[moduleColors == target_module]
cat("Genes in", target_module, "module:", length(module_genes), "\n")

# 筛选Hub基因
mm_col <- paste("MM", target_module, sep = "")
gs_col <- paste("GS.", "Disease", sep = "")

hub_criteria <- abs(MM[module_genes, mm_col]) > 0.8 &
                abs(GS[module_genes, gs_col]) > 0.2

hub_genes <- module_genes[hub_criteria]
cat("Hub genes:", length(hub_genes), "\n")
print(hub_genes)

# GS vs MM散点图
verboseScatterplot(abs(MM[module_genes, mm_col]),
                   abs(GS[module_genes, gs_col]),
                   xlab = paste("Module Membership in", target_module, "module"),
                   ylab = paste("Gene significance for Disease"),
                   main = paste("MM vs. GS\n"),
                   cex.main = 1.2, cex.lab = 1.2, cex.axis = 1.2,
                   col = target_module)

# 导出网络到Cytoscape
# 提取模块内top连接
TOM_module <- TOM[moduleColors == target_module, moduleColors == target_module]
dimnames(TOM_module) <- list(module_genes, module_genes)

# 导出top connections
cyt <- exportNetworkToCytoscape(TOM_module,
  edgeFile = paste("CytoscapeInput-edges-", target_module, ".txt", sep = ""),
  nodeFile = paste("CytoscapeInput-nodes-", target_module, ".txt", sep = ""),
  weighted = TRUE,
  threshold = 0.02,  # 连接权重阈值
  nodeNames = module_genes,
  nodeAttr = moduleColors[moduleColors == target_module])

第六步:模块功能富集分析

白话解释: 确定了感兴趣的模块后,对模块内的基因做GO/KEGG富集分析,看这些共表达的基因到底参与了什么生物过程。

代码示例:

library(clusterProfiler)
library(org.Hs.eg.db)

# 对目标模块做GO富集
module_genes_entrez <- bitr(hub_genes,
                             fromType = "SYMBOL",
                             toType = "ENTREZID",
                             OrgDb = org.Hs.eg.db)

# GO富集
ego <- enrichGO(gene = module_genes_entrez$ENTREZID,
                OrgDb = org.Hs.eg.db,
                ont = "BP",
                pAdjustMethod = "BH",
                pvalueCutoff = 0.05,
                readable = TRUE)

dotplot(ego, showCategory = 20, title = paste(target_module, "module GO enrichment"))

# KEGG富集
ekegg <- enrichKEGG(gene = module_genes_entrez$ENTREZID,
                     organism = "hsa",
                     pvalueCutoff = 0.05)

dotplot(ekegg, showCategory = 15, title = paste(target_module, "module KEGG enrichment"))

# 对所有模块批量做富集
all_modules <- unique(moduleColors)
module_enrichment <- list()

for (mod in all_modules) {
  mod_genes <- colnames(datExpr)[moduleColors == mod]
  mod_entrez <- bitr(mod_genes, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
  if (nrow(mod_entrez) > 10) {
    module_enrichment[[mod]] <- enrichGO(gene = mod_entrez$ENTREZID,
                                          OrgDb = org.Hs.eg.db, ont = "BP",
                                          pvalueCutoff = 0.05, readable = TRUE)
  }
}

实战命令(可复制)

# ===== WGCNA完整流程(可直接运行)=====
library(WGCNA)
options(stringsAsFactors = FALSE)
enableWGCNAThreads(nThreads = 8)

# 1. 读取数据(行=样本, 列=基因)
datExpr <- read.csv("expr_matrix_samples_as_rows.csv", row.names = 1)
traitData <- read.csv("clinical_data.csv", row.names = 1)

# 2. 质控
gsg <- goodSamplesGenes(datExpr, verbose = 3)
if (!gsg$allOK) datExpr <- datExpr[gsg$goodSamples, gsg$goodGenes]

# 3. 软阈值
powers <- c(1:10, seq(12, 20, 2))
sft <- pickSoftThreshold(datExpr, powerVector = powers, networkType = "signed", verbose = 5)
softPower <- sft$powerEstimate
if (is.na(softPower)) softPower <- 8

# 4. 网络构建
net <- blockwiseModules(datExpr, power = softPower, networkType = "signed",
  TOMType = "signed", minModuleSize = 30, mergeCutHeight = 0.25,
  numericLabels = TRUE, saveTOMs = TRUE, verbose = 3, maxBlockSize = 20000)
moduleColors <- labels2colors(net$colors)

# 5. 模块-性状关联
MEs <- moduleEigengenes(datExpr, moduleColors)$eigengenes
MEs <- orderMEs(MEs)
moduleTraitCor <- cor(MEs, traitData, use = "p")
moduleTraitPvalue <- corPvalueStudent(moduleTraitCor, nrow(datExpr))

# 6. Hub基因
# (选择与目标性状最相关的模块)
target_module <- "blue"  # 根据热图结果选择
module_genes <- colnames(datExpr)[moduleColors == target_module]
MM <- cor(datExpr[, module_genes], MEs[, paste0("ME", target_module)], use = "p")
GS <- cor(datExpr[, module_genes], traitData$Disease, use = "p")
hub_genes <- module_genes[abs(MM) > 0.8 & abs(GS) > 0.2]

面试常问点

Q1: WGCNA的软阈值为什么要让网络满足无标度特征?

A: 生物网络(蛋白互作网络、代谢网络等)通常具有无标度特征:大多数节点连接度低,少数hub节点连接度极高。通过调整软阈值β使网络的连通度分布近似幂律分布(log-log线性),可以让构建的共表达网络更接近真实生物网络的拓扑结构,从而更可靠地识别hub基因。

Q2: WGCNA需要多少样本?

A: 官方建议至少15个样本,实际经验表明>25个效果较好,>50个最佳。样本量影响:(1) 相关系数估计的稳定性;(2) 模块检测的稳健性;(3) 模块-性状关联的统计力。小样本(<15)时WGCNA结果不稳定,建议使用其他方法(如加权GCN但不做WGCNA整套流程)。

Q3: signed和unsigned网络的区别?

A: Unsigned:只看相关性绝对值,正相关和负相关的基因可能在同一模块。Signed:区分正负相关,只有正相关的基因聚在一起。推荐用signed——生物学上共表达基因(正相关)更可能参与相同通路;负相关基因应分属不同模块。Signed hybrid是折中方案。

Q4: 为什么不能用差异表达基因作为WGCNA输入?

A: (1) 预筛选差异基因引入了人为偏差——只保留了与特定条件相关的基因,丢失了其他生物学关系;(2) WGCNA设计是对无偏的全转录组数据做无监督聚类;(3) 差异基因间的"共表达"可能只是因为它们都和同一个条件相关,而非真正的共调控关系;(4) 正确做法是先WGCNA再看模块与性状的关联。

Q5: Module Membership和Gene Significance如何解读?

A: MM衡量一个基因"多像这个模块的成员"——MM=0.9说明它和模块整体表达模式高度一致,是模块核心基因。GS衡量一个基因"多与目标性状相关"——GS=0.5说明它可能是疾病相关基因。Hub基因同时具有高MM和高GS,既是模块核心又与性状强相关,是最值得实验验证的候选。

Q6: 模块合并阈值(mergeCutHeight)如何选择?

A: mergeCutHeight是合并ME相关性>1-阈值的模块。默认0.25意味着相关性>0.75的模块会合并。选择策略:(1) 画ME聚类树看分支高度;(2) 太高(如0.4)导致过多合并,可能丢失生物学差异;(3) 太低(如0.1)导致模块过多且碎片化。通常0.2-0.3是合理范围。

Q7: WGCNA结果如何验证?

A: (1) 模块保存性检验:在独立数据集中检验模块是否存在(modulePreservation函数);(2) 功能富集:模块基因是否富集于已知通路;(3) Hub基因验证:文献支持或实验验证(qPCR/Western/knockdown);(4) 与已知调控关系比较:hub基因是否已知为该通路的调控因子。

Q8: WGCNA的局限性有哪些?

A: (1) 需要较大样本量(>15,最好>25);(2) 假设线性相关关系,非线性共调控可能被遗漏;(3) 结果依赖参数选择(power、minModuleSize、mergeCutHeight);(4) 计算量大,基因数>20000时内存需求高;(5) 识别的是相关而非因果关系;(6) 对batch effect敏感。


易错点

1. 输入数据方向错误(应为行=样本,列=基因)

问题: 多数表达矩阵是行=基因、列=样本。直接输入WGCNA会把样本当作基因处理。 正确做法: 使用t()转置表达矩阵,确认nrow=样本数、ncol=基因数。

2. 使用原始count而非归一化数据

问题: raw count有文库大小偏差,影响相关系数计算。 正确做法: 使用DESeq2的VST/rlog变换、或log2(TPM+1)、或log2(FPKM+1)。注意不要用DESeq2的normalized counts(仍是count空间)。

3. 软阈值R²达不到0.85就用默认值

问题: 有时数据因为batch effect、异常样本等原因无法满足scale-free,强行选power会得到无意义结果。 正确做法: (1) 检查并去除异常样本/batch effect;(2) 确认数据适合WGCNA;(3) 如果R²最高只到0.7-0.8,选择该范围内connectivity不太低的power(通常mean connectivity > 50-100)。

4. 基因数过多导致内存溢出

问题: >20000基因时TOM计算需要大量内存(N^2矩阵)。 正确做法: (1) 用blockwiseModules()自动分块计算;(2) 预先过滤到5000-10000高变异基因;(3) 使用高内存服务器(>64GB RAM)。

5. 性状数据包含非数值型变量

问题: WGCNA的cor()需要数值型数据,字符型变量(如"Male"/"Female")会报错。 正确做法: 将分类变量转为0/1编码。多水平分类变量(如A/B/C组)做dummy编码或序数编码。

6. 过度解读单个hub基因

问题: 将WGCNA找到的hub基因直接当作"关键驱动基因"或"治疗靶点"。 正确做法: hub基因只是统计意义上的"网络中心",需要结合文献、功能注释和实验验证。高MM可能只是表达量高或变异大。

7. 忽略模块保存性验证

问题: 在单一数据集上找到的模块可能是过拟合,换一个数据集就不存在了。 正确做法: 使用modulePreservation()在独立数据集中验证模块的保存性(Zsummary > 10为高度保存)。


补充知识

适用场景

场景适合WGCNA不适合WGCNA
样本量>15(最好>25)<10
实验设计多条件/渐变/时间序列仅2组对照
目标发现共调控模块、hub基因仅找差异基因
数据类型RNA-seq、microarray单细胞(需pseudobulk)

单细胞WGCNA变体

  • hdWGCNA:专为单细胞设计的WGCNA,使用metacells/pseudocells减少稀疏性
  • Monocle3 + WGCNA:在pseudotime bin中做WGCNA
  • scWGCNA:直接在单细胞数据上运行的改进版本

与其他网络方法的对比

方法特点适用场景
WGCNA加权无标度共表达网络,模块化模块发现、hub基因
ARACNe互信息+数据处理不等式转录调控网络推断
GENIE3随机森林推断调控关系GRN构建
SCENICTF调控子推断(与scRNA-seq结合)单细胞调控网络
CEMiTool自动化WGCNA(简化参数选择)快速模块分析

本教程系统覆盖了WGCNA从数据准备到hub基因识别的完整流程,适合转录组共表达分析和生物标志物发现。