Jens Törnell
Jens Törnell

Reputation: 24778

Chartjs average line over bars

I use https://www.chartjs.org/. In the example below I list values for every day for two weeks. For that I want a line of the average values for this period.

Is it possible with chartjs and if so how?

enter image description here

I've started but it follows the bar locations and it should create a new path with fewer points.

Chartjs

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <canvas id="canvas"></canvas>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>
  <script>
    var ctx = document.getElementById('canvas').getContext('2d');
    var mixedChart = new Chart(ctx, {
      type: 'bar',
      data: {
        datasets: [{
          label: 'Bar Dataset',
          data: [1, 2, 3, 1, 3, 4, 7, 2, 4, 3, 1, 6, 5, 2],
          backgroundColor: "#FF9881",
          order: 2
        }, {
          label: 'Line Dataset',
          data: [1, 2],
          type: 'line',
          borderColor: "#FF312D",
          fill: false,
          borderWidth: 1,
          order: 1
        }],
        labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
      },
      options: {
        "scales": {
          "yAxes": [{
            "ticks": {
              "beginAtZero": true
            }
          }]
        }
      }
    });
  </script>
</body>

</html>

Upvotes: 2

Views: 6884

Answers (2)

setedivento
setedivento

Reputation: 43

If your requirements are to draw just a flat, average line, I would go for their official annotation plugin [chartjs-plugin-annotation].

I will insert a really basic, self-consistent example here, adapted from their documentation.

index.html:

<html>
    <head>
        <script src="index.js"></script>
    </head>

    <body>
        <canvas id="canvas"></canvas>
    </body>
</html>

anyname.js [which you then need to browserify into the index.js source file]:

/* the following is just a shortcut to register all the needed elements, for any chart.
   more info here:
   https://www.chartjs.org/docs/3.3.0/getting-started/integration.html#bundlers-webpack-rollup-etc */
const Chart = require("chart.js/auto");

const annotationPlugin = require("chartjs-plugin-annotation");

function average(ctx) {
    const values = ctx.chart.data.datasets[0].data;
    return values.reduce((a, b) => a + b, 0) / values.length;
}

const data = {
    labels: ["1", "2", "3", "4", "5", "6", "7"],
    datasets: [
        {
            label: "Sample Series",
            data: [40, 100, 54, 34, 13, 78, 41]
        }
    ]
};

const annotation = {
    type: 'line',
    borderColor: 'black',
    borderDash: [6, 6],
    borderDashOffset: 0,
    borderWidth: 3,
    label: {
        enabled: true,
        content: (ctx) => "Average: " + average(ctx).toFixed(2),
        position: 'end'
    },
    scaleID: 'y',
    value: (ctx) => average(ctx)
};

const config = {
    type: 'bar',
    data,
    options: {
        plugins: {
            title: {
                display: true,
                text: "Sample Chart",
                font: {
                    size: 14
                }
            },
            annotation: {
                annotations: {
                    annotation
                }
            }
        }
    }
};

// the annotation plugin needs to be registered, too
Chart.register(annotationPlugin);

document.addEventListener('DOMContentLoaded', () => {
    const myChart = new Chart(document.getElementById('canvas'), config);
}, false);

Node required libraries [which you need to install for browserify to work]:

  • chart.js -- v. 3.7.1
  • chartjs-plugin-annotation -- v. 1.3.1

References:

https://www.chartjs.org/chartjs-plugin-annotation/1.2.0/samples/line/average.html

Upvotes: 2

Argee
Argee

Reputation: 1224

I would calculate the average and spread it over the whole graph like this:

const getLineData = (initialData, lengthOfDataChunks) => {
  const numOfChunks = Math.ceil(initialData.length / lengthOfDataChunks);
  const dataChunks = [];

  for (var i = 0; i < numOfChunks; i++) dataChunks[i] = [];

  initialData.forEach((entry, index) => {
    const chunkNumber = Math.floor(index / lengthOfDataChunks);
    dataChunks[chunkNumber]
    dataChunks[chunkNumber].push(entry);
  });

  const averagedChunks = dataChunks.map(chunkEntry => {
    const chunkAverage = chunkEntry.reduce(sumArray) / lengthOfDataChunks;
    return chunkEntry.map(chunkEntryValue => chunkAverage);
  });

  return averagedChunks.flat();
}

const ctx = document.getElementById('canvas').getContext('2d');
const barData = [1, 2, 3, 1, 3, 4, 7, 2, 4, 3, 1, 6, 5, 2];
const sumArray = (accumulator, currentValue) => accumulator + currentValue;
const averageBarValue = barData.reduce(sumArray) / barData.length;
const lineData = getLineData(barData, 7);

var mixedChart = new Chart(ctx, {
  type: 'bar',
  data: {
    datasets: [{
      label: 'Bar Dataset',
      data: barData,
      backgroundColor: "#FF9881",
      order: 2
    }, {
      label: 'Line Dataset',
      data: lineData,
      type: 'line',
      borderColor: "#FF312D",
      fill: false,
      borderWidth: 1,
      order: 1
    }],
    labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
  },
  options: {
    "scales": {
      "yAxes": [{
        "ticks": {
          "beginAtZero": true
        }
      }]
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>


<canvas id="canvas"></canvas>

I hope I got your question right. If not, please let me know!

Upvotes: 2

Related Questions