D3.js レイアウト – d3.layout.packでパックチャート

Share

パックチャートはツリーチャートと同系統のものです。ツリーの場合は親から子に枝分かれするのに対して、パックでは親の円が子の円を包み込みます。
だから、使えるデータ構造やコードもツリーとかなり似ています。d3.layout.treeと違うのは、d3.layout.packでは円を作るためのデータが出ることです。これでデータを変換すると、データの親子関係と値に合わせて、円の中心のx,yと円の半径が出ます。

使うデータの構造は下のようになります。”name”は各円の名前、”種類”にはその子となる円のリストが入っています。一番下の子には”好き度”という値が入っています。これを使って円の半径を変えることができます。

{
 "name": "明治",
 "種類": [
  {
   "name": "チョコ菓子シリーズ",
   "種類": [
    {
     "name": "きのこの山",
     "種類": [
      {"name": "味わいミント", "好き度": 10},
 	 {"name": "黒糖きなこ", "好き度": 5},
      {"name": "チョコバナナ", "好き度": 15}
     ]
    },
    {
     "name": "たけのこの里",
     "種類": [
      {"name": "マカダミアクッキー", "好き度": 2},
      {"name": "焼き栗", "好き度": 10}
     ]
    },
	{
	"name": "すぎのこ村", "好き度": 20
	}
   ]
  }
 ]
}

パックの設定は下のようなコードになります。

  • size()で全体のタテ・ヨコの大きさを指定します。
  • value()で半径の大きさを決めるためのキー名を指定します。この場合は”好き度”という名前です。実際にはそれを指定するための関数をここに入れます。
  • children()で子を入れるリストのキー名を指定します。この場合は”種類”という名前です。ここでも指定するための関数を入れます。
  • padding()で円同士の間の余白を決めます。
var pack = d3.layout.pack()
.size([width, height - 100])
.value(value) // 値の名前を指定。
.children(children) // 入れ子の名前を指定。
.padding(10); // 円どおしの余白設定。

function value(d) {
  return d["好き度"];
}

function children(d) {
  return d["種類"];
}

データから円の情報に変換するには、pack.nodes()の中にデータを入れます。
そうすると、親子に合わせて円の位置x,yと半径rが自動的に計算されて出ます。

var nodes = pack.nodes(data); // x,yと、半径rが計算されて出る。

実際にパックを作る時は、このデータを使ってcircleを作ることになります。

では具体的な例です。

 

パックチャートを作る:

結果:
d3レイアウト-packlayout1

コード:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
</head>
<body>

	<script src="http://d3js.org/d3.v3.min.js"></script>

	<script>
	var width = 500;
	var height = 500;

	var svg = d3.select("body").append("svg")
	.attr("width", width)
	.attr("height", height)
	.attr("transform","translate(50,50)");

	var color10 = d3.scale.category10();

	var pack = d3.layout.pack()
	.size([width, height - 100])
	.value(value) // 値の名前を指定。
	.children(children) // 入れ子の名前を指定。
	.padding(10); // 円どおしの余白設定。

	function value(d) {
	  return d["好き度"];
	}

	function children(d) {
	  return d["種類"];
	}

	d3.json("kinoko_takenoko.json", function(data) {

	var nodes = pack.nodes(data); // x,yと、半径rが計算されて出る。

	var node = svg.selectAll(".node")
	.data(nodes)
	.enter()
	.append("g")
	.attr("class","node")
	.attr("transform", function(d){ return "translate("+ d.y + "," + d.x + ")";}); // gをそれぞれのx,yに移動。 

	node.append("circle")
	.attr("r", function(d){ return d.r}) // packで作られたrを使う。
	.attr("fill",function(d){ return color10(d.depth); })
	.attr("stroke", "orange")
	.attr("stroke-width",2)
	.attr("opacity", 0.25); // 重なった円が見えるように透明度をつける。

	node.append("text")
	.attr("text-anchor","middle")
	//.text(function(d) { return d.children? "" : d.name; }); //一番下の子の名しか表示しない。
	.text(function(d){ return d.depth == 0 ? "" : d.name; }) // 一番上の親の名前は表示しない。
	.attr("stroke", function(d){
		return d.depth==1 ? "darkgray" : d.depth==2 ? "gray" : "black";
	 }); // 子、孫になるごとに色が黒くする。

	});

	</script>

</body>
</html>

解説:
親の円が子を含んでいくチャートができました。今回はさらに、階層の深さにしたがって円の色と文字の色も変化させています。

パックの設定とデータ変換は上で紹介したのとほぼ同じです。

変換したデータはまず、コンテナとしてのg要素を作るのに使っています。

var node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class","node")
.attr("transform", function(d){ return "translate("+ d.y + "," + d.x + ")";}); // gをそれぞれのx,yに移動。

次にg要素にcircle要素を追加しています。
半径rには変換したデータをd.rとして使っています。
さらに階層の深さによって円の色を変えるために、d.depthでデータの親子関係の深さを出しています。一番上の親なら0で子になるほど数字が上がっていきます。数字の違いで色を変えるために、他の場所で作ったcolor10()を使っています。

node.append("circle")
.attr("r", function(d){ return d.r}) // packで作られたrを使う。
.attr("fill",function(d){ return color10(d.depth); })
.attr("stroke", "orange")
.attr("stroke-width",2)
.attr("opacity", 0.25); // 重なった円が見えるように透明度をつける。

次にg要素にテキストを追加しています。
さらに”text-anchor”の”middle”で、テキストを中央揃えにしています。
テキストの内容は、一番上の親の名前=”明治”だけは表示しないで、それ以下の子は表示するようにしています。そのためにd.depth==0かどうかの条件分岐を使っています。
テキストの色は、親子の階層の違いで分けています。これもd.depthの値の違いで条件分岐しています。

node.append("text")
.attr("text-anchor","middle")
.text(function(d){ return d.depth == 0 ? "" : d.name; }) // 一番上の親の名前は表示しない。
.attr("stroke", function(d){
	return d.depth==1 ? "darkgray" : d.depth==2 ? "gray" : "black";
 }); // 子、孫になるごとに色が黒くする。

コメントを残す