D3.js v4/v5 partition 使い方 – サンプルプログラム
D3の階層構造を可視化するpartition(d3.partition)のプログラムデモです。
サンプルデモ
サンプルプログラム
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 71 72 73 74 75 76 77 78 79 80 81 82 |
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>D3 v5 hierarchy partition v4/v5</title> </head> <body> <svg width="800" height="600"></svg> <script src="https://d3js.org/d3.v5.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; }) .sort(function(a, b) { return b.height - a.height || b.value - a.value; }); var partition = d3.partition() .size([height, width]) .padding(1) .round(true); partition(root); // 3. svg要素の配置 var g = d3.select("svg") .selectAll(".node") .data(root.descendants()) .enter() .append("g") .attr("class", "node") .attr("transform", function(d) { return "translate(" + d.y0 + "," + d.x0 + ")"; }); g.append("rect") .style("width", function(d) { return d.y1 - d.y0; }) .style("height", function(d) { return d.x1 - d.x0; }) .style("fill", function(d) { while(d.depth > 1) d = d.parent; if(d.depth == 0) return "gray"; return d3.schemeCategory10[parseInt(d.value % 7)]; }) .style("opacity", 0.6) g.append("text") .attr("text-anchor", "start") .attr("x", 5) .attr("dy", 30) .attr("font-size", "150%") .attr("class", "node-label") .text(function(d) { return d.data.name + " : " + d.value; }); </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 } ] }; |
描画用のデータを用意します。データ構造の詳細はこちらを参照ください。また、すべての末端ノードに、partitionの大きさを表示するためのvalue値を設定します。
2. 描画用のデータ変換
1 2 3 4 5 6 7 8 9 10 11 |
root = d3.hierarchy(data); root .sum(function(d) { return d.value; }) .sort(function(a, b) { return b.height - a.height || b.value - a.value; }); var partition= d3.partition() .size([height, width]) .padding(1) .round(true); partition(root); |
準備したデータを、描画用のデータ構造に変更します。準備したデータ→hierarchy用のデータ→描画種類ごと(今回はpartition)のデータと2段階の変換が必要です。
1 2 3 4 |
root = d3.hierarchy(data); root .sum(function(d) { return d.value; }) .sort(function(a, b) { return b.height - a.height || b.value - a.value; }); |
上記の関数でdataをhierarchy用のデータ構造rootに変更した後(データ構造詳細はこちらを参照)、value値を設定していないノードに、子孫ノードの合計value値を計算します。さらにpartitionの上側に一番合計値が大きいカテゴリーが来るようにソートしています。ソートしなければ指定したデータ構造に基づいてpartitionが作成されます。
次に、partition用のデータに変更するための関数を呼び出します。
1 2 3 4 5 6 |
var partition = d3.partition() .size([height, width]) .padding(1) .round(true); partition(root); |
d3.partition()で呼び出した関数にrootを引数として設定すると、次のデータがrootに付与されます。ここで、paritionはxが兄弟の並び方向、yが親、孫の並び方向になるため注意が必要です。
x0 | 四角形の左端座標 |
y0 | 四角形の上端座標 |
x1 | 四角形の右端座標 |
y1 | 四角形の下端座標 |
また、d3.parition()には以下の設定が可能です。
parition.size() | treemapレイアウト全体のサイズを[並び方向幅, 深さ方向幅]の2要素配列で設定します。引数が設定されていない場合は現在のサイズを返します。デフォルトは[1, 1]。 |
parition.round() | 計算の丸め(四捨五入)を許容するかを指定します。trueもしくはfalseで設定します。デフォルトはfalseです。 |
parition.padding() | 四角形の隙間の幅を設定します。値が設定されていない場合は現在の値を返します。 |
3 svg要素の配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var g = d3.select("svg") .selectAll(".node") .data(root.descendants()) .enter() .append("g") .attr("class", "node") .attr("transform", function(d) { return "translate(" + d.y0 + "," + (d.x0) + ")"; }); g.append("rect") .style("width", function(d) { return d.y1 - d.y0; }) .style("height", function(d) { return d.x1 - d.x0; }) .style("fill", function(d) { while(d.depth > 1) d = d.parent; if(d.depth == 0) return "gray"; return d3.schemeCategory10[parseInt(d.value % 7)]; }) .style("opacity", 0.6) g.append("text") .attr("text-anchor", "start") .attr("x", 5) .attr("dy", 30) .attr("font-size", "150%") .text(function(d) { return d.data.name + " : " + d.value; }); |
svg要素に”g”タグを設定した後、その中に”rect”と”text”を設定していきます。
データの割り当て部分で
1 |
root.descendants() |
の関数を使っていますが、これは入れ子になったrootからすべてのノードを配列として並べてくれる関数です。
また、四角形の背景色として、
1 2 3 4 5 |
.style("fill", function(d) { while(d.depth > 1) d = d.parent; if(d.depth == 0) return "gray"; return d3.schemeCategory10[parseInt(d.value % 7)]; }) |
のように色を割り当てています。まず、2行目の部分で自分が属するカテゴリーを探索します。d.depth=1は二番目の深さを意味し、d.depth=0は先頭のノードを意味します。今回は先頭のノードのみ”gray”を与えています。d3.schemeCategory10はD3.js v5から追加された配列で0~9の値を与えると、適当な色を返してくれます。今回は2番目の深さの親のvalue値に応じて色を変更する設定にしました。v4以前であれば、下のような関数を作って呼び出してもよいです。
1 |
color = ["red", "blue", ...]; |
まとめ
treemapとほぼ同じプログラムで描画できます。カテゴリーの重要度と構造を同時に表示することができるグラフです。