d3.layout.stack()
什么是堆栈图。
例如,有如下情况:
某公司,销售三种产品:个人电脑、智能手机、软件。
2005年,三种产品的利润分别为3000、2000、1100万。
2006年,三种产品的利润分别为1300、4000、1700万。
计算可得,2005年总利润为6100万,2006年为7000万。
如果要将2005年的总利润用柱形表示,那么应该画三个矩形,三个矩形堆叠在一起。这时候就有一个问题:每一个矩形的起始y坐标是多少?高应该是多少?
堆栈图布局(Stack Layout)能够计算二维数组每一数据层的基线,以方便将各数据层叠加起来,最适合用来处理以上这种场景。
1、数据
var dataset = [
{
name: "PC" ,
sales: [
{ year:2005, profit: 3000 },
{ year:2006, profit: 1300 },
{ year:2007, profit: 3700 },
{ year:2008, profit: 4900 },
{ year:2009, profit: 700 }
]
},
{
name: "SmartPhone" ,
sales: [
{ year:2005, profit: 2000 },
{ year:2006, profit: 4000 },
{ year:2007, profit: 1810 },
{ year:2008, profit: 6540 },
{ year:2009, profit: 2820 }
]
},
{
name: "Software" ,
sales: [
{ year:2005, profit: 1100 },
{ year:2006, profit: 1700 },
{ year:2007, profit: 1680 },
{ year:2008, profit: 4000 },
{ year:2009, profit: 4900 }
]
}
];
2、数据转换
var stack = d3.layout.stack()
.values(function(d){ return d.sales; })
.x(function(d){ return d.year; })
.y(function(d){ return d.profit; });
var data = stack(dataset);
values方法指定需要转换的数据集;
x方法指定数据集中X轴的字段;
y方法指定数据集中Y轴的字段;
转换后的数据如下:
如图,sales的每一项都多了两个值:y0和y。y0即该层起始坐标,y是高度。x坐标有就是year,这些坐标都是在左上角为起点计算的,这点要注意。
3、绘制图形
生成SVG容器
var width = 700;
var height = 500;
var svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height);
生成图表容器
var padding = {
top: 50,
right: 100,
bottom: 50,
left: 50
};
var charts = svg.append('g')
.attr('transform', 'translate(' + padding.left + ',' + padding.top + ')');
生成颜色比例尺
var colors = d3.scale.category10();
生成X轴和Y轴比例尺
var maxProfit = d3.max(data[data.length - 1].sales, function(d) {
return d.y0 + d.y;
});
var xScale = d3.scale.ordinal().domain([2005, 2006, 2007, 2008, 2009]).rangeBands([0, width - padding.left - padding.right], 0.3);
var yScale = d3.scale.linear().domain([0, maxProfit]).range([0, height - padding.top - padding.bottom]);
为每种类型数据创建容器
var stack = charts.selectAll('.stakc')
.data(data)
.enter()
.append('g')
.classed('stack', true)
.attr('fill', function(d, i) {
return colors(i);
})
为每种类型数据创建矩形图
var rect = stack.selectAll('rect')
.data(function(d) {
return d.sales;
})
.enter()
.append('rect')
.attr('x', function(d) {
return xScale(d.year);
})
.attr('y', function(d) {
return height - padding.top - padding.bottom - yScale(d.y0 + d.y);
})
.attr('width', xScale.rangeBand())
.attr('height', function(d) {
return yScale(d.y);
})
生成坐标轴
var xAxis = d3.svg.axis().scale(xScale);
var yAxis = d3.svg.axis().scale(yScale.range([height - padding.top - padding.bottom, 0])).orient('left');
charts.append('g')
.classed('x axis', true)
.attr('transform', 'translate(0,' + (height - padding.top - padding.bottom) + ')')
.call(xAxis)
charts.append('g')
.classed('y axis', true)
.call(yAxis)
生成数据类型图示
stack.append('circle')
.attr('cx', function(d) {
return width - padding.left - padding.right * 0.9
})
.attr('cy', function(d, i) {
return i * 50
})
.attr('r', 5)
stack.append('text')
.attr('x', function(d) {
return width - padding.left - padding.right * 0.8
})
.attr('y', function(d, i) {
return i * 50
})
.attr('dy', 5)
.text(function(d) {
return d.name;
})