如何使用Plotly.js创建兼具堆叠与分组样式的柱状图?
如何用Plotly.js创建分组堆叠柱状图
嘿,这个需求我刚好折腾过!Plotly.js本身没有直接提供「分组+堆叠」的一键配置,但咱们可以通过巧妙配置trace的stackgroup和offsetgroup属性来实现,效果完全符合你的要求。下面给你一步步拆解:
核心思路
要实现「分组内堆叠,分组间并排」的效果,关键要用到两个Plotly属性:
stackgroup:给同一个分组内需要堆叠的trace设置相同的ID,Plotly会自动把它们堆叠在一起offsetgroup:给不同的大分组设置不同的ID,Plotly会把这些分组的柱子并排显示- 配合布局里的
barmode: 'group',确保分组之间是并排排列的
完整代码示例
下面是一个可直接运行的示例,包含3个大分组,每个分组内有3个堆叠子类别:
// 需先在HTML中创建容器:<div id="myDiv"></div> // 定义图表数据 const data = [ // 第一组:Group 1 的堆叠项 { x: ['Group 1'], y: [20], name: 'Series X', type: 'bar', stackgroup: 'g1', // 属于Group1的堆叠组 offsetgroup: 'group1', // 属于Group1分组 marker: { color: '#1f77b4' } }, { x: ['Group 1'], y: [15], name: 'Series Y', type: 'bar', stackgroup: 'g1', offsetgroup: 'group1', marker: { color: '#ff7f0e' } }, { x: ['Group 1'], y: [10], name: 'Series Z', type: 'bar', stackgroup: 'g1', offsetgroup: 'group1', marker: { color: '#2ca02c' } }, // 第二组:Group 2 的堆叠项 { x: ['Group 2'], y: [25], name: 'Series X', type: 'bar', stackgroup: 'g2', offsetgroup: 'group2', marker: { color: '#1f77b4' } }, { x: ['Group 2'], y: [18], name: 'Series Y', type: 'bar', stackgroup: 'g2', offsetgroup: 'group2', marker: { color: '#ff7f0e' } }, { x: ['Group 2'], y: [12], name: 'Series Z', type: 'bar', stackgroup: 'g2', offsetgroup: 'group2', marker: { color: '#2ca02c' } }, // 第三组:Group 3 的堆叠项 { x: ['Group 3'], y: [18], name: 'Series X', type: 'bar', stackgroup: 'g3', offsetgroup: 'group3', marker: { color: '#1f77b4' } }, { x: ['Group 3'], y: [14], name: 'Series Y', type: 'bar', stackgroup: 'g3', offsetgroup: 'group3', marker: { color: '#ff7f0e' } }, { x: ['Group 3'], y: [9], name: 'Series Z', type: 'bar', stackgroup: 'g3', offsetgroup: 'group3', marker: { color: '#2ca02c' } } ]; // 配置布局 const layout = { title: '分组堆叠柱状图示例', xaxis: { title: '大分组' }, yaxis: { title: '数值', stackmode: 'normal' // 确保堆叠数值正确累加 }, barmode: 'group', // 关键:让不同offsetgroup的分组并排 bargap: 0.1, // 分组内柱子的间距 bargroupgap: 0.2, // 不同分组之间的间距 legend: { title: '子类别' } }; // 渲染图表 Plotly.newPlot('myDiv', data, layout);
优化技巧:用循环批量生成Trace
如果你的分组或子类别较多,手动写每个Trace会很繁琐,用循环可以大幅简化代码:
const groups = ['Group 1', 'Group 2', 'Group 3']; const series = ['Series X', 'Series Y', 'Series Z']; const seriesColors = ['#1f77b4', '#ff7f0e', '#2ca02c']; // 二维数组:[系列][分组]的数值 const values = [ [20, 25, 18], [15, 18, 14], [10, 12, 9] ]; const data = []; groups.forEach((group, groupIdx) => { series.forEach((seriesName, seriesIdx) => { data.push({ x: [group], y: [values[seriesIdx][groupIdx]], name: seriesName, type: 'bar', stackgroup: `g${groupIdx + 1}`, offsetgroup: `group${groupIdx + 1}`, marker: { color: seriesColors[seriesIdx] } }); }); }); // 布局配置和渲染代码和上面一致 Plotly.newPlot('myDiv', data, layout);
这样不管你有多少分组或子类别,只需要修改数据源即可,维护起来方便很多。
内容的提问来源于stack exchange,提问作者Ivan
相关产品推荐
相关产品推荐

