第一次接触Mantel分析时,我也被那些专业术语搞得晕头转向。直到真正用R语言跑通整个流程,才发现这套分析方法其实非常直观实用。简单来说,Mantel分析就是检验两个矩阵之间相关性的统计方法,在生态学、微生物组研究中特别常用。比如你想知道环境因子矩阵和微生物群落矩阵是否存在显著关联,Mantel测试就能给出定量答案。
网络图可视化则是将复杂的相关性关系用节点和连线的形式呈现出来。想象一下地铁线路图——每个站点是节点,线路是连线,这样就能一目了然地看到哪些站点关联紧密。在科研中,我们常用它展示基因共表达网络、物种互作网络等复杂关系。
这套分析流程特别适合:
我推荐使用R语言的linkET和netET包组合,它们用起来就像搭积木一样简单。下面我会用最通俗的语言,带你完整走一遍从数据准备到高级可视化的全流程。
工欲善其事,必先利其器。首先打开你的RStudio,运行以下代码安装核心工具包:
r复制# 安装CRAN上的基础包
install.packages(c("ggraph", "tidygraph", "vegan", "reshape2"))
# 安装GitHub上的特色包
if (!require("devtools")) install.packages("devtools")
devtools::install_github("Hy4m/linkET")
devtools::install_github("Hy4m/netET")
这里有个小技巧:安装igraph包时建议指定0.1.7版本,因为新版可能存在兼容性问题。我当初就踩过这个坑,折腾了半天才发现是版本问题:
r复制# 先卸载现有版本
remove.packages("igraph")
# 安装指定版本
install.packages("igraph", version = '0.1.7')
我们使用vegan包内置的经典数据集做演示:
r复制library(vegan)
data("varespec") # 植物物种数据
data("varechem") # 环境因子数据
# 查看数据结构
dim(varespec) # 24个样本×44个物种
dim(varechem) # 24个样本×14个环境因子
这些数据长什么样呢?varespec记录的是不同采样点的植物物种丰度,varechem则包含土壤pH值、氮磷钾含量等环境指标。在实际分析中,你只需要把自己的数据整理成类似的矩阵格式即可。
我们先计算环境因子之间的Pearson相关性:
r复制library(linkET)
# 计算相关性矩阵
cor_matrix <- correlate(varechem)
head(as_md_tbl(cor_matrix))
这个矩阵会显示每个环境因子两两之间的相关系数(r值)和显著性(p值)。但纯数字不够直观,我们把它可视化:
r复制qcorrplot(cor_matrix) +
geom_square() +
scale_fill_gradientn(colours = RColorBrewer::brewer.pal(11, "RdBu"))
这幅热图中,颜色深浅表示相关性强弱,红色代表正相关,蓝色代表负相关。我特别喜欢用geom_square()这个函数,它让热图的每个单元格都变成规整的正方形,比传统圆形更美观。
现在进入正题——检验环境因子与物种组成的关联:
r复制mantel_result <- mantel_test(
varespec, # 物种数据矩阵
varechem, # 环境数据矩阵
spec_select = list(
Spec01 = 1:7,
Spec02 = 8:18,
Spec03 = 19:37,
Spec04 = 38:44
)
) %>%
mutate(
rd = cut(r, breaks = c(-Inf, 0.2, 0.4, Inf),
labels = c("< 0.2", "0.2 - 0.4", ">= 0.4")),
pd = cut(p, breaks = c(-Inf, 0.01, 0.05, Inf),
labels = c("< 0.01", "0.01 - 0.05", ">= 0.05"))
)
这段代码做了三件事:
把Mantel结果与热图结合,可以制作信息量丰富的组合图:
r复制D0 <- qcorrplot(correlate(varechem), type = "lower") +
geom_square() +
geom_couple(aes(colour = pd, size = rd),
data = mantel_result,
curvature = nice_curvature()) +
scale_fill_gradientn(colours = RColorBrewer::brewer.pal(11, "RdBu")) +
scale_size_manual(values = c(0.5, 1, 2)) +
scale_colour_manual(values = color_pal(3)) +
guides(
size = guide_legend(title = "Mantel's r"),
colour = guide_legend(title = "Mantel's p"),
fill = guide_colorbar(title = "Pearson's r")
)
这里有个实用技巧:geom_couple()函数会用曲线连接热图中的环境因子与右侧的物种组,线条粗细表示r值大小,颜色表示p值显著性。我建议初次使用时调整curvature参数,找到最优雅的曲线弧度。
网络图能更直观展示复杂关系。我们先筛选显著相关性对:
r复制library(netET)
p1 <- correlate(varechem, varespec, method = "spearman") %>%
as_tbl_graph(abs(r) > 0.5, p < 0.05) %>%
mutate(Degree = centrality_degree())
这段代码做了关键过滤:
环形布局特别适合展示模块化结构:
r复制library(ggraph)
xy <- layout_on_circle(p1)
D1 <- ggraph(p1, xy) +
geom_edge_fan(aes(colour = r > 0), width = 0.8) +
geom_node_point(aes(size = Degree), colour = "#fa8c35") +
scale_edge_colour_manual(
values = c("TRUE" = "#c93756", "FALSE" = "#21a675"),
labels = c("Negative", "Positive")
) +
geom_node_text(
aes(label = name, angle = node_angle(x, y)),
hjust = "outward"
) +
expand_limits(x = c(-1.5, 1.5), y = c(-1.5, 1.5)) +
labs(edge_colour = "Spearman's r")
我特别喜欢这个图的几个设计细节:
当需要区分两类节点时(如环境因子vs物种),二分网络图更合适:
r复制p2 <- p1 %>%
as_bipartite_circular(outer_nodes = names(varespec))
D2 <- ggraph(p2, layout_bipartite_circular(p2)) +
annotate_arc_rect(0, 180, fill = "#e0eee8", r0 = 0.55, r1 = 1.05) +
geom_edge_circular(aes(colour = r > 0), edge_width = 0.75) +
geom_node_point(aes(size = Degree, colour = Degree == 0)) +
geom_node_text_circular(expand = 0.08) +
scale_colour_manual(values = c("TRUE" = "grey55", "FALSE" = "#065279")) +
scale_edge_colour_manual(
values = c("TRUE" = "#c93756", "FALSE" = "#21a675"),
labels = c("Negative", "Positive")
) +
coord_fixed() +
labs(edge_colour = "Spearman's r")
这个图的精妙之处在于:
用patchwork包可以轻松组合多个图形:
r复制library(patchwork)
library(cowplot)
combined_plot <- D0 + D1 + D2 +
plot_layout(nrow = 1, widths = c(6, 5, 6))
ggsave("combined_plot.jpg", combined_plot, width = 20, height = 10)
这里要注意三点:
在实际使用中,你可能会遇到这些问题:
r复制remove.packages("igraph")
install.packages("igraph", version = '0.1.7')
图形元素重叠:尝试调整layout参数,或使用ggrepel包自动避让标签。
颜色映射错误:检查scale_*_manual中的值是否与数据类型匹配。
想让你的图表脱颖而出?试试这些技巧:
r复制D1 + theme(
panel.background = element_blank(),
legend.position = "bottom",
text = element_text(family = "Arial")
)
r复制library(gganimate)
D1 + transition_states(Degree) +
shadow_mark()
r复制library(plotly)
ggplotly(D1)