Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I generate as many colors as I want using d3?

I'm building a pie chart using d3.js, and visualizing a big data set. There are more than 137 items to visualize on the chart. I have just 10 colors using this function.

d3.scale.category10().range()

by exploring other options : https://github.com/mbostock/d3/wiki/Ordinal-Scales

d3.scale.category20().range()

var chart = nv.models.pieChart()
    .x(function(d) {
        return d.key
    })
    .y(function(d) {
        return d.y
    })
    .color(d3.scale.category10().range())
    .width(width)
    .height(height);

How can I generate as many colors as I want using d3?

like image 399
user3147036 Avatar asked Dec 30 '13 20:12

user3147036


People also ask

What is scaleLinear in D3?

The d3. scaleLinear() method is used to create a visual scale point. This method is used to transform data values into visual variables.

What is D3 range?

range([0, 600]); D3 creates a function myScale which accepts input between 0 and 10 (the domain) and maps it to output between 0 and 600 (the range).


2 Answers

Update: d3v5 now includes continuous color scales, such as d3.interpolateSpectral. That's probably the simplest option.


I had the same problem, so I wrote a little tool to generate LOTS of perceptually-different colors: category color generator.

This tool produces a list of colors. You can then use that list like:

color = d3.scale.ordinal()
    .domain(YOUR_DATA_CATEGORIES)
    .range(["#30c514", "#9321ff", ...]);

There is also a generalised version if two lightnesses is not enough.

Here are some pre-generated example colour sets.

like image 117
jnnnnn Avatar answered Oct 13 '22 00:10

jnnnnn


As you are using a lot of categories it is impossible to use perceptually different colors. The good news is that, in a pie chart, only two colors are next to each one, then all do not need to be different, just those that are adjacent.

What I would do is generate a two different color scales, both using d3.interpolateHcl(). HCL and Lab are better color models to generate natural color gradients and are also what category20() used to generate perceptually different colors.

var colorScales =[
  d3.scale.linear()
    .interpolate(d3.interpolateHcl) 
    .range(["#9AF768", "#F27A4D"]),
  d3.scale.linear()
    //.domain([0,1])
    .interpolate(d3.interpolateHcl) 
    .range(["#112231","#3C769D"])
];

Put the colors you want in the range([...]) and apply these functions adding some randomness. I'm using the index i of the data to alternate between the two color schemes.

.color(function(d,i) {
  return colorScales[i%2]       
          (Math.random());
})

More on color theory and color models:

  • https://gist.github.com/mbostock/3014589
  • http://vis4.net/blog/posts/avoid-equidistant-hsv-colors
  • https://eagereyes.org/basics/rainbow-color-map
  • http://www.paulvanslembrouck.com/2011/theres-something-about-yellow

And a good HCL color space colorpicker:

  • http://tristen.ca/hcl-picker

UPDATE

The newer versions of d3 provide more colors schemes and interpolators with more sophisticated models. There are some types of schemes and I think the most usefull for this use case are the sequential multi-hue interpolators.

I have developed an interactive widget to explore these scales and interpolators while changing the number of colors. See below.

Note that is also difficult with these schemes to provide several perceptually different colors.

const categorical = [{
    "name": "interpolateViridis"  },
  { "name": "interpolateInferno"  },
  { "name": "interpolateMagma"    },
  { "name": "interpolatePlasma"   },
  { "name": "interpolateWarm"     },
  { "name": "interpolateCool"     },
  { "name": "interpolateCubehelixDefault"  },
  { "name": "interpolateRainbow"  },
  { "name": "interpolateSinebow"  }
]

const width = 600,
  height = 80;

const svg = d3.select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height)
  .append("g")


const unit = (s) => width / s.n;
const state = {
  n: 12,
  colorScale: d3.scaleSequential(d3[categorical[8].name])
};

const update = () => {

  bars.data(d3.range(0, 1, 1 / state.n))
    .enter().append("rect")
    .attr("class", "bars")
    .attr("y", 0)
    .attr("height", height)
    .attr("width", unit(state))
    .attr("x", (d, i) => i * unit(state))
    .style("fill", state.colorScale)
    .merge(bars);
    
  bars.exit().remove();

}

const bars = svg.selectAll(".bars");

update()

const sequentialButtons = d3.select(".categoricalButtons")
  .selectAll("button")
  .data(categorical)
  .enter().append("button")
  .text(d => d.name)
  .on("click",  (buttonValue) =>{ 
    state.colorScale = d3.scaleSequential(d3[buttonValue.name]);
    update();
    });

const itemsInput = d3.select('#items')
  .on('change',(d,i,arr)=>{
    state.n = arr[i].value;
    update();
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<div class="categoricalButtons">Color Scales: </div>
<div><input id="items" type="number" value="12"></div>
like image 37
David Lemon Avatar answered Oct 12 '22 23:10

David Lemon