Totals on column stacks

Building column stacks is easy: just set stackable = true on each column series. This tutorial will show how we can display automatic total value of the whole stack on top of it.

Base chart

Here's a base chart we're going to start with.

It's already got several ColumnSeries stacked. It also has "label bullets" set up to display individual column value if it fits.

See the Pen amCharts 4: showing stack total on top by amCharts team (@amcharts) on CodePen.0

Label bullets

If we take a closer look at the code in the above chart, we''ll notice that labels on columns series are bullets of type LabelBullet.

Each series has its label set up something like this:

let labelBullet = series.bullets.push(new am4charts.LabelBullet());
labelBullet.label.text = "{valueY}";
labelBullet.label.fill = am4core.color("#fff");
labelBullet.locationY = 0.5;
var labelBullet = series.bullets.push(new am4charts.LabelBullet());
labelBullet.label.text = "{valueY}";
labelBullet.label.fill = am4core.color("#fff");
labelBullet.locationY = 0.5;
{
// …
"series": [{
// …
"bullets": [{
"type": "LabelBullet",
"locationY": 0.5,
"label": {
"text": "{valueY}",
"fill": "#fff"
}
}]
}]
}

Label for "total"

Basically, a label for total is the same, with a few more/different configuration options.

Single series

First of all, since we only need one "total" label. We could add it to the last of the series (series can have any number of bullets) but that would mean that whenever that series is toggled off, our total bullet is toggled off as well.

Instead, we're going to create dummy series that will use zero values - and therefore not influence total number.

We're also make columns and their outlines transparent, as well as hide this series from legend.

let totalSeries = chart.series.push(new am4charts.ColumnSeries());
totalSeries.dataFields.valueY = "none";
totalSeries.dataFields.categoryX = "year";
totalSeries.stacked = true;
totalSeries.hiddenInLegend = true;
totalSeries.columns.template.strokeOpacity = 0;
var totalSeries = chart.series.push(new am4charts.ColumnSeries());
totalSeries.dataFields.valueY = "none";
totalSeries.dataFields.categoryX = "year";
totalSeries.stacked = true;
totalSeries.hiddenInLegend = true;
totalSeries.columns.template.strokeOpacity = 0;
{
// …
"series": [{
// …
}, {
// …
}, {
// …
}, {
"type": "ColumnSeries",
"dataFields": {
"valueY": "none",
"categoryX": "year"
},
"stacked": true,
"hiddenInLegend": true,
"columns": {
"strokeOpacity": 0
}
}]
}

NOTE We can make totals togglable just as well, by not hiding it from legend but, say, giving it a name = "Totals".

IMPORTANT Please also note, this new series uses "none" field as a its value field. This means that our data will have to be updated to have "none" fields with zeroes in them.

"Total" bullet

We already have our special series for displaying total label bullet. Let's add the bullet itself.

While it is the same LabelBullet we'll need to make it display different data.

While our regular labels used "{valueY}" to display value of the individual label, our total bullet will use "{valueY.total}" which will be replaced with a sum of values in the same stack.

We're also going to set various appearance options, so it looks a bit different from the rest of the labels.

let totalBullet = totalSeries.bullets.push(new am4charts.LabelBullet());
totalBullet.dy = -20;
totalBullet.label.text = "{valueY.total}";
totalBullet.label.hideOversized = false;
totalBullet.label.fontSize = 18;
totalBullet.label.background.fill = totalSeries.stroke;
totalBullet.label.background.fillOpacity = 0.2;
totalBullet.label.padding(5, 10, 5, 10);
var totalBullet = totalSeries.bullets.push(new am4charts.LabelBullet());
totalBullet.dy = -20;
totalBullet.label.text = "{valueY.total}";
totalBullet.label.hideOversized = false;
totalBullet.label.fontSize = 18;
totalBullet.label.background.fill = totalSeries.stroke;
totalBullet.label.background.fillOpacity = 0.2;
totalBullet.label.padding(5, 10, 5, 10);
{
// …
"series": [{
// …
}, {
// …
}, {
// …
}, {
"type": "ColumnSeries",
// …
"bullets": [{
"type": "LabelBullet",
"dy": 20,
"label": {
"text": "{valueY.total}",
"hideOversized": false,
"fontSize": 18,
"paddingTop": 5,
"paddingRight": 10,
"paddingBottom": 5,
"paddingLeft": 10,
"background": {
"fill": "#f999",
"fillOpacity": 0.2
}
}
}]
}]
}

A few of the configuration options deserve explanation:

  • dy adjusts vertical position of the element. We've set it to negative number to move our label slightly above the column.
  • hideOversized disables default behavior of hiding bullets/labels that do not fit into the available height (since we have zero-value columns, they would most definitely not fit).
  • background is a normally disabled background fill of the label. We've given it a color so our labels appear on a slight background rather just a number.

Final touches

We're not done, yet.

Calculating totals is a resource-intensive operation. And since it's not used in most of the charts, it's disabled by default.

We will need to enable it. To do that we'll need to set calculateTotals = true on our Value axis.

While we're at it, let's also give our labels some breathing space by expanding Value axis range, by using extraMax setting.

valueAxis.extraMax = 0.1;
valueAxis.calculateTotals = true;
valueAxis.extraMax = 0.1;
valueAxis.calculateTotals = true;
{
// ...
"yAxes": [{
"type": "ValueAxis",
// ...
"extraMax": 0.1,
"calculateTotals": true
}]
}

Total vs. sum

Please note, the {valueY.total} above calculates total out of absolute values. Meaning that you have both negative and positive values in your stack (e.g. 5, 3, and -5), the result will be sum of the absolute values: 13.

If we would rather use mathematical sum of the source values, we should use {valueY.sum} instead. This way, with the same values as above (5, 3, and -5) the result will be 3.

Final chart

See the Pen amCharts 4: showing stack total on top by amCharts team (@amcharts) on CodePen.0