R语言桑基图节点无法归组问题求助
控制R中桑基图节点水平位置的方法
默认情况下,networkD3和plotly的桑基图会根据链接流量自动布局节点,容易出现同名称节点不在同一水平组的问题。以下是两种直接控制节点水平位置的可行方案:
方法1:给networkD3的sankeyNetwork添加自定义JS逻辑
networkD3基于D3.js,我们可以通过自定义JavaScript代码,强制指定每个节点的水平层级,让同名称节点归到同一组。
示例代码
library(networkD3) library(htmlwidgets) # 原节点数据新增level列,指定每个节点的水平层级(0到4对应BL、M3、M12、M18、M24) nodes <- data.frame( 'name' = c("BL", "M3", "M3", "M3", "M3", "M12", "M12", "M12", "M12", "M18", "M18", "M18", "M18", "M24", "M24", "M24", "M24"), 'group' = as.factor(c("g","dgr","lgr","g","r","dgr","lgr","g","r","dgr","lgr","g","r","dgr","lgr","g","r")), 'level' = c(0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4) ) links <- data.frame( 'source' = c(0, 0, 0, 0, 3, 3, 3, 3, 4, 4, 4, 4, 7, 7, 7, 7, 8, 8, 8, 8, 11, 11, 11, 12, 12, 12, 12), 'target' = c(1, 2, 3, 4, 5, 6, 7, 8, 5, 6, 7, 8, 9, 10, 11, 12, 9, 10, 11, 12, 13, 15, 16, 13, 14, 15, 16), 'value' = c(70, 80, 1504, 843, 14, 13, 695, 317, 12, 44, 87, 271, 5, 10, 360, 183, 6, 12, 69, 219, 7, 186, 108, 2, 5, 46, 138), 'group' = as.factor(c("dgr_","lgr_","g_","r_","dgr_","lgr_","g_","r_","dgr_","lgr_","g_","r_","dgr_","lgr_","g_","r_","dgr_","lgr_","g_","r_","dgr_","g_","r_","dgr_","lgr_","g_","r_")) ) my_color <- 'd3.scaleOrdinal().domain(["g","dgr","lgr","r","g_","dgr_","lgr_","r_"]) .range(["green", "darkgrey", "lightgrey","red", "green", "darkgrey", "lightgrey","red"])' # 生成基础桑基图 sankey_R = sankeyNetwork( Links = links, Nodes = nodes, Source = "source", Target = "target", Value = "value", NodeID = "name", fontSize = 12, nodeWidth = 20, colourScale = my_color, NodeGroup = "group", LinkGroup = "group" ) # 添加自定义JS,强制固定节点水平位置 sankey_R <- onRender(sankey_R, ' function(el, x) { var nodes = x.nodes; var sankey = this.sankey; // 根据level列设置节点的x轴范围 nodes.forEach(function(d, i) { d.x0 = d.level * (sankey.nodeWidth() + sankey.nodePadding()); d.x1 = d.x0 + sankey.nodeWidth(); }); // 更新链接位置并重新绘图 sankey.relayout(); sankey.update(el); } ') sankey_R
方法2:使用ggalluvial包手动控制节点分组
ggalluvial基于ggplot2,支持直接指定节点的水平轴分组,能精准控制同名称节点的位置,适合静态可视化场景。
示例代码
library(ggalluvial) library(ggplot2) # 转换数据为ggalluvial要求的长格式 flow_data <- data.frame( BL = c(rep("g", 1504+843), rep("dgr",70), rep("lgr",80)), M3 = c(rep("g",1504), rep("dgr",14), rep("lgr",13), rep("r",317), rep("g",87), rep("dgr",12), rep("lgr",44), rep("r",271)), M12 = c(rep("g",695), rep("dgr",14), rep("lgr",13), rep("r",317), rep("g",87), rep("dgr",12), rep("lgr",44), rep("r",271), rep("g",360), rep("dgr",5), rep("lgr",10), rep("r",183), rep("g",69), rep("dgr",6), rep("lgr",12), rep("r",219)), M18 = c(rep("g",360), rep("dgr",5), rep("lgr",10), rep("r",183), rep("g",69), rep("dgr",6), rep("lgr",12), rep("r",219), rep("g",186), rep("dgr",7), rep("r",108), rep("g",46), rep("dgr",2), rep("lgr",5), rep("r",138)), M24 = c(rep("g",186), rep("dgr",7), rep("r",108), rep("g",46), rep("dgr",2), rep("lgr",5), rep("r",138)) ) # 绘制桑基图,通过axis参数指定水平分组 ggplot(flow_data, aes(y = 1, axis1 = BL, axis2 = M3, axis3 = M12, axis4 = M18, axis5 = M24)) + geom_alluvium(aes(fill = BL), width = 1/12) + geom_stratum(width = 1/12, fill = "white", color = "black") + geom_text(stat = "stratum", aes(label = after_stat(stratum))) + scale_x_discrete(limits = c("BL", "M3", "M12", "M18", "M24"), expand = c(0.05, 0.05)) + scale_fill_manual(values = c("g" = "green", "dgr" = "darkgrey", "lgr" = "lightgrey", "r" = "red")) + theme_minimal() + theme(axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y = element_blank())
方案对比
- 方法1保留了
networkD3的交互性,适合需要用户交互的场景。 - 方法2布局更直观可控,适合生成静态报告或论文插图。
内容的提问来源于stack exchange,提问作者Zsolt
相关产品推荐
相关产品推荐

