如何用R绘制节点按列固定的关联网络图?求优化方案
解决方案:用ggraph+tidygraph实现固定列的关联图
针对你需要将X/Y/Z类节点分固定列展示关联关系的需求,我推荐使用ggraph + tidygraph的组合方案——这两个包在处理这类分簇节点的有向关联图时灵活性拉满,而且完美适配你提到的大规模节点场景(20个X、120个Y、200个Z),完全不用手动调整节点位置。
步骤1:数据格式转换
首先需要把你现有的两个数据框转换成图结构所需的节点表和边表:
library(tidyverse) library(tidygraph) library(ggraph) # 你的原始数据 y1 <- c(1, 0, 0) y2 <- c(0, 1, 0) y3 <- c(0, 0, 1) df1 <- data.frame(y1, y2, y3, row.names = c("x1", "x2", "x3")) y1 <- c(1, 0, 0) y2 <- c(1, 0, 0) y3 <- c(1, 0, 0) df2 <- data.frame(y1, y2, y3, row.names = c("z1", "z2", "z3")) # 转换df1为X→Y的边数据(只保留有关联的条目) edges_x_to_y <- df1 %>% rownames_to_column("source") %>% pivot_longer(cols = -source, names_to = "target", values_to = "weight") %>% filter(weight == 1) %>% select(-weight) # 转换df2为Y→Z的边数据(注意这里df2的行是Z节点,列是Y节点,所以要反转source/target) edges_y_to_z <- df2 %>% rownames_to_column("target") %>% pivot_longer(cols = -target, names_to = "source", values_to = "weight") %>% filter(weight == 1) %>% select(-weight) # 合并所有边 all_edges <- bind_rows(edges_x_to_y, edges_y_to_z) # 创建节点表:标记每个节点所属的分组(X/Y/Z),手动加入无关联的z2/z3 all_nodes <- tibble(name = unique(c(all_edges$source, all_edges$target))) %>% add_row(name = c("z2", "z3")) %>% distinct(name) %>% mutate(group = case_when( str_detect(name, "^x") ~ "X", str_detect(name, "^y") ~ "Y", str_detect(name, "^z") ~ "Z" )) # 构建tidygraph图对象 graph <- tbl_graph(nodes = all_nodes, edges = all_edges, directed = TRUE)
步骤2:绘制固定列的关联图
用ggraph的线性布局可以强制节点按分组分成三列,完全解决你之前sankey图中z2/z3不显示的问题:
ggraph(graph, layout = "linear", circular = FALSE, sort.by = group) + # 绘制节点:按分组设置颜色,大小可根据节点数量调整 geom_node_point(aes(color = group), size = 6) + # 添加节点标签,放在节点下方 geom_node_text(aes(label = name), vjust = 1.8, size = 3.5, color = "black") + # 添加有向连线:设置箭头和透明度,避免连线重叠混乱 geom_edge_link(aes(start_cap = label_rect(node1.name), end_cap = label_rect(node2.name)), arrow = arrow(length = unit(2.5, "mm")), alpha = 0.6, color = "gray50") + # 强制X/Y/Z分组按顺序排列成三列 scale_x_discrete(limits = c("X", "Y", "Z")) + # 使用简洁的主题,去掉多余网格和背景 theme_graph(base_family = "sans") + labs(color = "Node Category") + # 调整图例位置(可选) theme(legend.position = "bottom")
方案优势
- 固定列布局:通过
sort.by = group和scale_x_discrete,X/Y/Z节点会严格分成三列,不会出现sankey图中无关联节点不显示的问题 - 高可扩展性:无论你的节点数量是20还是200,代码都能自动处理,不需要手动维护节点位置或索引
- 高度自定义:可以轻松调整节点大小、颜色、连线样式、标签位置,甚至添加交互(结合
ggiraph包)
如果你想继续优化networkD3的sankey图
如果一定要用sankeyNetwork,你可以给无关联的节点(z2、z3)添加权重为0的边,让它们显示在右侧列:
library(networkD3) # 节点表保留所有节点 Nodes <- data.frame(name = c("x1", "x2", "x3", "y1", "y2", "y3", "z1", "z2", "z3"), group = c("1", "1", "1", "2", "2", "2", "3", "3", "3")) # 原有的边 + 给z2/z3添加权重为0的边(随便连一个Y节点即可,权重0不会显示连线) Links <- data.frame( source = c(0, 1, 2, 3, 4, 5, 3, 3), target = c(3, 4, 5, 6, 6, 6, 7, 8), value = c(1, 1, 1, 1, 1, 1, 0, 0) ) sankeyNetwork(Links = Links, Nodes = Nodes, Source = "source", Target = "target", Value = "value", NodeGroup = "group", NodeID = "name", sinksRight = FALSE)
不过这种方法只是临时解决显示问题,对于大规模节点来说维护成本很高,还是推荐ggraph的方案。
内容的提问来源于stack exchange,提问作者ADF
相关产品推荐
相关产品推荐

