How to Code JS Column Chart with Multi-Level X-Axis — Challenge AnyChart!

April 17th, 2019 by Irina Maximova

How to code an interactive JS column chart with a wulti-level X-axis using the AnyChart JavaScript charting library, in the Challenge AnyChart! blog featureWe continue to update the Challenge AnyChart! section of our blog with new data visualization tutorials. They nicely demonstrate how powerful our JavaScript charting library is, which our Support Team is always eager to prove to everyone. In one of the first challenges, we already told you how to create a JS chart with nested axes. Since our customers keep showing interest in such forms of data presentation, now we’ll show you how to build another interesting chart with a different appearance but quite similar code — an interactive JS column chart with a multi-level X-axis.

Data Visualization Task

Here’s the issue a customer asked us to solve:

I want to display the data with subcategories in a column chart, is it possible?

To show how the nested axis should be placed, they attached the following picture:
Customer's chart with a multi-level X-axis

Typically, here is what’s needed to make a chart like the one in the picture:

  • work with data using view and iterator objects,
  • use extra axis,
  • use custom scales, and
  • use weights of scale ticks.

Solution Overview

First of all, let’s modify the source data and add empty values in it to visually separate data by category.

Then, once the chart has been drawn and calculations for the scales and bounds completed, add an extra axis and set the ticks and labels in a preferred way.

Preprocessing

Before feeding the data to the chart, let’s add empty values into the data, which will make visible where one category ends and another begins.

To achieve this, add one empty value to the beginning of the array, one more to the end, and two empty items every time the category name changes.

function preprocessData(data){
  if (data.length > 0) {
    data.unshift([data[0][0]]);
    data.push([data[data.length - 1][0]]);
    for (var i = 2; i < data.length - 1; i++) {
      var previous = data[i-1][0];
      var current = data[i][0];
      if (current!=previous) {
        data.splice(i, 0, [previous], [current]);
        i = i+2;
      }
      else {
        data.splice(i, 0, [previous]);
        i += 1;
      }
    }
  }
  return anychart.data.set(data);
}

When it’s done, add subcategory names to the meta using the mapAs() method to get a different view and use them as names of the X-axis ticks this way:

chart.column(data.mapAs({'year': 0, 'value': 2, 'sub-category': 1}));
chart.xScale().names('sub-category');

Now the JS column chart itself can be drawn.

Extra Axis and Additional Scale on JS Column Chart

The time has come for adding an extra axis to the chart. To achieve this, create a function with the iterator object. It will be used for exploring the view and finding categories and subcategories.

After that, draw ticks between the categories and set up the weights. This will give the chart a better look:

var iter = data.mapAs({'category': 0, 'sub-category': 1}).getIterator();
while(iter.advance()) {
  var name = iter.get('category');
  var value = iter.get('sub-category');
  names.push(name);
  if (name && names[names.length - 1] != names[names.length - 2])
    ticks.push(iter.getIndex());
  }
  weights.push(value?0.5:0.2);
}

Axes work with scales. To be more precise, the former visualize the latter. So in order to implement the idea, a custom scale containing data of categories and subcategories is needed. Then let’s pass the values, names, and ticks to this scale:

var customScale = anychart.scales.ordinal();
customScale.values(chart.xScale().values());
customScale.names(names);
customScale.ticks(ticks);

And build the new axis on it:

chart.xAxis(1)
  .scale(customScale)
  .orientation('top')
  .ticks(true);

Finally, we synchronize the weights with the chart scale:

chart.xScale().weights(weights);

And disable the ticks on the main axis:

chart.xAxis(0).ticks(false);

As was said, everything is possible with AnyChart! Now the entire JS column chart with a multi-level X-axis, which has been created along the tutorial, is ready to be presented. Check it out right here below, and if you want, you are welcome to view and modify this sample on AnyChart Playground.


The full code is placed below. Take a look through the lines to better understand the implementation:

