【 D3.js 高級系列 — 3.0 】 堆棧圖

堆棧圖佈局(Stack Layout)可以計算二維數組每一數據層的基線,以方便將各數據層疊加起來。本文講解堆棧圖的製做方法。javascript

301

先說說什麼是堆棧圖。html

例如,有以下狀況:java

某公司,銷售三種產品:我的電腦、智能手機、軟件。數組

2005年,三種產品的利潤分別爲3000、2000、1100萬。app

2006年,三種產品的利潤分別爲1300、4000、1700萬。dom

計算可得,2005年總利潤爲6100萬,2006年爲7000萬。svg

若是要將2005年的利潤用柱形表示,那麼應該畫三個矩形,三個矩形堆疊在一塊兒。這時候就有一個問題:每個矩形的起始y座標是多少,高應該是多少。wordpress

輸入數組,直接計算出上述問題的,就是堆棧圖佈局。佈局


1. 數據

某公司銷售我的電腦、智能手機、軟件的數據以下:.net

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 }] }
    ];

dataset是一個數組,數組的每一項是一個對象,對象裏含有name和sales。name是產品名、sales是銷售狀況。sales也是一個數組,每一項也是對象,對象裏包含有year表示年份、profit表示利潤。

現要將此數據繪製成堆棧圖。


2. 佈局(數據轉換)

首先建立堆棧圖佈局。

var stack = d3.layout.stack()
					.values(function(d){ return d.sales; })
					.x(function(d){ return d.year; })
					.y(function(d){ return d.profit; });

values訪問器指定的是d.sales,表示接下來接收的數組,要計算的數據在數組每一項的變量sales中。x訪問器指定的是d.year,y訪問器指定的是d.profit,都是相對於values訪問器指定的對象說的,即sales數組每一項的變量year和profit。

以dataset爲stack參數,結果保存在data中:

	var data = stack(dataset);
	console.log(data);

要注意,轉換以後原數據也會改變,所以dataset和data的值是同樣的。data的輸出值如圖1所示。能夠看到,sales的每一項都多了兩個值:y0和y。y0即該層起始座標,y是高度。x座標有就是year。

302

圖1


3. 繪製

首先要建立x軸和y軸比例尺,在添加圖形元素和座標軸的時候都要用到。要繪製座標軸,要馬上想到要給座標軸的刻度留出一部分空白。先定義一個外邊框對象。

var padding = { left:50, right:100, top:30, bottom:30 };

右邊部分留出的空白較多,是爲了在後面添加標籤的。x軸比例尺的定義以下:

var xRangeWidth = width - padding.left - padding.right;
		
	var xScale = d3.scale.ordinal()
					.domain( data[0].sales.map(function(d){ 
return d.year; 
}))
					.rangeBands([0, xRangeWidth],0.3);

本例中x軸表明年份,2005年、2006年、2007年等,是離散的,也就是說比例尺的定義域是離散的。從第5章的內容可知,序數比例尺d3.scale.ordinal的定義域是離散的。上面代碼將定義域設定成:

[2005, 2006, 2007, 2008, 2009]

值域是根據rangeBands()計算的,實際是:

[31, 134, 238, 342, 446]		//(省略了小數點)

所以,在2005年處堆疊的矩形的x座標爲31。

而後再建立y軸的比例尺。

        //最大利潤(定義域的最大值)
	var maxProfit = d3.max(data[data.length-1].sales, function(d){ 
							return d.y0 + d.y; 
					});
	
	//最大高度(值域的最大值)
	var yRangeWidth = height - padding.top - padding.bottom;
	
	var yScale = d3.scale.linear()
					.domain([0, maxProfit])		//定義域
					.range([0, yRangeWidth]);		//值域

這段代碼中,求最大利潤時,是對data數組中的最後一項data[2]求取sales的最大值。這是由於data[2]表明着最高的層,如圖2所示,data[2].sales中的各項y0+y,一定比data[1]和data[0]的大。所以,只要用d3.max()求取data[2].sales中的最大值便可。值域是SVG的高度減去外邊框的上下寬度。而後爲d3.scale.linear()設定定義域和值域便可。

303

圖2

有了比例尺後,須要添加足夠數量的分組元素<g>,每個分組表明一種產品,每一種產品都用一種顏色來標識。

        //顏色比例尺
	var color = d3.scale.category10();
	
	//添加分組元素
	var groups = svg.selectAll("g")
					.data(data)
					.enter()
					.append("g")
					.style("fill",function(d,i){ return color(i); });

如今添加了3個分組,分別表明PC、SmartPhone、Software,且每個分組元素的fill都設定了顏色。接下來爲每一個分組添加矩形元素。

        //添加矩形
	var rects = groups.selectAll("rect")
					.data(function(d){ return d.sales; })
					.enter()
					.append("rect")
					.attr("x",function(d){ return xScale(d.year); })
					.attr("y",function(d){ 
return yRangeWidth - yScale( d.y0 + d.y ); 
})
					.attr("width",function(d){ 
return xScale.rangeBand(); 
})
					.attr("height",function(d){ return yScale(d.y); })
					.attr("transform","translate(" + padding.left + "," 
   + padding.top + ")");

每個分組元素裏還要綁定數組sales,以添加足夠數量(每一個分組5個)的矩形。而後再使用比例尺爲矩形的x、y、width、height屬性賦值。再添加上座標軸後,結果如圖3所示。

304

圖3

可是,什麼顏色表明什麼產品從圖裏看不出來。解決此問題最經常使用的方法是,在圖表旁邊添加幾個圖形標誌,旁邊加上文字,告訴用戶某種顏色對應的是什麼。在分組裏繼續添加圖形元素,代碼以下。

        var labHeight = 50;
	var labRadius = 10;
	
	var labelCircle = groups.append("circle")
							.attr("cx",function(d){ 
return width - padding.right*0.98; 
})
							.attr("cy",function(d,i){ 
return padding.top * 2 + labHeight * i; 
})
							.attr("r",labRadius);
					
	var labelText = groups.append("text")
						.attr("x",function(d){ 
return width - padding.right*0.8; 
})
						.attr("y",function(d,i){ 
return padding.top * 2 + labHeight * i; 
})
						.attr("dy",labRadius/2)
						.text(function(d){ return d.name; });

用圓和文字做爲標籤,添加到圖表的右邊。最終結果如圖4所示。

301

圖4

完整代碼請點擊如下網址,再右鍵查看源代碼:

http://www.ourd3js.com/demo/G-3.0/stack.html

謝謝閱讀。

文檔信息

相關文章
相關標籤/搜索