第一次接触WGCNA(Weighted Gene Co-expression Network Analysis)时,我也被这个高大上的名字吓到了。但实际用起来才发现,它就像给基因表达数据做"社交网络分析"——找出哪些基因喜欢扎堆活动,哪些基因是社交达人(hub基因)。这种分析方法特别适合处理高通量基因表达数据,比如RNA-seq结果。
WGCNA的核心思想很简单:通过计算基因间的相关性,把行为相似的基因归为同一个模块。这些模块就像公司里的不同部门,有的负责代谢,有的参与免疫反应。而每个模块里的"部门经理"就是hub基因,它们往往在生物学功能中起关键作用。
为什么要用WGCNA?我遇到的实际案例中,有个研究生想从3000个差异表达基因里找出关键调控网络。传统方法只能得到一堆散点,而WGCNA帮他找到了3个核心功能模块,其中一个模块的hub基因正好是他研究的信号通路关键因子。这就是WGCNA的价值——它让海量数据变得有结构、可解释。
数据预处理就像做饭前的备菜,处理不好后面全完蛋。我常用的输入数据是基因表达矩阵,行是基因,列是样本。第一次用的时候犯了个低级错误——忘记转置了,结果聚类出来全是基因而不是样本,白白浪费两小时。
r复制# 正确做法示例
expr_data <- read.csv("ExpData_WGCNA.csv", row.names = 1)
datExpr <- data.frame(t(expr_data)) # 关键转置步骤
colnames(datExpr) <- rownames(expr_data)
rownames(datExpr) <- colnames(expr_data)
有个坑得特别注意:如果你的数据里有缺失值或异常值,WGCNA会直接报错。有次我数据里混了几个NA,折腾半天才发现。建议先用summary()检查数据完整性。
样本过滤是很多人容易忽略的步骤。有次分析结果很奇怪,最后发现是有个样本在运输过程中降解了。goodSamplesGenes()函数是WGCNA自带的质检员:
r复制gsg <- goodSamplesGenes(datExpr, verbose = 3)
if (!gsg$allOK) {
datExpr <- datExpr[gsg$goodSamples, gsg$goodGenes]
}
画样本聚类图能直观看出异常样本。我习惯用hclust配合dist函数:
r复制sampleTree <- hclust(dist(datExpr), method = "average")
pdf("sample_clustering.pdf")
plot(sampleTree, main = "Sample clustering")
dev.off()
看到离群样本怎么办?我的经验是:如果样本明显远离主群(比如高度>1500),果断去掉。但要注意保留至少30个样本,太少会影响网络构建。
软阈值(soft threshold)就像社交关系的"亲密程度"标准。设得太低,所有基因都勉强关联;设得太高,只剩少数基因有联系。这个参数直接影响后续模块识别效果。
WGCNA推荐选择使网络达到近似无标度拓扑结构的power值。简单说就是找拐点——当R²达到0.85左右时的最小power值。我通常测试1-30的范围:
r复制powers <- c(c(1:10), seq(12, 30, 2))
sft <- pickSoftThreshold(datExpr, powerVector = powers, verbose = 5)
画两个图能帮你做决定。第一个图看R²值,我习惯选R²>0.85的最小power:
r复制par(mfrow = c(1,2))
plot(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
xlab="Soft Threshold", ylab="Scale Free Topology Model Fit")
abline(h=0.85, col="red")
第二个图看平均连接度。power越大,基因间连接越少。要平衡网络特性和信息保留:
r复制plot(sft$fitIndices[,1], sft$fitIndices[,5],
xlab="Soft Threshold", ylab="Mean Connectivity")
实际项目中,我遇到过一个棘手情况:R²曲线一直达不到0.85。后来发现是样本异质性太高,通过增加样本量解决了问题。
这是最耗时的步骤,大数据集建议在服务器运行。关键参数包括:
r复制net <- blockwiseModules(datExpr, power = 14,
TOMType = "signed",
minModuleSize = 30,
mergeCutHeight = 0.25)
遇到内存不足怎么办?可以设置maxBlockSize分块计算。我有次处理2万个基因的数据,设maxBlockSize=5000才跑通。
用plotDendroAndColors()展示基因聚类树和模块颜色:
r复制mergedColors <- labels2colors(net$colors)
pdf("module_dendrogram.pdf")
plotDendroAndColors(net$dendrograms[[1]], mergedColors,
"Module colors", dendroLabels = FALSE)
dev.off()
模块太多怎么办?可以调整mergeCutHeight参数合并相似模块。我有次初始得到20个模块,调整后合并为12个更有生物学意义的模块。
导出各模块基因便于后续分析。我习惯用循环批量输出:
r复制moduleColors <- labels2colors(net$colors)
unique_colors <- unique(moduleColors)
for(color in unique_colors){
module_genes <- colnames(datExpr)[moduleColors == color]
write.csv(module_genes, paste0(color, "_genes.csv"))
}
记得保存中间结果!我有次跑了8小时的程序因为没保存RData,断电全没了。教训惨痛:
r复制save(net, datExpr, file = "WGCNA_results.RData")
表型数据需要与表达数据样本顺序一致。我常用match函数对齐:
r复制traitData <- read.csv("TraitData.csv", row.names=1)
traitRows <- match(rownames(datExpr), rownames(traitData))
datTraits <- traitData[traitRows, ]
moduleEigengenes()计算模块特征基因(模块的代表性表达模式),再与表型做相关:
r复制MEs <- moduleEigengenes(datExpr, moduleColors)$eigengenes
modTraitCor <- cor(MEs, datTraits, use = "p")
modTraitP <- corPvalueStudent(modTraitCor, nrow(datExpr))
用labeledHeatmap展示相关系数和p值:
r复制textMatrix <- paste(signif(modTraitCor, 2), "\n(",
signif(modTraitP, 1), ")", sep="")
dim(textMatrix) <- dim(modTraitCor)
pdf("module_trait_heatmap.pdf")
labeledHeatmap(modTraitCor, xLabels = colnames(datTraits),
yLabels = names(MEs), yColor=moduleColors,
xLabelsAngle = 45, colorLabels = FALSE,
colors = blueWhiteRed(50),
textMatrix = textMatrix, setStdMargins = FALSE)
dev.off()
有个实用技巧:用颜色深浅表示相关性强弱,正相关用红色,负相关用蓝色。这样一眼就能看出哪些模块与目标性状最相关。
Hub基因就像社交网络中的大V。计算模块内基因的连接度(kME)来识别:
r复制geneModuleMembership <- as.data.frame(cor(datExpr, MEs, use = "p"))
hub_genes <- list()
for(module in colnames(geneModuleMembership)){
kME <- geneModuleMembership[,module]
hub_genes[[module]] <- names(sort(kME, decreasing = TRUE))[1:10]
}
TOM(拓扑重叠矩阵)记录基因间连接强度。导出特定模块的边信息:
r复制TOM <- TOMsimilarityFromExpr(datExpr, power=14)
probes <- colnames(datExpr)
for(module in unique(moduleColors)){
inModule <- (moduleColors==module)
modProbes <- probes[inModule]
modTOM <- TOM[inModule, inModule]
cyt <- exportNetworkToCytoscape(modTOM,
edgeFile = paste0("edges_",module,".txt"),
nodeFile = paste0("nodes_",module,".txt"),
weighted = TRUE, threshold = 0.02)
}
在Cytoscape中导入时,注意选择"从表格导入网络"。我常用"force-directed"布局,能清晰展示hub基因的中心位置。
大数据集常遇到内存问题。三个实用解决方案:
r复制net <- blockwiseModules(datExpr, maxBlockSize = 5000, ...)
调整这些参数可以优化模块数量:
WGCNA结果可能因随机种子变化。建议:
r复制set.seed(123) # 保证结果可重复
遇到模块功能不明确时,可以:
对于多时间点数据,我习惯用两种策略:
r复制# 时间作为协变量示例
datTraits$time <- as.numeric(substr(rownames(datTraits), 6,7))
WGCNA可以扩展整合甲基化、蛋白等数据。关键步骤:
我开发了一套自动化分析流程,主要功能:
r复制source("autoWGCNA.R")
results <- autoWGCNA("expression.csv", "traits.csv")
用ggplot2替代基础图形:
r复制library(pheatmap)
pheatmap(modTraitCor, cluster_rows=F, cluster_cols=F,
display_numbers=textMatrix)
最近帮一个客户分析肝癌RNA-seq数据,目标是找到与肿瘤分级相关的共表达模块。原始数据包括50个样本(25对癌与癌旁),约2万个基因。
蓝色模块富集在细胞周期通路,其hub基因包括多个已知的癌基因。通过Cytoscape可视化,发现一个以CDK1为中心的调控网络,与临床预后数据吻合。
记得定期保存工作空间图像,我习惯每完成一个重要步骤就保存一次:
r复制save.image(file = "WGCNA_backup.RData")