D3.js实现直方图的方法详解
一、直方图简介
直方图就是一种照片的分析方式,横向代表亮度,纵向代表像素数量。首先分析出照片中所有像素的亮度,然后计算出具体数值,再把它们映射到横轴上。这样的话,越高,这个亮度上的像素就越多。
直方图的观看规则就是“左黑右白”,左边代表暗部,右边代表亮部,而中间则代表中间调。
纵向上的高度代表像素密集程度,越高,代表的就是分布在这个亮度上的像素很多。
直方图用于描述概率分布,D3 提供了直方图的布局 Histogram 用于转换数据。
假设有数组 a = [10, 11, 11.5, 12.5, 13, 15, 19, 20 ],现在把10~20的数值范围分为5段,即:
10~12, 12~14, 14~16, 16~18, 18~20
那么数组 a 的各数值都落在这几段区域的哪一部分呢?经过计算,可以知道,这5段分别具有的元素个数为:
3, 2, 1, 0 , 2
将这个用图形展示出来的,就是直方图。
好了,开始制作吧~
二、数据
首先生成随机数据:
var rand = d3.random.normal(0,25); var dataset = []; for(var i=0;i<100;i++){ dataset.push( rand() ); }
d3.random.normal
生成一个函数,这个函数能够按正态(高斯)分布随机生成数值。要传入两个参数,第一个是位置参数,第二个是尺寸参数。关于正态分布的定义,可参见维基百科。将这个函数赋值给 rand 之后,接下来只要用 rand()
即可生成随机数。
三、布局(数据转换)
接下来,要将上述数据进行转换,即确定一个区间和分隔数之后,另数组的数值落在各区域里。先定义一个布局:
var bin_num = 15; var histogram = d3.layout.histogram() .range([-50,50]) .bins(bin_num) .frequency(true);
d3.layout.histogram: 直方图的布局
range: 区间的范围
bins: 分隔数
frequency: 若值为 true,则统计的是个数;若值为 false,则统计的是概率
接下来即可转换数据:
var data = histogram(dataset);
来看看转换前后的数据有什么分别吧。转换前:
转换后:
可以看到,转换后的数组,长度即分隔数,每一个区间内有落到此区间的数值(图中的0,1,2,...),数值的个数(length),还
有三个参数:
x: 区间的起始位置
dx: 区间的宽度
y: 落到此区间的数值的数量(如果 frequency 为 true);落到此区间的概率(如果 frequency 为 false)
四、绘制
绘制之前,需要定义一个比例尺,因为通常我们需要让转换后的 y 在希望的范围内伸缩。
var max_height = 400; var rect_step = 30; var heights = []; for(var i=0;i<data.length;i++){ heights.push( data[i].y ); } var yScale = d3.scale.linear() .domain([d3.min(heights),d3.max(heights)]) .range([0,max_height]);
最后,绘制图形:
//绘制图形 var graphics = svg.append("g") .attr("transform","translate(30,20)"); //绘制矩形 graphics.selectAll("rect") .data(data) .enter() .append("rect") .attr("x",function(d,i){ return i * rect_step; }) .attr("y", function(d,i){ return max_height - yScale(d.y); }) .attr("width", function(d,i){ return rect_step - 2; }) .attr("height", function(d){ return yScale(d.y); }) .attr("fill","steelblue"); //绘制坐标轴的直线 graphics.append("line") .attr("stroke","black") .attr("stroke-width","1px") .attr("x1",0) .attr("y1",max_height) .attr("x2",data.length * rect_step) .attr("y2",max_height); //绘制坐标轴的分隔符直线 graphics.selectAll(".linetick") .data(data) .enter() .append("line") .attr("stroke","black") .attr("stroke-width","1px") .attr("x1",function(d,i){ return i * rect_step + rect_step/2; }) .attr("y1",max_height) .attr("x2",function(d,i){ return i * rect_step + rect_step/2; }) .attr("y2",max_height + 5); //绘制文字 graphics.selectAll("text") .data(data) .enter() .append("text") .attr("font-size","10px") .attr("x",function(d,i){ return i * rect_step; }) .attr("y", function(d,i){ return max_height; }) .attr("dx",rect_step/2 - 8) .attr("dy","15px") .text(function(d){ return Math.floor(d.x); });
五、总结
以上就是这篇文章的全部内容了,希望能对大家的学习或者工作带来一定的帮助,如果有疑问大家可以留言交流。
下一篇:关于JS中二维数组的声明方法