Loading...
桑基图 (Sankey Diagram),是一种特定类型的流图,用于描述一组值到另一组值的流向。上图为1869年,查尔斯米纳德(Charles Minard)绘制的1812年拿破仑征俄图(Map of Napolean's Russian Campaign of 1812),这是一个在地图上覆盖桑基图的流程图。1898年爱尔兰人 Matthew Henry Phineas Riall Sankey 在土木工程师学会会报纪要的一篇关于蒸汽机能源效率的文章中首次推出了第一个能量流动图,此后便以其名字命名为 Sankey 图,中文音译为桑基图。
图中延伸的分支的宽度对应数据流量的大小。桑基图的特点如下:
桑基图通常应用于能源、材料成分、金融等数据的可视化分析。
英文名:Sankey Diagram
图表类型 | 基础桑基图 |
---|---|
适合的数据 | 流向数据:包含源节点(source)、目标节点(target)和流量值(value)的数据 |
功能 | 展示不同节点之间的流向关系和流量大小 |
数据与图形的映射 | 源节点映射到左侧位置 目标节点映射到右侧位置 流量值映射到连接线的宽度 |
适合的场景 | 展示能源流动、资金流向、用户路径等流程分析 |
例子 1: 适合展示能源流动分析
下图是一个能源流动的桑基图,展示了从原始能源到最终消费的完整流程。
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'sankey',autoFit: true,data: {type: 'fetch',value: 'https://assets.antv.antgroup.com/g2/energy.json',transform: [{type: 'custom',callback: (data) => ({ links: data }),},],},layout: {nodeAlign: 'center',nodePadding: 0.03,},style: {labelSpacing: 3,labelFontWeight: 'bold',nodeStrokeWidth: 1.2,linkFillOpacity: 0.4,},});chart.render();
说明:
source
字段映射到起始节点,表示能源的来源target
字段映射到目标节点,表示能源的去向value
字段映射到连接线的宽度,表示流量的大小例子 2: 适合展示用户路径分析
桑基图可以有效展示用户在网站或应用中的行为路径,帮助分析用户流失点和转化漏斗。
import { Chart } from '@antv/g2';const data = {links: [{ source: '首页', target: '产品页', value: 1000 },{ source: '首页', target: '关于我们', value: 300 },{ source: '首页', target: '联系我们', value: 200 },{ source: '产品页', target: '购物车', value: 600 },{ source: '产品页', target: '产品详情', value: 400 },{ source: '购物车', target: '结算页', value: 450 },{ source: '购物车', target: '继续购物', value: 150 },{ source: '结算页', target: '支付成功', value: 380 },{ source: '结算页', target: '放弃支付', value: 70 },{ source: '产品详情', target: '购物车', value: 200 },{ source: '产品详情', target: '返回', value: 200 },]};const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'sankey',autoFit: true,data: { value: data },layout: {nodeAlign: 'justify',nodePadding: 0.05,},style: {labelSpacing: 5,labelFontSize: 12,nodeStrokeWidth: 2,linkFillOpacity: 0.6,},scale: {color: {range: ['#1890ff', '#52c41a', '#faad14', '#f5222d', '#722ed1'],},},});chart.render();
说明:
例子 3: 适合展示资金流向分析
桑基图特别适合展示预算分配、成本分析等财务数据的流向关系。
import { Chart } from '@antv/g2';const budgetData = {links: [{ source: '总预算', target: '研发部门', value: 5000000 },{ source: '总预算', target: '市场部门', value: 3000000 },{ source: '总预算', target: '运营部门', value: 2000000 },{ source: '研发部门', target: '产品开发', value: 3000000 },{ source: '研发部门', target: '技术研究', value: 2000000 },{ source: '市场部门', target: '广告投放', value: 2000000 },{ source: '市场部门', target: '品牌建设', value: 1000000 },{ source: '运营部门', target: '人力成本', value: 1200000 },{ source: '运营部门', target: '办公成本', value: 800000 },{ source: '产品开发', target: '移动端', value: 1800000 },{ source: '产品开发', target: '网页端', value: 1200000 },{ source: '广告投放', target: '线上广告', value: 1500000 },{ source: '广告投放', target: '线下推广', value: 500000 },]};const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'sankey',autoFit: true,data: { value: budgetData },layout: {nodeAlign: 'center',nodePadding: 0.08,},style: {labelSpacing: 8,labelFontSize: 11,nodeStrokeWidth: 1.5,linkFillOpacity: 0.5,},scale: {color: {range: ['#096dd9', '#36cfc9', '#52c41a', '#faad14', '#ff7a45'],},},});chart.render();
说明:
例子 1: 不适合展示简单的分类对比
当数据主要用于比较不同类别的数值大小,而非展示流向关系时,桑基图不是最佳选择。这种情况下,柱状图或饼图会更加直观。
import { Chart } from '@antv/g2';// 示例:销售额对比数据(不适合用桑基图)const salesData = [{ category: '手机', value: 25000 },{ category: '电脑', value: 18000 },{ category: '平板', value: 12000 },{ category: '耳机', value: 8000 },{ category: '配件', value: 5000 },];const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'interval',autoFit: true,data: { value: salesData },encode: {x: 'category',y: 'value',color: 'category'},axis: {y: { title: '销售额(元)' },x: { title: '产品类别' }},style: {fill: '#1890ff',},});chart.render();
说明:此类数据更适合使用柱状图展示,因为重点是比较各类别的数值大小,而非展示数据间的流向关系。桑基图在这种场景下会增加不必要的复杂性。
例子 2: 不适合展示过于复杂的网络关系
当节点数量过多(超过30个)或流向关系过于复杂时,桑基图可能会变得难以阅读。这种情况下,可以考虑使用网络图或层次图。
import { Chart } from '@antv/g2';// 示例:复杂的组织架构关系(不适合用桑基图)const complexData = {links: [{ source: 'CEO', target: '研发VP', value: 1 },{ source: 'CEO', target: '销售VP', value: 1 },{ source: 'CEO', target: '市场VP', value: 1 },{ source: 'CEO', target: '运营VP', value: 1 },{ source: '研发VP', target: '前端团队', value: 1 },{ source: '研发VP', target: '后端团队', value: 1 },{ source: '研发VP', target: 'QA团队', value: 1 },{ source: '研发VP', target: 'DevOps团队', value: 1 },{ source: '销售VP', target: '直销团队', value: 1 },{ source: '销售VP', target: '渠道团队', value: 1 },{ source: '销售VP', target: '商务团队', value: 1 },{ source: '销售VP', target: '客服团队', value: 1 },{ source: '市场VP', target: '品牌团队', value: 1 },{ source: '市场VP', target: '活动团队', value: 1 },{ source: '市场VP', target: '内容团队', value: 1 },{ source: '运营VP', target: '数据团队', value: 1 },{ source: '运营VP', target: '产品团队', value: 1 },{ source: '运营VP', target: '用户团队', value: 1 },]};const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'sankey',autoFit: true,data: { value: complexData },layout: {nodeAlign: 'justify',nodePadding: 0.02,},style: {labelSpacing: 2,labelFontSize: 10,nodeStrokeWidth: 1,linkFillOpacity: 0.3,},});chart.render();
说明:此类层次结构数据使用桑基图会显得过于拥挤且难以阅读。更适合使用组织架构图、树形图或网络图来展示这种层次关系。桑基图的优势在于展示有意义的"流量"关系,而非仅仅是组织结构。
可以通过不同的节点对齐方式来优化桑基图的布局效果。
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'sankey',autoFit: true,data: {type: 'fetch',value: 'https://assets.antv.antgroup.com/g2/energy.json',transform: [{type: 'custom',callback: (data) => ({ links: data.slice(0, 20) }), // 使用部分数据以便展示},],},layout: {nodeAlign: 'left', // 可选值:'left', 'right', 'center', 'justify'nodePadding: 0.05,nodeWidth: 0.02,},style: {labelSpacing: 5,labelFontWeight: 'bold',nodeStrokeWidth: 2,linkFillOpacity: 0.3,},});chart.render();
说明:
nodeAlign: 'left'
将所有节点左对齐nodePadding
控制节点之间的间距nodeWidth
控制节点的宽度可以根据节点类型或流量方向自定义颜色映射。
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'sankey',autoFit: true,data: {type: 'fetch',value: 'https://assets.antv.antgroup.com/g2/energy.json',transform: [{type: 'custom',callback: (data) => ({ links: data }),},],},layout: {nodeAlign: 'justify',nodePadding: 0.03,},encode: {color: (d, idx) => {// 根据节点名称的首字母分配颜色const firstChar = d.key?.charAt(0).toLowerCase() || '';if (firstChar <= 'f') return '#1890ff';if (firstChar <= 'n') return '#52c41a';if (firstChar <= 's') return '#faad14';return '#f5222d';},},style: {labelSpacing: 3,labelFontWeight: 'bold',nodeStrokeWidth: 1,linkFillOpacity: 0.6,},});chart.render();
说明:
encode.color
自定义颜色映射函数