Chat with us, powered by LiveChat

How to Add Multi-level Category Axis — Challenge AnyChart!

November 29th, 2017 by Vitaly Radionov

Multi-level Category Axis — Challenge AnyChart!We are glad to present to you a new article in the Challenge AnyChart! series.

Just to remind you, these are articles inspired by your requests to our Support Team and our will to publicly prove that the opportunities of interactive data visualization with our JavaScript (HTML5) charting libraries are profound.

One of our customers asked us how to add a multi-level category axis for categorical data and display subcategories. Ok, there you go! We’ve made a quick tutorial and created the corresponding JavaScript chart example (you can already see it in the title picture).

Data Visualization Task

Here’s how the task of the challenge was laid down by the customer:

We’ve got data with nested subcategories. How to display both categories and subcategories in stacked columns with the help of AnyChart?

In addition, the customer shared the following picture to illustrate what he would like to create by using the JavaScript charting library of AnyChart:

Multi-level Category Axis | From customer

To build a solution according to this task, here’s what we need:

  • work with data by means of view and iterator;
  • use extra axes;
  • use custom scales;
  • 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 scales and bounds completed, we’ll add extra axes.

Find Groups in Data

Working with a data set does not stop after feeding it as data to a chart.

With the help of the mapAs method, it is possible to get a different view, i.e. a breakdown by a given parameter. We’ll take on this opportunity.

Then, let’s use the iterator object to explore the view, in order to find categories and subcategories.

Use Extra Axes Not As Axes

In the JavaScript chart we are going to build, we will use three X-axes overall, primary one and two extra axes.

One X-axis will be the default X-axis that we’ll use exactly as an axis. We’ll only disable its own ticks.

Another X-axis will serve solely to position main categories. We won’t display its ticks as well as the axis line.

The third X-axis will be utilized to position ticks that are common for the former two axes.

Additional Scales for Supplementary Calculations

Axes work with scales and essentially visualize them. So in order to implement the idea, we’ll need a custom scale containing data about categories and subcategories.

We’ll analyze the data and create a set of ticks and category names. Then we’ll build the axes on that scale.

Use Weights for Ticks as Visualization Tool

To get a beautiful picture in which different groups of data are separated from one another, let’s modify the data and insert points with empty values in it.

After that, in order to shift the visual focus from the empty points to the real data, we’ll use the weight feature.

Kind of padding we’ll get in result should make the perception of the data on such a visualization more efficient.

Result: Interactive JavaScript Chart with Multi-level Category Axis

Look at the result!

See the Pen Sub-Categories on X-Axis by Vitaly (@Radionov) on CodePen.light

Check out the code of the chart with the multi-level category axis:

anychart.onDocumentReady(function () {
    var data = preprocessData([
        ['Accelerate', 'Onsite', 18, NaN, NaN],
        ['CIS Renew', 'Offshore', 6, NaN, 2],
        ['CIS Renew', 'Onsite', 7, 1, 4],
        ['CIS Others', 'Offshore', NaN, NaN, 1],
        ['CIS Others', 'Onsite', 2, 1, 1]
    ]);

    var chart = anychart.column();

    // configure global settings for series labels
    chart.labels({position:'center', fontColor:'#000'});

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

    // turn on stacking
    chart.yScale().stackMode('value');

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

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

    // calculate extra axes
    createTwoLevelAxis(chart, data, 0.1);

});

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 - 2; i++) {
            var previous = data[i-1][0];
            var current = data[i][0];
            if (current!=previous)
            {
                data.splice(i, 0, [previous], [current]);
                i = i+2;
            }
        }
    }
    return anychart.data.set(data);
}

function createTwoLevelAxis(chart, data, padding){
    // subcategory names
    var names = [];
    // ticks for axes based on 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 us to go over data, so
    // create an iterator for the 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 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?1:padding);
    }

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

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

    // create an extra chart axis and hide its ticks and the axis line, leaving only labels displayed
    chart.xAxis(1)
            .scale(customScale)
            .stroke('none')
            .ticks(false);
    // draw one more extra axis without the axis line and labels, leaving only big ticks
    var additionalXaxis = chart.xAxis(2);
    additionalXaxis.scale(customScale);
    additionalXaxis.labels(false);
    additionalXaxis.stroke('none');
    additionalXaxis.ticks()
            .length(46)
            .position('inside');
}

We hope you like this article explaining how to build a JavaScript chart with the multi-level category axis (X-axis) and that you’ll find it useful.

If you have an interesting but complicated data visualization task and you are unsure (yet) how to deal with it in AnyChart JS Charts, send us an email at support@anychart.com with the subject line “Challenge”. We are always happy to help you. And your question may well become the focus of our next Challenge AnyChart! article.

In fact, all is possible with AnyChart when it comes to interactive data visualization! We’ll show you how.


Comments (1)

*