福岡拠点の野田です。今日は、データ解析に欠かせない可視化を支援するツールとして、D3.jsの使い方を紹介します。
可視化をするとき、棒グラフだったり、線グラフを出力することが多いと思います。Python、Rなどを使った画像出力ももちろん簡単に実現できるのですが、凝った複雑なグラフを出そうとするとき、D3.jsで描くSVGの柔軟性には目を見張るところがあります。SVGは、Scalable Vector Graphicsの略でベクター情報のイメージデータになります。そのため、拡大しても非常に高精細に表示することができます。また、CSSやJavasciptでイベント制御も可能なため、凝った動きのあるグラフを実現できます。
D3.jsの最新版は、ver5.1.0(2018/05/02時点)になります。ver3.xからver4.xにアップデートした際に大きく変更があったため、今回は個人的にもなじみのあるver3.xでの使い方を紹介します。
使うためには、HTMLヘッダーに以下を組み込むだけ。とても簡単です。
ver3.x
1 |
<script src="https://d3js.org/d3.v3.min.js"></script> |
ちなみにver5.xでは以下のような感じです。
1 |
<script src="https://d3js.org/d3.v5.min.js"></script> |
ここからは、ver3.xでの記述例になります。
以下のような流れでグラフを表示します。
1) 領域確保
グラフを表示する領域を確保します。軸に値を表示するため、マージンを確保します。
1 2 3 4 5 6 7 8 |
var margin = {top: 30, right: 100, bottom: 40, left: 50}; // 領域確保 var svg = d3.select("body").append("svg") .attr("width", (width + margin.left + margin.right)) .attr("height", (height + margin.top + margin.bottom)) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
2) スケール設定
値の描画変換を行うスケール(基準となる物差し)を指定します。のちにデータ描画でも使います。
1 2 3 4 5 6 7 8 |
// スケール設定 var xScale = d3.time.scale() //時系列の場合、d3.time.scale()を使用 .domain([new Date(minX), new Date(maxX)]) .range([0, width]); var yScale = d3.scale.linear() .domain([minY, maxY]) .range([height, 0]); |
3) 軸の描画
スケールをもとに軸を描画します。
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 |
// X軸設定 var xAxis = d3.svg.axis() .ticks(24) // 区切りの数 .scale(xScale) // X-スケール .orient("bottom") // 軸の表示位置 .tickSize(6, -height) // 区切り線のサイズ .tickFormat(function(d,i){ // 区切りの値 // 1年以内の場合 // var fmtFunc = d3.time.format("%Y/%m"); // 1か月以内の場合 var fmtFunc = d3.time.format("%m/%d"); // 1日以内の場合 // var fmtFunc = d3.time.format("%H:%M"); return fmtFunc(d); }); // X軸を描画。 svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis) .append("text") .attr("y", 30) .attr("x", width + 10); // Y軸設定 var yAxis = d3.svg.axis() .ticks(16) // 区切りの数 .scale(yScale) // Y-スケール .orient("left") // 表示位置 .tickSize(6, -width); // 区切り線のサイズ // y軸を描画。 svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("y", -10) .attr("x", -10) .style("text-anchor", "end") // .text("Y軸タイトル") ; |
4) 補助線の描画
必須ではないですが、指定することでぐっとクオリティがアップします。
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 |
// ライン関数を定義 var line = d3.svg.line() .x(function(d) {return xScale(new Date(d["x"]));}) .y(function(d) {return yScale(d["y"]);}); // X軸の基準線を作成 var baseLine = [ [{x: minX, y:0},{x: maxX, y:0}], ]; for (xi = 0; xi < baseLine.length; xi++) { var baseRecord = baseLine[xi]; svg.append('path') .attr({ 'd': line(baseRecord), 'stroke': '#999', 'stroke-width': 1, }); } // Y軸のtickからX軸に平行な区切り線を描画 var diff = (maxY - minY) / (svg.selectAll("g.y g.tick")[0].length - 1); for (yi = minY + diff; yi < maxY; yi = yi + diff) { var guideRecord = [ {x: minX, y: yi}, {x: maxX, y: yi} ]; svg.append('path') .attr({ 'd': line(guideRecord), 'stroke': '#aaa', 'stroke-width': 0.2, }); } |
5) データ値の描画
線グラフだったり、点グラフだったり、棒グラフなどデータを描画します。今回は、線グラフを扱います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
d3.csv(csvUrl, function(data){ // 線を描画 var trackWidth = 0.5; var strokeColor = 'none'; var strokeWidth = 1; var color = '#666'; var line = d3.svg.line() .x(function(d) { return xScale(new Date(d[targetDate])); }) .y(function(d) { return yScale(d[target]); }) .defined(function(d) { return d[target]!=null; }) .interpolate("linear"); // 線の形 svg.append("path") .attr("d",line(data)) .attr("fill", strokeColor) .attr("stroke", color) .attr("stroke-width", strokeWidth); }); |
これでCSVを読み込むと以下のようなグラフを表示することができるようになります。
SVGは、HTMLと同じくXMLで記述されるDOM(文書オブジェクトモデル)の1種です。イベント処理も差し込むことができるため、HTMLと同様な感覚で動的処理を扱えるのは強みと思います。D3.js自体、jQueryなどと同様、DOM操作するための言語とも言えます。慣れが若干必要ですが、凝ったグラフの描けるは大きな魅力です。
これを機会にぜひ触れてみてください。
今回一部変数を省略していますが、以下のような値を設定しています。