D3.js v4/v5 pack 使い方
D3の階層構造を可視化するpack(d3.hieararchy)のプログラムデモです(v4/v5対応)。
サンプルデモ
サンプルプログラム
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 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>D3 v5 hierarchy pack v4/v5</title> </head> <body> <svg width="800" height="600"></svg> <script src="https://d3js.org/d3.v4.min.js"></script> <script> // 1. 描画用のデータ準備 var width = document.querySelector("svg").clientWidth; var height = document.querySelector("svg").clientHeight; var data = { "name": "A", "children": [ { "name": "B", "value": 25 }, { "name": "C", "children": [ { "name": "D", "value": 10 }, { "name": "E", "value": 15 }, { "name": "F", "value": 10 } ] }, { "name": "G", "value": 15 }, { "name": "H", "children": [ { "name": "I", "value": 20 }, { "name": "J", "value": 10 } ] }, { "name": "K", "value": 10 } ] }; // 2. 描画用のデータ変換 root = d3.hierarchy(data); root.sum(function(d) { return d.value; }); var pack = d3.pack() .size([width, height]) .padding(0); pack(root); // 3. svg要素の配置 var node = d3.select("svg").selectAll(".node") .data(root.descendants()) .enter() .append("g") .attr("transform", function(d) { return "translate(" + d.x + "," + (d.y) + ")"; }); var color = ["orange", "Khaki", "Ivory"]; node.append("circle") .attr("r", function(d) { return d.r; }) .attr("stroke", "black") .attr("fill", function(d) { return color[d.depth]; }); node.append("text") .style("text-anchor", function(d) { return d.children ? "end" : "middle"; }) .attr("font-size", "150%") .text(function(d) { return d.children ? "" : d.data.name; }); </script> </body> </html> |
解説
1. 描画用データの準備
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
var data = { "name": "A", "children": [ { "name": "B", "value": 25 }, { "name": "C", "children": [ {"name": "D", "value": 10 }, { "name": "E", "value": 15 }, { "name": "F", "value": 10 } ] }, { "name": "G", "value": 15 }, { "name": "H", "children": [ { "name": "I", "value": 20 }, { "name": "J", "value": 10 } ] }, { "name": "K", "value": 10 } ] }; |
描画用のデータを用意します。データ構造の詳細はこちらを参照ください。また、すべての末端ノードに、バルーンの大きさを表示するためのvalue値を設定します。親ノードのサイズは子、孫ノードのvalue値の合計値を次のデータ変換時に設定します。
2. 描画用のデータ変換
1 2 3 4 5 6 7 8 |
root = d3.hierarchy(data); root.sum(function(d) { return d.value; }); var pack = d3.pack() .size([width, height]) .padding(0); pack(root); |
準備したデータを、描画用のデータ構造に変更します。準備したデータ→hierarchy用のデータ→描画種類ごと(今回はpack)のデータと2段階の変換が必要です。
1 2 |
root = d3.hierarchy(data); root.sum(function(d) { return d.value; }); |
上記の関数でdataをhierarchy用のデータ構造rootに変更した後(データ構造詳細はこちらを参照)、value値を設定していないノードに、子孫ノードの合計value値を計算します。
次に、pack用のデータに変更するための関数を呼び出します。
1 2 3 4 5 |
var pack = d3.pack() .size([width, height]) .padding(0); pack(root); |
d3.pack()で呼び出した関数にrootを引数として設定すると、次のデータがrootに付与されます。
x | サークルの中心x座標 |
y | サークルの中心y座標 |
r | サークルの半径 |
また、d3.pack()には以下の設定が可能です。
pack.radius() | サークルの半径を引数にfunction(d){return d.value;}などとした関数で指定します。デフォルトはnullです。 |
pack.size() | packレイアウト全体のサイズを[幅, 高さ]の2要素配列で設定します。引数が設定されていない場合は現在のサイズを返します。デフォルトは[1, 1]。 |
pack.padding() | サークルの接線間の距離を指定します。デフォルトは0です。 |
3. svg要素の配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// 3. svg要素の配置 var node = d3.select("svg").selectAll(".node") .data(root.descendants()) .enter() .append("g") .attr("transform", function(d) { return "translate(" + d.x + "," + (d.y) + ")"; }); var color = ["orange", "Khaki", "Ivory"]; node.append("circle") .attr("r", function(d) { return d.r; }) .attr("stroke", "black") .attr("fill", function(d) { return color[d.depth]; }); node.append("text") .style("text-anchor", function(d) { return d.children ? "end" : "middle"; }) .attr("font-size", "150%") .text(function(d) { return d.children ? "" : d.data.name; }); |
サークル用のsvg要素を設定します。はじめに”g”要素を設定し、その中に”circle”と”text”を設定していきます。
データの割り当ての部分で
1 |
root.descendants() |
の関数を使っていますが、これは入れ子になったノードを配列として並べてくれる関数です。
“circle”では初めにカラー配列を設定し、”fill”の属性として深さ方向の位置に応じた色を設定しています。また、”text”で設定している”text-anchor”はtextの位置を設定するスタイルです。
まとめ
あまりお目にかからないチャートですが、インパクトのある階層構造の描画に用いることができます。末端要素の大きさ(半径)以外は数値で規定されないため、使い方には注意が必要です。