1. 程式人生 > >大資料視覺化第三天——D3.js初探:餅形圖

大資料視覺化第三天——D3.js初探:餅形圖

在找了許多參考資料後,我發現大部分關於D3.js的講解不滿足我的需求,有些講的太過冗長,舉了很多不需要的點,比如用D3來做排序過濾這種分析階段就應該做完的事;有些又太簡單,很多省略的細節讓人看的似是而非。這篇我在網上找了一個餅狀圖的小例子來幫助自己理解D3一般圖形繪製過程,在程式碼中涉及到某個知識點時再有針對性的學習。

首先來定義四個類別,每個類別中有不同數目的樣本。

	var dataset = [{
			label: "A",
			count: 10
		},
		{
			label: "B",
			count: 20
		},
		{
			label: "C",
			count: 30
		},
		{
			label: "D",
			count: 40
		}
	];
然後定義幾個常量,主要是繪製時使用。

	var width = 360;
	var height = 360;
	var radius = Math.min(width, height) / 2;
	var color = d3.scaleOrdinal(d3.schemeCategory20);

其中寬、高是svg的屬性,radiu是圓的半徑。這裡因為width和heigtht是一樣的,所以不用Math.min也沒關係。color這裡構建了一個顏色的標度,d3.scaleOrdinal()函式對

d3.schemeCategory20中的20個顏色構建了一個有序的顏色列表。

	var svg = d3.select("#chart")
		.append('svg')
		.attr('width',width)
		.attr('height',height)
		.append('g')
		.attr('transform', 'translate(' + (width / 2) +  ',' + (height / 2) + ')');

這裡首先用d3提供的select方式選中了以ID"chart"命名的標籤(這是我們預先定義的一個<div>標籤)。

d3提供了兩種select方法,select()和selectAll(),前面一種方法返回匹配這個選擇器的第一個元素,後面一種方法返回匹配這個選擇器的所有元素,前面加#代表了選擇ID,加.代表了選擇類。

然後在被選中的<div>標籤中,通過.append()方法增加了一個svg區域用來繪製圖形,隨後的兩個.attr()給這個svg區域加入了寬高的屬性。這裡有一個連續呼叫函式的方法成為“連綴語法”。在設定完svg區域的屬性後,又在svg區域中加入了一個<g>標籤,用來劃定一個區域,設定了這個區域的transform屬性,屬性的值為translate(180,180),作用是把<g>標籤所劃定的區域先向右,再向左分別移動180的位置。

	var arc = d3.arc()
  		.innerRadius(0)
  		.outerRadius(radius);
  	var pie = d3.pie()
  		.value(function(d) { return d.count; })
  		.sort(null);
上面這段是定義了d3中的兩個圖形,一個是圓形arc(),一個是餅圖pie(),這兩個函式後面所append()的是一些針對這個圖形的特殊設定函式,現在不必太過深究,到使用的時候再查文件也可以。這裡簡單介紹一下,innerRadius()和outerRadius()設定了圓環的內徑和外徑,假如innerRadius()裡面引數不為0,將畫出一個圓環;d3.pie().value()則通過接受一組資料來返回餅狀圖幾條線的角度,並且不進行排序

  	var path = svg.selectAll('path')
  		.data(pie(dataset))
  		.enter()
  		.append('path')
  		.attr('d',arc)
  		.attr('fill',function(d,i){
 			return color(d.data.label);
  		})

上面這段是D3的核心,主要做了下面幾件事,按行來講解:

  1. 選擇了SVG中的所有<path>標籤,但是我們之前並沒有在<svg>區域中設定<path>標籤,所以將返回一個空值,但是隨後將通過enter()建立這些<path>
  2. 我們將資料通過data()函式繫結到<path>上,這裡涉及到D3的資料繫結機制,有datum()和data()兩種方法,前者將被選中的所有物件都繫結同一個值,後者則按照被選中物件與data()中引數資料的數量對比進行不同處理:如果兩者相等,直接執行後面的語句,也稱作update();如果元素不夠,比如現在的情況,將一直增加元素到與資料匹配為止,也稱作enter(),每次enter增加一個標籤然後執行update()的操作,所以在我們的例子中,將執行四次enter(),如果元素多餘,則刪除元素,也叫exit().
  3. 每次enter()會執行append('path')也就是增加一個<path>標籤
  4. 然後增加一個path的屬性d,這是SVG作圖中一個相當複雜的屬性,包含了許多svg的移動劃線等操作,其屬性值為d3的arc()函式所返回的path data.
  5. 然後每次enter()的過程fill一個不同的顏色,這個function(d,i)可能有些費解,這是由d3.js提供的變數,分別代表了數值和數值的索引。
上面整個過程確實比較複雜,不過.selectAll(ele).data().enter().append(ele).update()是一個d3中操作圖表的常見流程,選擇元素標籤——繫結資料——執行enter()過程——增加元素標籤——執行後續操作。

效果圖如下:


全部程式碼如下:

<div id="chart">
</div>

<script src="d3/d3.js"></script>
<script>
	var dataset = [{
			label: "A",
			count: 10
		},
		{
			label: "B",
			count: 20
		},
		{
			label: "C",
			count: 30
		},
		{
			label: "D",
			count: 40
		}
	];

	var width = 360;
	var height = 360;
	var radius = Math.min(width, height) / 2;
	var color = d3.scaleOrdinal(d3.schemeCategory20);
	
	var svg = d3.select("#chart")
				.append('svg')
				.attr('width',width)
				.attr('height',height)
				.append('g')
				.attr('transform', 'translate(' + (width / 2) +  ',' + (height / 2) + ')');
	var arc = d3.arc()
  				.innerRadius(0)
  				.outerRadius(radius);
  	var pie = d3.pie()
  				.value(function(d) { return d.count; })
  				.sort(null);
  	var path = svg.selectAll('path')
  				.data(pie(dataset))
  				.enter()
  				.append('path')
  				.attr('d',arc)
  				.attr('fill',function(d,i){
  					return color(d.data.label);
  				})
  				
</script>