anychart.onDocumentReady(function () {
  var data = preprocessData([
    ['2016', 'Rel 04', 18],
    ['2016', 'Rel 06', 13],
    ['2016', 'Rel 10', 17],
    ['2017', 'Rel 02', 4],
    ['2017', 'Rel 04', 13],
    ['2017', 'Rel 06', 12],
    ['2017', 'Rel 08', 6],
    ['2017', 'Rel 10', 17],
    ['2018', 'Rel 02', 12],
    ['2018', 'Rel 06', 9],
    ['2018', 'Rel 10', 15]
  ]);

  var chart = anychart.column();

  // configure global settings for series labels
  chart.labels({position:'top'});

  // add subcategory names to the meta of one of the series
  chart.column(data.mapAs({'year': 0, 'value': 2, 'sub-category': 1}));

  // use subcategory names as names of X-axis ticks
  chart.xScale().names('sub-category');

  chart.xAxis().labels().rotation(90);
  chart.xAxis().labels().anchor("center");
  chart.xAxis().overlapMode('allow-overlap');

  // set a container and draw the chart
  chart.container('container').draw();

  // calculate an extra axis
  createTwoLevelAxis(chart, data);
});

function preprocessData(data){
  // to make beautiful spacing between categories, add
  // several empty lines with the same category names to the data
  if (data.length > 0) {
    // add one to the beginning of the array
    data.unshift([data[0][0]]);
    // add one more to the end of the data
    data.push([data[data.length - 1][0]]);
    // add two empty items every time the category name changes,
    // to each category
    for (var i = 2; i < data.length - 1; i++) {
      var previous = data[i-1][0];
      var current = data[i][0];
      if (current!=previous) {
        data.splice(i, 0, [previous], [current]);
        i = i+2;
      } 
      else {
        data.splice(i, 0, [previous]);
        i += 1;
      }
    }
  }
  return anychart.data.set(data);
}

function createTwoLevelAxis(chart, data, padding){
  // subcategory names
  var names = [];
  // ticks for axes based on main categories
  var ticks = [];
  // weights of ticks (to make spacing between categories by using
  // the empty lines created in preprocessData)
  var weights = [];
  // the iterator feature allows you to go over data, so
  // create an iterator for a new breakdown
  var iter = data.mapAs({'category': 0, 'sub-category': 1}).getIterator();
  while(iter.advance()) {
    var name = iter.get('category');
    var value = iter.get('sub-category');
    // store category names
    names.push(name);
    // when the border between the categories is identified, create a tick
    if (name && names[names.length - 1] != names[names.length - 2]) 					{
      ticks.push(iter.getIndex());
    }
    // assign weight to the tick
    weights.push(value?0.5:0.2);
  }

  // create a custom scale
  var customScale = anychart.scales.ordinal();
  // supply values from the chart to the scale
  customScale.values(chart.xScale().values());
  // names and ticks of the main categories only
  customScale.names(names);
  customScale.ticks(ticks);

  // synchronize weights with the chart scale
  chart.xScale().weights(weights);

  // disable ticks along the main axis
  chart.xAxis(0).ticks(false);

  // create an extra chart axis
  chart.xAxis(1)
    .scale(customScale)
    .orientation('top')
    .ticks(true);

  chart.xAxis(1).ticks().length(60).position('center');
  chart.xAxis(1).labels().offsetY(30);

  chart.xGrid(0).scale(customScale);

  chart.title('Year / Release');

  // format the tooltip title
  chart.tooltip().titleFormat("{%year}");

  // format the tooltip body
  chart.tooltip().format("{%sub-category}: {%value}");
}

Conclusion

Hoping you liked this article, we invite you to check out more of the similar ones in the Challenge AnyChart! section of our blog. If you have any interesting data visualization questions that might be a good fit for a tutorial like this, please send an email to our Support Team at support@anychart.com with “Challenge” in the subject line.

We will be glad to keep showing you how to create cool, sophisticated data visualizations using AnyChart JS Charts and further demonstrate the vast capabilities of our JS charting library in action.

Any feedback is always welcome. If you have something to say, please write a comment below.


No Comments Yet

*