堆叠柱状图
上一篇
堆叠面积图
下一篇
分布曲线图
Loading...
堆叠柱状图是柱状图的扩展,通过在单个柱子中堆叠多个数据系列来显示分类数据。每个柱子代表一个类别的总数,而柱子内的不同颜色段代表各个子类别的数值。堆叠柱状图不仅能够展示每个类别的总量,还能显示各个子类别对总量的贡献。
堆叠柱状图特别适合于比较多个类别的组成结构,以及观察各子类别在不同类别中的分布情况。通过颜色编码,用户可以轻松识别不同的子类别,并比较它们在各个主类别中的占比。
当需要同时展示总量和构成时,堆叠柱状图是一个很好的选择。可以进一步扩展为百分比堆叠柱状图来显示各部分的相对比例。
英文名:Stacked Bar Chart, Stacked Column Chart
图表类型 | 垂直堆叠柱状图 |
---|---|
适合的数据 | 多系列分类数据:一个分类数据字段、一个连续数据字段、一个系列分类字段 |
功能 | 对比不同类别的总量以及各子类别的构成 |
数据与图形的映射 | 分类字段映射到横轴位置 数值字段映射到柱子高度 系列字段映射到颜色,通过堆叠显示在同一柱子中 |
适合的数据条数 | 主类别不超过 12 条,子类别不超过 8 条 |
图表类型 | 水平堆叠柱状图 |
---|---|
适合的数据 | 多系列分类数据:一个分类数据字段、一个连续数据字段、一个系列分类字段 |
功能 | 对比不同类别的总量以及各子类别的构成 |
数据与图形的映射 | 分类字段映射到纵轴位置 数值字段映射到柱子宽度 系列字段映射到颜色 |
适合的数据条数 | 主类别不超过 30 条,子类别不超过 8 条 |
例子 1: 适合展示多城市降雨量的月度构成
下图展示了伦敦和柏林两个城市在不同月份的降雨量分布,能够清楚地看出每个月的总降雨量以及两个城市各自的贡献。
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'interval',autoFit: true,data: [{ name: 'London', 月份: 'Jan.', 月均降雨量: 18.9 },{ name: 'London', 月份: 'Feb.', 月均降雨量: 28.8 },{ name: 'London', 月份: 'Mar.', 月均降雨量: 39.3 },{ name: 'London', 月份: 'Apr.', 月均降雨量: 81.4 },{ name: 'London', 月份: 'May', 月均降雨量: 47 },{ name: 'London', 月份: 'Jun.', 月均降雨量: 20.3 },{ name: 'London', 月份: 'Jul.', 月均降雨量: 24 },{ name: 'London', 月份: 'Aug.', 月均降雨量: 35.6 },{ name: 'Berlin', 月份: 'Jan.', 月均降雨量: 12.4 },{ name: 'Berlin', 月份: 'Feb.', 月均降雨量: 23.2 },{ name: 'Berlin', 月份: 'Mar.', 月均降雨量: 34.5 },{ name: 'Berlin', 月份: 'Apr.', 月均降雨量: 99.7 },{ name: 'Berlin', 月份: 'May', 月均降雨量: 52.6 },{ name: 'Berlin', 月份: 'Jun.', 月均降雨量: 35.5 },{ name: 'Berlin', 月份: 'Jul.', 月均降雨量: 37.4 },{ name: 'Berlin', 月份: 'Aug.', 月均降雨量: 42.4 },],encode: { x: '月份', y: '月均降雨量', color: 'name' },transform: [{ type: 'stackY' }],interaction: [{type: 'elementHighlight',background: true,region: true,},],});chart.render();
说明:
月份
字段映射到横轴位置,表示时间维度月均降雨量
字段映射到柱子高度,通过 stackY
变换进行堆叠name
字段映射到颜色,区分不同城市例子 2: 适合展示人口年龄结构的地区对比
堆叠柱状图非常适合展示不同地区的人口年龄结构分布,既能看出各地区的总人口,又能分析年龄构成。
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'interval',autoFit: true,data: {type: 'fetch',value:'https://gw.alipayobjects.com/os/bmw-prod/f129b517-158d-41a9-83a3-3294d639b39e.csv',format: 'csv',},encode: { x: 'state', y: 'population', color: 'age' },transform: [{ type: 'stackY' }, { type: 'sortX', by: 'y', reverse: true }],axis: {x: {labelSpacing: 4,labelTransform: 'rotate(90)',},y: {labelFormatter: '~s',title: null,},},legend: {color: {position: 'right',title: '年龄段',},},interaction: [{type: 'elementHighlight',background: true,},],});chart.render();
说明:
sortX
变换按总人口数量排序,便于比较labelFormatter: '~s'
格式化数值显示(如 1M 表示 1 百万)例子 3: 水平堆叠柱状图适合类别较多的情况
当分类较多时,水平堆叠柱状图能够更好地展示标签,避免文字重叠。
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',theme: 'classic',width: 800,height: 600,});chart.options({type: 'interval',autoFit: true,data: {type: 'fetch',value:'https://gw.alipayobjects.com/os/bmw-prod/f129b517-158d-41a9-83a3-3294d639b39e.csv',format: 'csv',},coordinate: { transform: [{ type: 'transpose' }] },encode: { x: 'state', y: 'population', color: 'age' },transform: [{ type: 'stackY' }, { type: 'sortX', by: 'y', reverse: true }],axis: {y: {labelFormatter: '~s',title: null,},x: {title: null,},},legend: {color: {position: 'bottom',title: '年龄段',},},});chart.render();
说明:
coordinate: { transform: [{ type: 'transpose' }] }
实现水平方向例子 4: 百分比堆叠柱状图展示比例关系
当重点关注各部分占比而非绝对数值时,百分比堆叠柱状图是更好的选择。
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',theme: 'classic',width: 800,height: 600,});chart.options({type: 'interval',autoFit: true,data: {type: 'fetch',value:'https://gw.alipayobjects.com/os/bmw-prod/f129b517-158d-41a9-83a3-3294d639b39e.csv',format: 'csv',},coordinate: { transform: [{ type: 'transpose' }] },encode: { x: 'state', y: 'population', color: 'age' },transform: [{ type: 'stackY' },{ type: 'normalizeY' },{ type: 'sortX', by: 'y', reverse: true },],axis: {y: {labelFormatter: '.0%',title: null,},x: {title: null,},},legend: {color: {position: 'bottom',title: '年龄段',},},tooltip: {channel: 'y0',valueFormatter: '.0%',},});chart.render();
说明:
stackY
和 normalizeY
变换实现百分比堆叠labelFormatter: '.0%'
将 y 轴标签格式化为百分比例子 1: 不适合展示负值数据的简单堆叠
传统的堆叠柱状图不适合包含负值的数据,因为堆叠逻辑会导致视觉混乱。
例子 2: 子类别过多时视觉效果差
当子类别数量超过 8-10 个时,颜色区分度下降,用户难以有效区分和比较。
import { Chart } from '@antv/g2';// 模拟过多子类别的数据const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'interval',autoFit: true,data: [{ category: 'A', type: 'Type1', value: 10 },{ category: 'A', type: 'Type2', value: 8 },{ category: 'A', type: 'Type3', value: 6 },{ category: 'A', type: 'Type4', value: 4 },{ category: 'A', type: 'Type5', value: 3 },{ category: 'A', type: 'Type6', value: 2 },{ category: 'A', type: 'Type7', value: 2 },{ category: 'A', type: 'Type8', value: 1 },{ category: 'A', type: 'Type9', value: 1 },{ category: 'A', type: 'Type10', value: 1 },{ category: 'A', type: 'Type11', value: 1 },{ category: 'A', type: 'Type12', value: 1 },{ category: 'B', type: 'Type1', value: 12 },{ category: 'B', type: 'Type2', value: 9 },{ category: 'B', type: 'Type3', value: 7 },{ category: 'B', type: 'Type4', value: 5 },{ category: 'B', type: 'Type5', value: 4 },{ category: 'B', type: 'Type6', value: 3 },{ category: 'B', type: 'Type7', value: 2 },{ category: 'B', type: 'Type8', value: 2 },{ category: 'B', type: 'Type9', value: 1 },{ category: 'B', type: 'Type10', value: 1 },{ category: 'B', type: 'Type11', value: 1 },{ category: 'B', type: 'Type12', value: 1 },],encode: { x: 'category', y: 'value', color: 'type' },transform: [{ type: 'stackY' }],});chart.render();
在这种情况下,建议将较小的类别合并为"其他"类别,或者使用其他图表类型。
分散堆叠柱状图可以展示正负值的对比,特别适合展示盈亏、增减等对比数据。
import { Chart } from '@antv/g2';const chart = new Chart({ container: 'container' });chart.options({type: 'view',paddingLeft: 25,children: [{type: 'interval',data: {type: 'fetch',value: 'https://assets.antv.antgroup.com/g2/nivo-gain-lost.json',transform: [{type: 'fold',fields: ['lost > 100$','lost <= 100$','gained <= 100$','gained > 100$',],},],},encode: { x: 'user', y: 'value', color: 'key' },transform: [{ type: 'stackY' }],scale: {x: { padding: 0.2 },y: { domainMin: -100, domainMax: 100 },color: {domain: ['lost > 100$','lost <= 100$','gained <= 100$','gained > 100$',],range: ['#97e3d5', '#61cdbb', '#e25c3b', '#f47560'],},},style: { radius: 10 },axis: {y: { position: 'right', title: false, labelFormatter: (v) => `${v}%` },},legend: { color: { title: false } },labels: [{text: 'value',position: 'inside',formatter: (v) => (v ? `${v}%` : ''),transform: [{ type: 'overlapDodgeY' }],fill: '#000',fontSize: 10,},],},{type: 'lineY',data: [0],style: { lineWidth: 2, stroke: '#e25c3b', strokeOpacity: 1 },},{type: 'text',style: {x: -10,y: '75%',text: 'lost',fontWeight: 'bold',dy: -10,transform: 'rotate(-90)',fill: '#61cdbb',},},{type: 'text',style: {x: -10,y: '20%',text: 'gain',fontWeight: 'bold',dy: -10,transform: 'rotate(-90)',fill: '#e25c3b',},},],});chart.render();
说明:
domainMin
和 domainMax
控制坐标轴范围