梦入琼楼寒有月,行过石树冻无烟

D3 直方布局

直方布局(Histogram Layout),可以用于表示数据的分布。可分为矩阵直方图(Matrix histogram)曲线直方图(Curve histogram)两种表达方式。

ID DA FA
d3.layout.histogram() 创建一个直方图布局
histogram.value() 设置或获取值访问器
histogram.range() 设置数据的分布范围,主要分为两个数组指定(分别表述上限(min)以及下线(max)
histogram.bins() 设置数据的分布区间的个数或长度,如果没有参数则默认返回当前设置的值
histogram.bins(count) count作为参数则代表数据的分布区间(bins) 的数量,bins的区间长度则会被均匀的分割
histogram.bins(thresholds) 可以指定任意长度的区间(如:[100, 125, 130, 135, 140, 145]
histogram.bins(function) 当前函数必须返回 一个thresholds 数组
histogram.frequency(frequency) 值为true为统计数量;反之false则是统计频率
d3.random.normal() 用于生成正态分布,主要用于表述一个不明的随机变量 ,通常第一个参数用于表述平均值,第二个参数是标准差

数据转换与确定数据

normal 确定数据


normal 即 正态分布,主要用于表示出一个不明的随机变量,通常第一个参数用于表述出平均值,而第二个参数即代表标准差,(Standard Deviation)在概率统计上标准差定义是总体各单位的标准值与其平均数离差的算数平均数的平方根,可以反映出个体间的离散程度,通过下述 Code 你会感觉到每次输出的数据坐标,都是不一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
|_ 创建初始数据
|_|-- 通过 databases 来保存 rand 数据
|_|-- 根据 rand 生成平均值为170,差值为10,保存至 databases 中。

*/
var rand = d3.random.normal(170,50);
var databases = []

for(var i =0; i<100;i++) {
databases.push(rand())
}

console.log(databases)

转换数据

通过使用normal生成的随机数据是人类无法直接理解其意思的,因此需要通过使用其直方布局进行转换出人类可理解的数据含义,主要有三个变量,分别为x:区间的下限值、dx:区间的长度、y:此处的区间数量(frequency true)或频率(frequency false)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
|_ 转换数据
|_|-- 数据的分布范围 rangeMin / rangeMax
|_|-- 数据的分布区间 binNum
*/
var binding = {
binNum: 20,
rangeMin: 130,
rangeMax: 210
}

var histogram = d3.layout.histogram()
.range([binding.rangeMin,binding.rangeMax])
.bins(binding.binNum)
.frequency(true)

var hisData = histogram(databases)
console.log(hisData)

绘制

矩形直方图


通过使用 normal 以及 Histogram Layout计算出数据后,通过比例尺来创建x\y轴并绘制矩形,最后形成为矩形直方图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
var padding = {
width: 500,
height: 500,
top: 20,
right: 30,
bottom: 30,
left: 30
}

var svg = d3.select("body")
.append("svg")
.attr("width", padding.width)
.attr("height", padding.height)

/*
|_ 创建初始数据
|_|-- 通过 databases 来保存 rand 数据
|_|-- 根据 rand 生成100个随机数据,保存至 databases 中。

*/
var rand = d3.random.normal(170,50);
var databases = []

for(var i =0; i<100;i++) {
databases.push(rand())
}

console.log(databases)
/*
|_ 转换数据
|_|-- 数据的分布范围 rangeMin / rangeMax
|_|-- 数据的分布区间 binNum
*/
var binding = {
binNum: 20,
rangeMin: 130,
rangeMax: 210
}

var histogram = d3.layout.histogram()
.range([binding.rangeMin,binding.rangeMax])
.bins(binding.binNum)
.frequency(true)

var hisData = histogram(databases)
console.log(hisData)

/* 定义 x 轴比例尺 */
var xAxisWidth = 450,
xTicks = hisData.map( function (d) {
return d.x
})

var xScale = d3.scale.ordinal()
.domain(xTicks)
.rangeRoundBands([0, xAxisWidth],0.1);


/* 定义 y 轴比例尺 */
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([padding.height - padding.top - padding.bottom ,0])

/*
分别定义 x/y 轴比例尺
绘制比例尺。
*/
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickFormat(d3.format(".0f"))

svg.append("g")
.attr("class","axis")
.attr("transform", "translate(" + padding.left + "," + (padding.height - padding.bottom) + ")")
.call(xAxis)

var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.tickFormat(d3.format(".0f"))

svg.append("g")
.attr("class","axis")
.attr("transform", "translate("+ padding.left + "," + padding.top + ")")
.call(yAxis)

/* 绘制矩形 */
var gRect = svg.append("g")
.attr("transform", "translate(" + padding.left + "," + (- padding.bottom + -10) + ")" )
.style("opacity",1.0)

gRect.selectAll("rect")
.data(hisData)
.enter()
.append("rect")
.attr("class","rect")
.attr("x", function (d,i) {
return xScale(d.x)
})
.attr("y", function (d,i) {
return padding.height - yScale(d.y)
})
.attr("width", function (d,i) {
return xScale.rangeBand()
})
.attr("height", function (d) {
return yScale(d.y)
})

需要注意的是,我们需要为此添加CSS样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@color: #929292;
.axis path, /* 坐标轴路径设置 */
.axis line { /* 设置 坐标轴的线条 */
fill: none; /* 设置坐标轴宽度 */
stroke: @color; /* 坐标轴颜色 */
shape-rendering: crispEdges; /* 将形状渲染为清晰的边缘 */
}

.axis text {
font-family: sans-serif;
font-size: 11px;
fill: @color;
}

rect {
fill: @color;
}

曲线直方图


相比与矩形直方图( Rectangle Histogram)来比,曲线直方图(Curve Histogram)更加的的连贯,在下述的 code 中,主要将坐标轴以及比例尺进行倒序,之后通过line将其绘制路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
var padding = {
width: 500,
height: 500,
top: 20,
right: 30,
bottom: 30,
left: 30
}

var svg = d3.select("body")
.append("svg")
.attr("width", padding.width)
.attr("height", padding.height)

/*
|_ 创建初始数据
|_|-- 通过 databases 来保存 rand 数据
|_|-- 根据 rand 生成100个随机数据,保存至 databases 中。

*/
var rand = d3.random.normal(170,10);
var databases = []

for(var i =0; i<100;i++) {
databases.push(rand())
}

console.log(databases)
/*
|_ 转换数据
|_|-- 数据的分布范围 rangeMin / rangeMax
|_|-- 数据的分布区间 binNum
*/
var binding = {
binNum: 20,
rangeMin: 130,
rangeMax: 210
}

var histogram = d3.layout.histogram()
.range([binding.rangeMin,binding.rangeMax])
.bins(binding.binNum)
.frequency(true)

var hisData = histogram(databases)
console.log(hisData)

/* 定义 x 轴比例尺 */
var xAxisWidth = 450,
xTicks = hisData.map( function (d) {
return d.x
})

var xScale = d3.scale.ordinal()
.domain(xTicks)
.rangeRoundBands([0, xAxisWidth],0.1);


/* 定义 y 轴比例尺 */
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([0, yAxisWidth])

/* 绘制路径 */
var lineGenerator = d3.svg.line()
.x(function(d){ return xScale(d.x); })
.y(function(d){ return padding.height - yScale(d.y); })
.interpolate("basis");

var gLine = svg.append("g")
.attr("transform","translate(" + padding.left + "," + ( -padding.bottom ) + ")")

gLine.append("path")
.attr("fill","none")
.attr("stroke-width",1)
.attr("stroke","#929292")
.attr("d",lineGenerator(hisData));

/*
分别定义 x/y 轴比例尺
绘制比例尺。
*/
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")

yScale.range([yAxisWidth, 0])

var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")

svg.append("g")
.attr("class","axis")
.attr("transform", "translate(" + padding.left + "," + (padding.height - padding.bottom) + ")")
.call(xAxis)

svg.append("g")
.attr("class","axis")
.attr("transform", "translate(" + padding.left + "," + (padding.height - padding.bottom - yAxisWidth) + ")")
.call(yAxis)
⬅️ Go back