布局 直方图

直方图(Histogram),是用于表示数据分布的图表。

何为数据分布?

假设,某学校高三学生共有 100 人。这 100 人里:

也就是说,100 个人的身高数据分布在上述五个区间里。将以上分布表现出来的图表,就是直方图。光看“直方图”这三个字,很容易让人联想到柱形图,但两者的意义完全不同。

数据

D3 中提供有 d3.random.normal(),用于生成正态分布,该函数的第一个参数是平均值,第二个是标准差,本例中分别设置为 170 和 10,即这 100 个人的平均身高是 170cm。请看下面的代码:

//创建一个生成正态分布的函数,其中平均值为170,标准差为10
var rand = d3.random.normal(170,10);

//定义一个数组,用于保存正态分布生成的数值
var dataset = [];

//根据正态分布生成100个随机数,插入到数组dataset中
for(var i=0; i<100; i++){
  dataset.push( rand() );
  }
  
//输出数组到控制台
console.log(dataset);

按正态分布生成的 100 个数值被保存在数组 dataset 里。dataset 的部分数据如下图所示。

布局(转换数据)

创建一个直方图布局,设定 bins 数为 20,数据分布的范围为 130cm~210cm,统计方式为数量统计。

var binNum = 20,
   rangeMin = 130,
   rangeMax = 210;
    
var histogram = d3.layout.histogram()
            .range([rangeMin, rangeMax])  //数据分布的范围
            .bins(binNum)   //bins的数量
            .frequency(true);  //按照数量统计的方式
  
var hisData = histogram(dataset);    //转换数据
console.log(hisData);

转换后的数据保存在变量hisData中,在控制台的输出结果如图10-38所示。可以看到hisData变成一个长度为20的数组,数组的每一项代表一个分布区间。每一项里含有落到此区间的数值,例如落到第六区间的数值有:

156.22,157.23,154.14,155.05

此外,还有三个变量,意义如下。

绘制

由转换后的数据可知,x轴表示区间,y轴表示此区间的数量。例如,身高在158cm~162cm(x轴)区间内的人数为14人(y轴)。我们知道,不能用14个像素来代表14个人,那样太短了。因此,需要用到比例尺,x轴使用序数比例尺,y轴使用线性比例尺。

首先,定义x轴的比例尺:

var xAxisWidth = 450,
   xTicks = hisData.map( function(d){ return d.x; } );
  
   var xScale = d3.scale.ordinal()
          .domain(xTicks)
          .rangeRoundBands([0, xAxisWidth],0.1);

xAxisWidth 是坐标轴的实际长度,单位为像素,作为比例尺值域的上限使用。xTicks 是一个数组,保存有x轴的刻度,其值为:

[130, 134, 138, 142, 146, 150, 154, 158, 162, 166, 170, 174, 178, 182, 186, 190, 194, 198, 202, 206]

这是使用 hisData.map() 求得的。map() 是数组对象 Array 的一个方法,在 map 中定义一个无名函数 function(d),如此一来,hisData 的每一项元素都会调用此无名函数,最终返回一个数组。xTicks 作为 x 轴比例尺的定义域使用。

其次,绘制 x 轴,令 x 轴的刻度在轴的下方。

//外边框
var padding = { top: 30 , right: 30, bottom: 30, left: 30 };
  
//绘制x轴
var xAxis = d3.svg.axis()
      .scale(xScale)
      .orient("bottom")
      .tickFormat(d3.format(".0f"));
          
svg.append("g")
    .attr("class","axis")
    .attr("transform","translate(" + padding.left + "," + 
              (height - padding.bottom) +  ")")
    .call(xAxis);

结果如下图所示。坐标轴的每一个刻度表示一个区间,例如 130 表示身高在 130cm~134cm 之间。

然后,添加 y 轴的比例尺。虽然本例不将 y 轴画出来,但是需要比例尺来伸缩长度,因为刚才提到,不可能用 14 个像素来表示 14 个人。代码如下。

var yAxisWidth = 450;

var yScale = d3.scale.linear()
      .domain([ d3.min(hisData, function(d){ return d.y; }),
           d3.max(hisData, function(d){ return d.y; }) ])
      .range([5, yAxisWidth]);

这是一个线性比例尺,要用于计算矩形和曲线的 y 坐标,其值域被设定为 [5, 450],也就是说,d.y 中的最小值对应到 5,最大值对应到 450。

最后,添加矩形元素。绑定数组 hisData,添加对应数量的矩形,再用比例尺分别计算矩形的 x、y、width、height。

//绘制矩形
var gRect = svg.append("g")
        .attr("transform","translate(" + padding.left + "," +
                    ( -padding.bottom ) +  ")");
  
gRect.selectAll("rect")
    .data(hisData)
    .enter()
    .append("rect")
    .attr("class","rect")
    .attr("x",function(d,i){     //x坐标
      return xScale(d.x);    
    })
    .attr("y", function(d,i){      //y坐标
      return height - yScale(d.y);
    })
    .attr("width", function(d,i){    //宽度
      return xScale.rangeBand(); 
    })
    .attr("height", function(d){   //高度
      return yScale(d.y);
    });

结果

源代码

下载地址:histogram.zip

在线演示

委托名片直接发送邮件(需邮箱客户端)点击复制邮箱地址14721230383项目委托、技术支持,欢迎与我们联系。休息日7:00 ~ 11:0012:00 ~ 22:00工作日11:00 ~ 11:4016:00 ~ 22:00

Copyright 2014-2016, DecemberCafe, All Rights Reserved