CodeWorld
CodeWorld

Reputation: 2307

How can I draw pie chart with custom color in d3.js

I am getting some data which are in range of 0 to 100, so I want to group them in different group say [0, 50, 80, 100] and plot pie chart with different color for each group of data like
(0-50 red, 51-80 yellow, 81-100 green).

function drawPieChart(data) {
        var height = 500;
        var width = 500;
        var radius = Math.min(width, height)/4;
        var canvas = d3.select('#divId').append('svg')
            .attr('width', width).attr('height', height);
        var arc = d3.arc().outerRadius(radius - 10).innerRadius(0);
        var pie = d3.pie().sort(null).value(function (data) {
            return data.value;
        });
        var color = d3.scaleOrdinal().domain([0,50,80,100]).range(['red', 'orange', 'green']);
        var arcs = canvas.selectAll('arc')
            .data(pie(data))
            .enter()
            .append('g')
            .attr('class', 'arc')
            .append('path')
            .attr('d', arc).attr('transform', 'translate(' + height /4 + ',' + width /4 +')')

             .style('fill', function (data) {
                //**my silly attempt to achieve this. unsuccessful though**

                /*if(data.value > 90){
                    return arc.apply('fill', 'blue');
                }else if(data.value > 70 && data.value <= 90){
                    return arc.apply('fill', 'green');
                }else if(data.value > 50 && data.value <= 70){
                    return arc.apply('fill', 'yellow');
                }else{
                    return arc.attr('fill', 'red');
                }*/

                return color(data); // this function is resulting the whole graph of one color, whichever defind first in range (here red)
            })
    }

Upvotes: 0

Views: 1380

Answers (2)

REEE
REEE

Reputation: 517

You can use d3.scaleThreshold, docs here: https://github.com/d3/d3-scale#threshold-scales.

I used a pie chart example bl.ock from here, to get this codepen: https://codepen.io/anon/pen/Yjwryz?editors=0011, and here's the code you would be interested in:

var threshold = 0.00000000000001;
var groupDomain = [
  50 + threshold,   // Red group        x: (x <= 50)
  70 + threshold,   // Yellow group     x: (50 < x <= 70)
  90 + threshold,   // Green group      x: (70 < x <= 90)
  100               // Blue group       x: (x > 90)
];
var color = d3.scaleThreshold()
              .domain(groupDomain)
              .range(['red', 'yellow', 'green', 'blue']);
                          ...
                          ...
  .style("fill", function(d) {return color(d.data); });

Note, I added a small error threshold to the group domains you wanted since I think .scaleThreshold is set up to work up until a value in the domain, i.e. < 50 not <= 50. I'm not sure though I didn't check the docs.

Edit: Sorting the data array with '.sort' will take care of the problem you're having with unordered data:

var data = [
  100, 20, 30, 40, 50, // Red group
  55, 10, 65, 70,     // Yellow group
  75, 80, 90,         // Green group
  95, 100             // Blue group
].sort(function (a, b) { 
  return a - b
});

Updated codepen: https://codepen.io/anon/pen/Yjwryz?editors=0011

Second edit: I attempted what I believe you were referring to in your comment by grouping all of the data between the specified ranges and then basing the arc segment on the amount of data points which fall in each specified range. Here's a codepen: https://codepen.io/anon/pen/wxMNgg?editors=0010

The relevant code for this change:

var color = d3.scaleThreshold()
              .domain(groupDomain)
              .range(['red', 'yellow', 'green', 'blue']);

var groupedData = [];
data.forEach(function (e) {
  var group = color.invertExtent(color(e))[1];
      group = (group == 100) ? group : group - threshold;
  var groupIndex = groupedData.findIndex(d => d.group == group)

  if (groupIndex != -1) {
    groupedData[groupIndex].count += 1;
  } else {
    groupedData.push({
      group : group,
      count : 1
    });
  }
});

Upvotes: 1

rioV8
rioV8

Reputation: 28653

You need to group your data before creating the Pie chart

var dataByGroup = d3.nest()
  .key(function(d) { if (d<=50) return "50"; if (d<=80) return "80"; return "100"; })
  .rollup(function(v) { return v.length; })
  .entries(data);

Upvotes: 0

Related Questions