当前位置: 代码迷 >> 综合 >> D3 scales and colors
  详细解决方案

D3 scales and colors

热度:34   发布时间:2024-01-11 15:57:55.0

By Jerome Cukier

点击打开链接



Scales: the main idea


scales transform a number in a certain interval( called domain) into a number in another interval( called range)




For instance, let's suppose you know your data is always over 20 and always below 80. you would like to plot it, say, in a bar plot,  which can be only 120 pixels tall.


you could, obviously, do the math:


.attr("height",function(d) {return (d-20)*2;})


but what happen when your data changes? you have to go back to the entrails  of your code and make the change. this is very error prone. so instead, you can use a scale.



var y = d3.scale.linear().domain(20,80).range(0,120);<strong>.attr("height“,y);  //this is equivalent to .attr("height",function(d){return y(d);});</strong>



Fun with Scales


quantitative scales can be of several types:

1. linear (including quantize and quantile) 

2. logarithmic 

3. power(including square root scales)


they have a lot in common


1. Domain and range

for all scales, with the exception of quantize and quantile scales which are a bit different, domain and range work the same

var y = d3.scale.linear().range([20,60]).domain([0,120]);

typically there're two numbers in the domain and range, but it can be more. If this is more, we are talking about a polypoint scale: there are as many segments in the intervals as there are numbers in the domain. When using the scale, if a number is in teh n-th segment of the domain, it's transformed into a number in the n-th segment of the range.




Bounds of domain and ranges need not be numbers, as long as they can be converted to numbers. One useful example are colors. Color names can be used as range.

var ramp = d3.scale.linear().domain([0,100]).range("red","blue");

this will transform any number between 0 and 100 into the corresponding color between red and blue;


Clamping:




var clamp = d3.scale().linear().domain([20,80]).range([0,120]);clamp(100) ;//160
clamp.clamp(true);
clamp(100); // 120

Scales and nice numbers


more often than not, the bounds of the domain and/or those of the ranges wil be calculated. So chances are they won't be round numbers, or numbers a human would like. Scales, however, come with a bunch of method to address that. 


Keep in mind that scales are often used to position marks along an axis.


.nice()

var data=[-2.347, 4, 5.23,-1.234,6.234,7.431]; // or whatevervar y = d3.scale.linear().domain(d3.extent(data)).range([0,120]);y.domain();//[-2.347,7.431]
y.nice();//[-3,-8]


.ticks()



.rangeRound()


this will guarantee that the output of the scales are integers, which is better to position marks on the screen with pixel precision than numbers with decimals.



.invert([value])


it will turns the scale upside down : given range number, returns which number in the domain .

var y=d3.scale.linear().range([0,120]);
y(50); //60
y.invert(60); //50


Power scales and log scales


 y=axk+b, or y=a.log(x)+b.



Also note that if you are using a log scale, you cannot have 0 in the domian


Quantize and quantile 



quantize works with a discrete, rather than continuous range, in other terms 

the range can only take a certain number of values;


var q=d3.scale.quantize().domain([0,10]).range([0,2,8]);q(0); //0
q(3); //0
q(5);//2
q(8);//8
q(10000);//8



quantile on the other hand, matches values in the domain(which is the full dataset) with their respective quantile. The number of quantiles is specified by the range.


var q=d3.scale.quantile().domian([0,1,5,6,2,4,6,2,4,6,7,8]).range([0,100]);
q.quantile(); //[4.5], only one quantile --the median
q(0);//0
q(4); //0
q(4.449);//0
q(4.5); //100
q(1000);//100q.range([0,25,50,75,100]);
q.quantiles(); // [2,4,5.6,6]
q(2);//25
q(4);


Ordinal scales


the big difference is that ordinal scales have a discrete domain. in other words, they can turn a limited number of values into something else, without caring what's between those values.


var x=d3.scale.ordinal().domain(["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]) // 7 items.rangeBands([0,120]);
x("Tuesday"); // 34.285714285714285


there're 3 possibilities for range() 

.rangePoints()

.rangeBands()

.range()


<span style="font-family:Arial;font-size:12px;">var x=d3.scale.ordinal().domain(["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]);
x.rangePoints([0,120]);
x("Saturday"); // 120
x.rangeBands([0,120]);
x("Saturday"); // 102.85714285714286
x("Saturday")+x.rangeBand(); // 120</span>


.range()

we can also use .range methods with several values. We can specify the domain, ornot.

then, if we use such a scale on a value which is not part of the domain(or if the domian is left empty), this value is added to the domain. If there'are n values in the range, while there're n+1 values in the domain, then, the n+1 value of the domain is matched with the first value in the range.


var x=d3.scale.ordial().range(["hello","world"]);x.domain();//[]
x(0);//hello
x(1);//world
x(2);//hello
x.domain();//[0,1,2]



Color palettes


There are 4 built-in color palette in protovis: d3.scale.category10(), d3.scale.category20(), d3.scale.category20b(), and d3.scale.category20c().


A palette like d3.scale.category10() works exactly like an ordinal scale.


var p=d3.scale.category10();
var r=p.range(); // ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", // "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"]
var s=d3.scale.ordinal().range(r); 
p.domain(); // [] - empty
s.domain(); // [] - empty, see above
p(0); // "#1f77b4"
p(1); // "#ff7f0e"
p(2); // "#2ca02c"
p.domain(); // [0,1,2];
s(0); // "#1f77b4"
s(1); // "#ff7f0e"
s(2); // "#2ca02c"
s.domain(); // [0,1,2];


Colors


Compared to protovis, d3.color is simpler. The main reason is that protovis handled color and transparency together with the pv.Color object, whereas in SVG, those two are distinct attributes: you handle the background color of a filled object with fill, its transparency with opacity, the color of the outline with stroke and the transparency of that color with stroke-opacity.

d3 has two color objects: d3_Rgb and d3_Hsl, which describe colors in the two of the most popular color spaces: red/green/blue, and hue/saturation/light.

With d3.color, you can make operations on such objects, like converting colors between various formats, or make colors lighter or darker.

d3.rgb(color), and d3.hsl(color) create such objects.
In this context, color can be (straight from the manual):

  • rgb decimal – “rgb(255,255,255)”
  • hsl decimal – “hsl(120,50%,20%)”
  • rgb hexadecimal – “#ffeeaa”
  • rgb shorthand hexadecimal – “#fea”
  • named – “red”, “white”, “blue”

Once you have that object, you can make it brighter or darker with the appropriate method.
You can use .toString() to get it back in rgb hexadecimal format (or hsl decimal), and .rgb() or .hsl() to convert it to the object in the other color space.

1
2
3
4
5
6
7
8
var c=d3.rgb( "violet" ) // d3_Rgb object
c.toString(); // "#ee82ee"
c.darker().toString(); // "#a65ba6"
c.darker(2).toString(); // "#743f74" - even darker
c.brighter().toString(); // "ffb9ff"
c.brighter(0.1).toString(); // "#f686f6" - only slightly brighter
c.hsl(); // d3_Hsl object
c.hsl().toString() // "hsl(300, 76, 72)"