1
votes

My goal is to combine a clustered and a stacked chart with amcharts 4. Each stack can contain both positive and negative values.

I am using a this example Stacked and Clustered Column Chart, but I modified the data in chart.data.

/**
 * ---------------------------------------
 * This demo was created using amCharts 4.
 * 
 * For more information visit:
 * https://www.amcharts.com/
 * 
 * Documentation is available at:
 * https://www.amcharts.com/docs/v4/
 * ---------------------------------------
 */

// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end

// Create chart instance
var chart = am4core.create("chartdiv", am4charts.XYChart);

// Add data
chart.data = [ {
  "year": "2003",
  "europe": -2.5,
  "namerica": -2.5,
  "asia": 2.1,
  "lamerica": 1.2,
  "meast": 0.2,
  "africa": -1
}, {
  "year": "2004",
  "europe": 2.6,
  "namerica": 2.7,
  "asia": 2.2,
  "lamerica": 1.3,
  "meast": 0.3,
  "africa": 0.1
}, {
  "year": "2005",
  "europe": 2.8,
  "namerica": 2.9,
  "asia": 2.4,
  "lamerica": 1.4,
  "meast": 0.3,
  "africa": 0.1
} ];

// Create axes
var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "year";
categoryAxis.title.text = "Local country offices";
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.minGridDistance = 20;
categoryAxis.renderer.cellStartLocation = 0.1;
categoryAxis.renderer.cellEndLocation = 0.9;

var  valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.title.text = "Expenditure (M)";

// Create series
function createSeries(field, name, stacked) {
  var series = chart.series.push(new am4charts.ColumnSeries());
  series.dataFields.valueY = field;
  series.dataFields.categoryX = "year";
  series.name = name;
  series.columns.template.tooltipText = "{name}: [bold]{valueY}[/]";
  series.stacked = stacked;
  series.columns.template.width = am4core.percent(95);
}

createSeries("europe", "Europe", false);
createSeries("namerica", "North America", true);
createSeries("asia", "Asia", false);
createSeries("lamerica", "Lating America", true);
createSeries("meast", "Middle East", true);
createSeries("africa", "Africa", true);

// Add legend
chart.legend = new am4charts.Legend();
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

#chartdiv {
  width: 100%;
  height: 500px;
}
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/charts.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
<div id="chartdiv"></div>

This is the result I got

As you see the stacking for the first element is wrong, as i stacks over the last negative element, not over the current stack.

I manage to get the expected behavior by adding empty series between the stacks; but then I am left with a blank space I am unable to remove. unsatisfying workaround

Is there a way to either:

a) Correctly stack the series

b) Totally hide an empty series, including its space on the category axis?

Thank you

2

2 Answers

0
votes

This note in our guide on stacking data is relevant to the phenomenon you're seeing:

Note about stacked data

Please note, that the chart does not support stacking of series with gaps in data.

By setting a series' stacked property to false, we're saying we want another cluster of stacked columns to start at that series. The Africa series is the third stack in the second cluster, but the first to have a negative value for that first category (2003). I am not sure how that's expected to be displayed, e.g. when everything else is positive, how is it a negative value occupies its share in a stack especially when it's stacked on top of positive values?

Nevertheless, if we remove the "gap" in data and make the Africa series the first stack in the new cluster, you'll find for the purposes of this data set things work again:

createSeries("europe", "Europe", false);
createSeries("namerica", "North America", true);
createSeries("africa", "Africa", false); // Switched this with Asia
createSeries("asia", "Asia", true);
createSeries("lamerica", "Latin America", true);
createSeries("meast", "Middle East", true);

Screenshot:

screenshot of demo

Demo:

https://codepen.io/team/amcharts/pen/d824b734db7d2e266aa29c440d98eedb

0
votes

I found the correct workaround for my needs: insert empty series with stacked=false to separate the stacks but put stacked=true for all other series:

createSeries("europe", "Europe", true);
createSeries("namerica", "North America", true);
// empty series to separate stacks //
   var series = chart.series.push(new am4charts.ColumnSeries());
   series.dataFields.valueY = '';
   series.dataFields.categoryX = '';
////
createSeries("asia", "Asia", true);
createSeries("lamerica", "Lating America", true);
createSeries("meast", "Middle East", true);
createSeries("africa", "Africa", true);

this way I managed to get the expected result without having to re-order the series

The main drawback is that an empty element is created in the legend. In my case it didn't matter because I used a custom legend created in an external div.