ChartJS: datalabels: show percentage value in Pie piece
Asked Answered
I

10

57

I have a piechart with four labels:

var data = [{
    data: [50, 55, 60, 33],
    labels: ["India", "China", "US", "Canada"],
    backgroundColor: [
        "#4b77a9",
        "#5f255f",
        "#d21243",
        "#B27200"
    ],
    borderColor: "#fff"
}];

Using chartjs-plugin-datalabels plugin I wanted to show percentage value in each Pie piece with below code:

formatter: (value, ctx) => {

        let datasets = ctx.chart.data.datasets;

        if (datasets.indexOf(ctx.dataset) === datasets.length - 1) {
            let sum = 0;
            datasets.map(dataset => {
                sum += dataset.data[ctx.dataIndex];
            });
            let percentage = Math.round((value / sum) * 100) + '%';
            return percentage;
        } else {
            return percentage;
        }
    },
    color: '#fff',
}

I am getting 100% value for all the pie pieces, instead of respective percentages. Here is the JSFiddle (https://jsfiddle.net/kingBethal/a1Lvn4eb/7/)

Inglorious answered 27/8, 2018 at 17:9 Comment(1)
I am aware of chartjs-plugin-labels which can achive this, but I prefer chartjs-plugin-datalabels as I have dependency on this plugin for other charts functions.Inglorious
S
81

Updated fiddle with 2 decimal precision.

You were not computing the sum, instead storing the current value in sum only for every value.

Here is the working fiddle : https://jsfiddle.net/a1Lvn4eb/55/

var data = [{
    data: [50, 55, 60, 33],
    labels: ["India", "China", "US", "Canada"],
    backgroundColor: [
        "#4b77a9",
        "#5f255f",
        "#d21243",
        "#B27200"
    ],
    borderColor: "#fff"
}];

var options = {
    tooltips: {
        enabled: false
    },
    plugins: {
        datalabels: {
            formatter: (value, ctx) => {
                let sum = 0;
                let dataArr = ctx.chart.data.datasets[0].data;
                dataArr.map(data => {
                    sum += data;
                });
                let percentage = (value*100 / sum).toFixed(2)+"%";
                return percentage;
            },
            color: '#fff',
        }
    }
};

var ctx = document.getElementById("pie-chart").getContext('2d');
var myChart = new Chart(ctx, {
    type: 'pie',
    data: {
        datasets: data
    },
    options: options
});
Seaton answered 27/8, 2018 at 17:17 Comment(8)
No, [50, 55, 60, 33] are data values and I wanted to show respective percentage of the sum total. It should be [25.25%, 27.77%, 3.3%, 16.66%]Inglorious
Sorry My bad, Updated answer. Do you need 2 point precision also ?Seaton
Updated as per requirement :)Seaton
How can I hide if there is a 0% value and any specific Label (ex: China) on the chart.Inglorious
Thanks, @Seaton but it's not working for multiple data set. in the case where we had a line chartKirkwood
labels are not centered in chart piece. why?Quietly
Small nitpick here. toFixed() can be unreliable. Better to use Intl.NumberFormat() nowadays.Perceivable
I needed to add sum += parseInt(data); to get it working.Ledaledah
P
15

chart.js >= 3.x

This is update for chart.js >= 3.x and chartjs-plugin-datalabels >= 2.x

chartjs-plugin-datalabels plugin no longer registers itself automatically (docs)

You have to do it manually

Register plugin for all charts

Chart.register(ChartDataLabels)

or only to specific charts

var chart = new Chart(ctx, {
  plugins: [ChartDataLabels],
  options: {
    // ...
  }
})

And here's the code below to render the pie chart

var data = [{
    data: [50, 55, 60, 33],
    backgroundColor: [
        "#4b77a9",
        "#5f255f",
        "#d21243",
        "#B27200"
    ],
    borderColor: "#fff"
}];

var options = {
    tooltips: {
        enabled: false
    },
    plugins: {
        datalabels: {
            formatter: (value, ctx) => {
                const datapoints = ctx.chart.data.datasets[0].data
                const total = datapoints.reduce((total, datapoint) => total + datapoint, 0)
                const percentage = value / total * 100
                return percentage.toFixed(2) + "%";
            },
            color: '#fff',
        }
    }
};

