Kevin
Kevin

Reputation: 36

How to have two consecutive points in a line chart be in the same category?

I'm trying to create a line chart that can have up to two points in one category in chartjs. In the example, the labels and data are each passed as arrays, so each label can only have one point. I would like to have up to two points.

I tried to put extra entries in the data, but the extra entry did not do anything.

    data: {
        datasets: [{
            data: [10, 20, 30, 40, 50, 60, 70] // Only the first 6 entries are shown.
        }],
        labels: ['January', 'February', 'March', 'April', 'May', 'June']
    },

Upvotes: 0

Views: 121

Answers (3)

Kevin
Kevin

Reputation: 36

I ended up figuring the problem out with the following, using the parsing option:

    data: {
        datasets: [{
            data: [
                {number: 10, month: 'January'},
                {number: 20, month: 'February'}
            ]
        }]
    },

and in the options

    parsing: {
      xAxisKey: "number",
      yAxisKey: "month"
    }

Upvotes: 1

MD AKMAL_04
MD AKMAL_04

Reputation: 11

short answer

actually data is an array of datasets so u can exactly add one more dataset object in array

labels,
datasets: [  //  a r r a y
  {
    fill: true,
    label,
    data,
    backgroundColor,
    borderColor,
  },
  {
       A N O T H  E R  D A T A S E T
   }
],

Upvotes: 0

kikon
kikon

Reputation: 8495

I don't see a simple solution to this. Each dataset data should be an array of data points, while the labels array should provide x axis values for those data points if the x axis is of type category, which is the default for a chart of type line.

Let's say that our data looks like this:

const data = {
    datasets: [{
        data: [10, 20, 30, 40, 50, 60, 70, 80]
    }],
    labels: ['January', 'February', 'February', 'March', 'April', 'May', 'June', 'June'],
};

by which we are defining the fact that two categories, the one for February and the one for June have two points while the others one point; other possible conventions might be used, if one wants to avoid duplication of text prone to typos.

To have a better control of the point positions and of the labels of the x axis you may employ a (possibly hidden) linear axis, together with setting relevant x values in the datasets data.

There are two possibilities: the first, if you want the categories to have the same size, then the points in the categories with two points will be closer together.

In this case one may use a secondary hidden linear x axis to position the points, while showing a uniform category x axis with the labels without repetitions:

const data = {
    datasets: [{
        data: [10, 20, 30, 40, 50, 60, 70, 80]
    }],
    labels: ['January', 'February', 'February', 'March', 'April', 'May', 'June', 'June'],
};

let xi = 0;
data.datasets.forEach(dataset => {
    dataset.data = dataset.data.map((y, i) => {
        let x = xi + 0.5;
        if(data.labels[i] === data.labels[i-1]){
            x += 1/6;
        }
        else if(data.labels[i] === data.labels[i+1]){
            x -= 1/6;
            xi -= 1;
        }
        xi += 1;
        return ({x, y});
    })
});

const options = {
    maintainAspectRatio: false,
    plugins: {
        tooltip:{
            callbacks: {
                title: ([{dataIndex}]) => data.labels[dataIndex]
            }
        },
        legend: {
            display: false,
        }
    },
    scales: {
        x: {
            display: false,
            type: 'linear',
        },
        x2:{
            type: 'category',
            offset: true,
            labels: [... new Set(data.labels)],
            grid: {
                color: 'rgba(0,0,0,0.4)',
                offset: true
            }
        },
        y: {
            beginAtZero: true,
        },
    },
};

new Chart('myChart', {type: 'line', options, data});
<canvas style="height: 160px" id="myChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

The second case, if you want to keep points equidistant and have the categories with variable widths, such that a category with two points will be twice the size of a one point one.

In this case, we should keep only the linear x axis visible, but modify its labels to correspond to the original data.labels. A second, similar, linear x axis might be the solution to show grid lines separating the categories:

const data = {
    datasets: [{
        data: [10, 20, 30, 40, 50, 60, 70, 80]
    }],
    labels: ['January', 'February', 'February', 'March', 'April', 'May', 'June', 'June'],
};

data.datasets.forEach(dataset => {
    dataset.data = dataset.data.map((y, i) => ({x: i+0.5, y}));
});

const options = {
    maintainAspectRatio: false,
    plugins: {
        tooltip:{
            callbacks: {
                title: ([{dataIndex}]) => data.labels[dataIndex]
            }
        },
        legend: {
            display: false,
        }
    },
    scales: {
        x: {
            type: 'linear',
            min: 0,
            max: data.labels.length,
            ticks: {
                stepSize: 0.5,
                callback: (value) => {
                    const idx = Math.floor(value),
                        label = data.labels[idx];
                    if(Math.abs(Math.round(value) - value) < 1e-6){
                        if(label === data.labels[idx - 1]){
                            return label;
                        }
                        else{
                            return null;
                        }
                    }
                    else{
                        if(label === data.labels[idx - 1] || label === data.labels[idx + 1]){
                            return null;
                        }
                        return label;
                    }
                }
            }
        },
        x2:{
            type: "linear",
            min: 0,
            max: data.labels.length,
            ticks: {
                display: false,
                stepSize: 0.5,
                callback(value, ...args){
                    if(value === this.max || value === this.min){
                        return '';
                    }

                    if(Math.abs(Math.round(value) - value) < 1e-6){
                        const idx = Math.floor(value),
                            label = data.labels[idx];
                        if(label === data.labels[idx - 1]){
                            return null;
                        }
                        else{
                            return '';
                        }
                    }
                    else{
                        return null;
                    }
                }
            },
            grid: {
                color: 'rgba(0,0,0,0.4)',
                drawTicks: false
                //offset: true
            }
        },
        y: {
            beginAtZero: true,
            //display: false,
        },
    },
};

new Chart('myChart', {type: 'line', options, data});
<canvas style="height: 160px" id="myChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

Upvotes: 0

Related Questions