一、直方图简介
直方图就是一种照片的分析方式,横向代表亮度,纵向代表像素数量。首先分析出照片中所有像素的亮度,然后计算出具体数值,再把它们映射到横轴上。这样的话,越高,这个亮度上的像素就越多。
直方图的观看规则就是“左黑右白”,左边代表暗部,右边代表亮部,而中间则代表中间调。
纵向上的高度代表像素密集程度,越高,代表的就是分布在这个亮度上的像素很多。
直方图用于描述概率分布,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,2
将这个用图形展示出来的,就是直方图。
好了,开始制作吧~
二、数据
d3.random.normal
生成一个函数,这个函数能够按正态(高斯)分布随机生成数值。要传入两个参数,第一个是位置参数,第二个是尺寸参数。关于正态分布的定义,可参见维基百科。将这个函数赋值给 rand 之后,接下来只要用 rand()
即可生成随机数。
三、布局(数据转换)
接下来,要将上述数据进行转换,即确定一个区间和分隔数之后,另数组的数值落在各区域里。先定义一个布局:
d3.layout.histogram:
直方图的布局range:
区间的范围bins:
分隔数frequency:
若值为 true,则统计的是个数;若值为 false,则统计的是概率接下来即可转换数据:
来看看转换前后的数据有什么分别吧。转换前:
转换后:
可以看到,转换后的数组,长度即分隔数,每一个区间内有落到此区间的数值(图中的0,...),数值的个数(length),还
有三个参数:
x: 区间的起始位置
dx: 区间的宽度
y: 落到此区间的数值的数量(如果 frequency 为 true);落到此区间的概率(如果 frequency 为 false)
四、绘制
绘制之前,需要定义一个比例尺,因为通常我们需要让转换后的 y 在希望的范围内伸缩。
最后,绘制图形:
graphics.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x",function(d,i){
return i * rect_step;
})
.attr("y",i){
return max_height - yScale(d.y);
})
.attr("width",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",i){
return i rect_step + rect_step/2;
})
.attr("y1",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",i){
return max_height;
})
.attr("dx",rect_step/2 - 8)
.attr("dy","15px")
.text(function(d){
return Math.floor(d.x);
});