var ctx = document.getElementById("pie-chart").getContext('2d');
var myChart = new Chart(ctx, {
    type: 'pie',
    data: {
        labels: ['India', 'China', 'US', 'Canada'],
        datasets: data
    },
    options: options,
    plugins: [ChartDataLabels],
});
.container {
   width: 40%;
   margin: 5px auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.2/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.0.0/chartjs-plugin-datalabels.min.js"></script>
 
 
 
<div class="container">
   <canvas id="pie-chart"></canvas>
</div>
Pachisi answered 14/12, 2021 at 23:44 Comment(1)
Your answer is very useful for me - but the problem is I use side script what gives final output in a format like [{"student_name":"APNET","marks":"45"},{"student_name":"ARZ","marks":"55"}] - so I have not a simple data: [45, 55] (what works !) but I suppose something like ["45", "55"] under data: marks - as a result a calculation is wrong at all. Is it possible to str_replace somewhere at the beginning where formatter: (value, ctx) => { to try remove double quites from ["45", "55"] what I suppose data: marks passes to const datapoints = ctx.chart.data.datasets[0].data ?Confiteor
L
11

I like to add a little in accepted answer, ctx.chart.data.datasets[0].data always gives you entire data even if you filter out some data by clicking on legend, means you will always get same percentage for a country even if you filter out some countries.

I have used context.dataset._meta[0].total to get the filtered total.

Here is the working snippet:

var data = [{
  data: [50, 55, 60, 33],
  backgroundColor: [
    "#4b77a9",
    "#5f255f",
    "#d21243",
    "#B27200"
  ],
  borderColor: "#fff"
}];

var options = {
  tooltips: {
    enabled: true
  },
  plugins: {
    datalabels: {
      formatter: (value, ctx) => {

        let sum = ctx.dataset._meta[0].total;
        let percentage = (value * 100 / sum).toFixed(2) + "%";
        return percentage;


      },
      color: '#fff',
    }
  }
};


var ctx = document.getElementById("pie-chart").getContext('2d');
var myChart = new Chart(ctx, {
  type: 'pie',
  data: {
  labels: ['India', 'China', 'US', 'Canada'],
    datasets: data
  },
  options: options
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<canvas id="pie-chart"></canvas>
Laster answered 19/12, 2019 at 4:47 Comment(4)
Thanks - but this doesn't actually require jquery, correct?Permalloy
Yes, it doesn't require jquery.Laster
Apparently it doesn't work here with version chartjs v3.7.0 and chartjs-plugin-datalabels v2.0.0. _meta is no valid variable (anymore?). Trying to figuring out what else might work. Background: I also want to update the percentages according to the visible ones.Hardball
I came up with an ugly monkey patch workaround I will post as a new answer, since this comment system is not suitable for that. Sorry. I am posting it here in this thread, though! --> https://mcmap.net/q/1632485/-chartjs-datalabels-show-percentage-value-in-pie-pieceHardball
C
7

The problem is how you're calculating sum. See below.

Fiddle

 var data = [{
   data: [50, 55, 60, 33],
   labels: ["India", "China", "US", "Canada"],
   backgroundColor: [
     "#4b77a9",
     "#5f255f",
     "#d21243",
     "#B27200"
   ],
   borderColor: "#fff"
 }];

 var options = {
   tooltips: {
     enabled: false
   },
   plugins: {
     datalabels: {
       formatter: (value, ctx) => {

         let datasets = ctx.chart.data.datasets;

         if (datasets.indexOf(ctx.dataset) === datasets.length - 1) {
           let sum = datasets[0].data.reduce((a, b) => a + b, 0);
           let percentage = Math.round((value / sum) * 100) + '%';
           return percentage;
         } else {
           return percentage;
         }
       },
       color: '#fff',
     }
   }
 };


 var ctx = document.getElementById("pie-chart").getContext('2d');
 var myChart = new Chart(ctx, {
   type: 'pie',
   data: {
     datasets: data
   },
   options: options
 });
Clincher answered 27/8, 2018 at 17:30 Comment(2)
Thanks Michael, it works too. I prefer @Seaton answer as it shows 2 decimal as well, which I need.Inglorious
sorry it didn't work for me in with last version of chartjs 3.0 I updated the plugin tooPhantasmagoria
A
6

You could use the tooltip with an Array reducer to perform the percentage calculation and display it.

  tooltips: {
    callbacks: {
      label: function (tooltipItem, data) {
        try {
          let label = ' ' + data.labels[tooltipItem.index] || '';

          if (label) {
            label += ': ';
          }

          const sum = data.datasets[0].data.reduce((accumulator, curValue) => {
            return accumulator + curValue;
          });
          const value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];

          label += Number((value / sum) * 100).toFixed(2) + '%';
          return label;
        } catch (error) {
          console.log(error);
        }
      }
    }
  }
Analogous answered 26/11, 2019 at 16:9 Comment(1)
I had to change a minor change in reduce method: return parseInt(accumulator) + parseInt(curValue);Fillin
Z
4

As some people mentioned, context.dataset._meta[0].total seem to have been removed in chart.js version 3 and above and didn't work anymore. Instead, I used context.chart.getDatasetMeta(0).total and it worked for me - it shows percentage values in pie chart and value gets updated based on the filtered total when the legends are clicked.

      options:{ 
        plugins:{         
          datalabels: {
            color: 'white',
            formatter: function(value, context) {                  
              return Math.round(value/context.chart.getDatasetMeta(0).total * 100) + "%" ;
            }
          }
        }
      }
Zerline answered 28/2, 2022 at 15:0 Comment(0)
H
3

I am using ChartJS v3.7.0 and chartjs-plugin-datalabels v2.0.0 and this answer:

https://mcmap.net/q/1632485/-chartjs-datalabels-show-percentage-value-in-pie-piece

does not really work for me, while I just wanted to keep the percentages up to date, also when hiding elements. It seems that ctx.dataset._meta is not available anymore. I came up with some ugly monkey patch:

formatter: function(value, context) {
    var hiddens = context.chart._hiddenIndices;
    var total = 0;
    var datapoints = context.dataset.data;
    datapoints.forEach((val, i) => {
        if (hiddens[i] != undefined) {
            if (!hiddens[i]) {
                total += val;
            }
        } else {
            total += val;
        }
    });
    var percentage = (value / total * 100).toFixed(2) + '%';
    var out = context.chart.data.labels[context.dataIndex] + '\n' + percentage;
    return out;
}

Hopw this might help anybody. Cheers.

Hardball answered 10/2, 2022 at 15:50 Comment(1)
See this answer after founding it by myself after some hours haha. It is indeed a great solution!Legibility
A
2

Apparently, ChartDataLabels is now exported from another package chartjs-plugin-datalabels. I installed the package using npm i chartjs-plugin-datalabels and then modified my code as below.

import React from "react";
import ChartDataLabels from "chartjs-plugin-datalabels";
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
import { Pie } from "react-chartjs-2";

ChartJS.register(ArcElement, Tooltip, Legend, ChartDataLabels);

export default function PieChart() {
  return (
    <>
      <Pie
        options={{
          responsive: true,
          maintainAspectRatio: false,
          plugins: {
            legend: { display: false },
            tooltip: false,
            datalabels: {
              formatter: (value, ctx) => {
                let sum = 0;
                let dataArr = ctx.chart.data.datasets[0].data;
                dataArr.map((data) => {
                  sum += data;
                });
                let percentage = ((value * 100) / sum).toFixed(2) + "%";
                return percentage;
              },
              color: "#FFF",
            },
          },
          onHover: function (e) {
            e.native.target.style.cursor = "pointer";
          },
          rotation: 270,
          events: ["click"],
          onClick: function (event, element) {
            console.log(element[0].index);
          },
        }}
        data={{
          labels: ["Above 60%", "Below 60%"],
          datasets: [
            {
              data: [20, 10],
              backgroundColor: ["#3A925D", "#FD7E14"],
              borderWidth: 0,
            },
          ],
        }}
      />
    </>
  );
}

Result enter image description here

Aldred answered 20/12, 2022 at 15:35 Comment(0)
A
1

if you use nodejs: follow the steps:

  1. npm install chartjs-plugin-datalabels
  2. import ChartDataLabels from 'chartjs-plugin-datalabels';
  3. Chart.register(...registerables, ChartDataLabels);
  4. copy the code from the over comments...

https://chartjs-plugin-datalabels.netlify.app/guide/#table-of-contents

Antivenin answered 17/1, 2022 at 18:12 Comment(0)
L
0

With a native 999 Bytes JavaScript Web Component: https://pie-meister.github.io/

<script src="https://pie-meister.github.io/PieMeister.min.js"></script>

<pie-chart pull="10" pulltext="100">
  <style>
    text {
      font: 3em Arial;
      text-anchor: middle;
      fill: white;
    }
  </style>
  <slice size="25%" stroke="#4b77a9" pull>India $size</slice>
  <slice size="28%" stroke="#5f255f" pull>China $size</slice>
  <slice size="30%" stroke="#d21243" pull>US $size</slice>
  <slice size="17%" stroke="#B27200" pull>Canada $size</slice>
</pie-chart>

JSWC

Landgraviate answered 23/7, 2023 at 8:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.