Loading...
A stacked bar chart is an extension of the basic bar chart that displays categorical data by stacking multiple data series within a single bar. Each bar represents the total amount for a category, while the different colored segments within the bar represent the values of various subcategories. Stacked bar charts not only show the total quantity for each category but also display how each subcategory contributes to the total.
Stacked bar charts are particularly suitable for comparing the compositional structure of multiple categories and observing the distribution of subcategories across different main categories. Through color coding, users can easily identify different subcategories and compare their proportions within each main category.
When you need to display both totals and composition simultaneously, stacked bar charts are an excellent choice. They can be further extended to percentage stacked bar charts to show the relative proportions of each part.
Other Names: Stacked Column Chart
Chart Type | Vertical Stacked Bar Chart |
---|---|
Suitable Data | Multi-series categorical data: one categorical field, one continuous field, one series categorical field |
Function | Compare totals of different categories and composition of subcategories |
Data-to-Visual Mapping | Categorical field mapped to horizontal axis position Numerical field mapped to bar height Series field mapped to color, displayed stacked in the same bar |
Suitable Data Volume | Main categories no more than 12, subcategories no more than 8 |
Chart Type | Horizontal Stacked Bar Chart |
---|---|
Suitable Data | Multi-series categorical data: one categorical field, one continuous field, one series categorical field |
Function | Compare totals of different categories and composition of subcategories |
Data-to-Visual Mapping | Categorical field mapped to vertical axis position Numerical field mapped to bar width Series field mapped to color |
Suitable Data Volume | Main categories no more than 30, subcategories no more than 8 |
Example 1: Suitable for displaying monthly rainfall composition across multiple cities
The chart below shows the rainfall distribution in London and Berlin across different months, clearly showing both the total monthly rainfall and each city's contribution.
import { Chart } from '@antv/g2';const chart = new Chart({container: 'container',theme: 'classic',});chart.options({type: 'interval',autoFit: true,data: [{ name: 'London', month: 'Jan.', rainfall: 18.9 },{ name: 'London', month: 'Feb.', rainfall: 28.8 },{ name: 'London', month: 'Mar.', rainfall: 39.3 },{ name: 'London', month: 'Apr.', rainfall: 81.4 },{ name: 'London', month: 'May', rainfall: 47 },{ name: 'London', month: 'Jun.', rainfall: 20.3 },{ name: 'London', month: 'Jul.', rainfall: 24 },{ name: 'London', month: 'Aug.', rainfall: 35.6 },{ name: 'Berlin', month: 'Jan.', rainfall: 12.4 },{ name: 'Berlin', month: 'Feb.', rainfall: 23.2 },{ name: 'Berlin', month: 'Mar.', rainfall: 34.5 },{ name: 'Berlin', month: 'Apr.', rainfall: 99.7 },{ name: 'Berlin', month: 'May', rainfall: 52.6 },{ name: 'Berlin', month: 'Jun.', rainfall: 35.5 },{ name: 'Berlin', month: 'Jul.', rainfall: 37.4 },{ name: 'Berlin', month: 'Aug.', rainfall: 42.4 },],encode: { x: 'month', y: 'rainfall', color: 'name' },transform: [{ type: 'stackY' }],interaction: [{type: 'elementHighlight',background: true,region: true,},],});chart.render();
Explanation:
month
field is mapped to horizontal axis position, representing the time dimensionrainfall
field is mapped to bar height, stacked using the stackY
transformname
field is mapped to color to distinguish different citiesExample 2: Suitable for displaying age structure comparison across regions
Stacked bar charts are very suitable for showing population age structure distribution across different regions, allowing you to see both total population and age composition.
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: 'Age Group',},},interaction: [{type: 'elementHighlight',background: true,},],});chart.render();
Explanation:
sortX
transform to sort by total population for easier comparisonlabelFormatter: '~s'
formats numeric display (e.g., 1M for 1 million)Example 3: Horizontal stacked bar charts are suitable for scenarios with many categories
When there are many categories, horizontal stacked bar charts can better display labels and avoid text overlap.
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: 'Age Group',},},});chart.render();
Explanation:
coordinate: { transform: [{ type: 'transpose' }] }
to achieve horizontal orientationExample 4: Percentage stacked bar charts display proportional relationships
When focusing on the proportion of each part rather than absolute values, percentage stacked bar charts are a better choice.
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: 'Age Group',},},tooltip: {channel: 'y0',valueFormatter: '.0%',},});chart.render();
Explanation:
stackY
and normalizeY
transforms to achieve percentage stackinglabelFormatter: '.0%'
formats y-axis labels as percentagesExample 1: Not suitable for simple stacking of negative value data
Traditional stacked bar charts are not suitable for data containing negative values, as the stacking logic can cause visual confusion.
Example 2: Poor visual effect when there are too many subcategories
When the number of subcategories exceeds 8-10, color differentiation decreases, making it difficult for users to effectively distinguish and compare.
import { Chart } from '@antv/g2';// Simulate data with too many subcategoriesconst 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();
In such cases, it's recommended to merge smaller categories into an "Others" category or use other chart types.
Diverging stacked bar charts can display comparisons of positive and negative values, particularly suitable for showing profit/loss, increase/decrease contrasts.
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();
Explanation:
domainMin
and domainMax
to control axis range