javascriptのライブラリ「D3.js」について調べてみました。
■ 公式
D3.js - Data-Driven Documents
■ 日本語訳
D3.js - 日本語ドキュメント
D3(Data Driven Document).jsとは
D3.jsとは、JSON、XML、CSV、タブ区切りテキスト、javascriptの配列などの
様々な形式のデータを可視化する時に便利なライブラリで、下記のような特徴があります。
・データに基づいてドキュメントを操作する。 =データドリブン(データ駆動)
・データの表現に必要な変換は行うが、表現そのものは直接行わない。
⇒表現は制作者に委ねられていますが、そのために複雑な表現が可能になっています。
⇒表現には主にSVGを使用します。
・モダンブラウザでの動作を前提としている。
⇒標準では古いブラウザはサポートしない。(IEは9以上)
・jQueryと同じようなチェーン構文を使用している。
・・・
D3.jsは本体ファイルが読み込まれていれば使用可能です。
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
サンプルの作成
今回は、D3.jsについて調べながら、
「1週間分の気温の推移を表すグラフ」を作成しました。
大きく分けると下記の処理を順番に行っています。
- 使用するデータの設定
- グラフを表示するSVG要素の作成
- 軸(目盛り)の作成
- 折れ線グラフの作成
- 散布図の作成
- テキストで気温の値を追加
- 都市名をクリックした時のグラフの書き換え
1. 使用するデータの設定
サンプルでは下記のように、各都市の「日付」「最高気温」「最低気温」を配列にして指定しています。
APIなどから常に最新のデータを取得するようにしておけば実用的になると思います。
var tokyo = [ [ "2014/05/21", "20", "16" ], [ "2014/05/22", "24", "16" ], [ "2014/05/23", "25", "16" ], [ "2014/05/24", "25", "16" ], [ "2014/05/25", "28", "19" ], [ "2014/05/16", "25", "19" ], [ "2014/05/27", "23", "18" ] ];
2. グラフを表示するSVG要素の作成
jQueryと同じようにチェーン構文を使用します。
メソッドはD3.jsで独自に用意されたものを使用します。
下記のソースでは、#resultを選択し(選択されたノードセットは「セレクション」と呼ばれます。)、
その内部にSVG要素を追加し幅と高さを設定しています。
var svg = d3.select("#result") .append("svg") .attr("width", w) .attr("height", h);
3. 軸(目盛り)の作成
次にグラフの軸(目盛り)を作成します。
最初に、使用するデータの値と、表示幅を対応させるためにスケール処理を行っています。
これを縦軸、横軸それぞれに行います。
スケールを上手に使用すると、表示するデータに合わせて軸の値を変更させることができるので、
軸の表示範囲を超える値が含まれる場合にもグラフの表示領域を変更せずにデータを表示させることも可能です。
// 軸 var xScale = d3.time.scale() .domain([ new Date(2014, 4, 21), new Date(2014, 4, 27) ]) .range([padding, w - xAxisPadding - padding]); var yScale = d3.scale.linear() .domain([30, 0]) .range([padding, h - yAxisPadding - padding]);
次に軸のデータを作成します。
横軸に使用している日付の目盛の値は表示形式を変換しています。
var xAxis = d3.svg.axis() .scale(xScale) .tickFormat(d3.time.format("%m/%d")) .ticks(7); var yAxis = d3.svg.axis() .scale(yScale) .orient("left");
最後に、作成した軸のデータを入れる要素を追加し、軸のデータを読み込んだ後、
表示位置や表示方法に関する属性やスタイルを指定します。
svg.append("g") .attr("class", "axis") .attr("transform", "translate(" + xAxisPadding + ", " + (h - yAxisPadding) + ")") .call(xAxis) .selectAll("text") .attr("x", 10) .attr("y", -5) .attr("transform", "rotate(90)") .style("text-anchor", "start"); svg.append("g") .attr("class", "axis") .attr("transform", "translate(" + xAxisPadding + ", 0)") .call(yAxis);
4. 折れ線グラフの作成
次に気温の推移を表す折れ線グラフを作成します。
折れ線の切り替わるポイントは、SVG要素の高さ・幅を元にして、
軸の表示やグラフの周りに必要な余白を考慮に入れながら各日ごとに計算していきます。
横軸(x)は処理するインデックスの値を元にして等間隔に値を指定します。
縦軸(y)は気温の値を計算式に使用して、各日ごとに異なる値を指定します。
下記のソースは最高気温のみの記述なので、同じように最低気温の分についても記述します。
// 折れ線グラフ var line = d3.svg.line() .x(function(d, i){ return (i * dayWidth) + xAxisPadding + padding; }) .y(function(d){ return h - padding - yAxisPadding - ((h - yAxisPadding - padding * 2) / 30 * d[1] ); });
次に、軸の場合と同じように折れ線グラフのデータを入れる要素を追加し、必要な属性を設定します。
svg.append("path") .attr("class", "high") .attr("d", line(tokyo)) .attr("stroke", "#ed5454") .attr("fill", "none");
5. 散布図の作成(各日の値を目立たせるための●)
先に作成した折れ線グラフの値が変わる位置の見分けがつきにくかったので、
この位置と同じ場所に円を表示して、位置の特定が容易になるようにしてみました。
各位置ごとにX軸、Y軸の値を指定し、円の大きさや色を指定しています。
また、グラフの表示時に目立たせるためのアニメーション処理も追加しています。
(円の半径が0pxから4pxに遷移します。)
下記のソースも最高気温のみの記述なので、同じように最低気温の分についても記述します。
// 散布図 svg.selectAll(".high_circle") .data(tokyo) .enter() .append("circle") .attr("class", "high_circle") .attr("cx", function(d,i){ return (i * dayWidth) + xAxisPadding + padding; }) .attr("cy", function(d){ return h - padding - yAxisPadding - ((h - yAxisPadding - padding * 2) / 30 * d[1] ); }) .attr("r", 0) .attr("stroke", "#ed5454") .attr("stroke-width", "1px") .attr("fill", "#f8d7d7") .transition() .duration(1000) .attr("r", 4);
6. テキストで気温の値を追加
さらに、テキストで各日の最高気温、最低気温の値を表示しています。
他と同じようにテキストを追加する要素を作成し、テキストを追加しています。
散布図で作成した円の下に表示されるように表示位置を微調整しています。
// テキスト svg.selectAll(".high_text") .data(tokyo) .enter() .append("text") .attr("class", "high_text") .text(function (d) { return d[1]; }) .attr("font-size", "12px") .attr("fill", "#ed5454") .attr("x", function(d, i){ return (i * dayWidth) + xAxisPadding + padding - 6; }) .attr("y", function(d){ return h - padding - yAxisPadding - ((h - yAxisPadding - padding * 2) / 30 * d[1] ) + 16; });
これで、東京の一週間分の最高気温と最低気温の遷移をグラフに表示させることができました。
7. 都市名をクリックした時のグラフの書き換え
サンプルでは、東京の他に大阪・札幌・那覇の気温データも存在しています。
グラフ下部のリンクをクリックすることで、グラフの再描画用に作成したdraw()関数を呼び出して必要な処理を行っています。
draw()関数では、データがバインドされた既存要素に新しい値と表示位置を設定しなおしています。
d3.selectAll("a.osaka") .on("click", function(){ draw(osaka); });
サンプルではアニメーションしながら新しいポイントへ遷移していますが、
これは、draw()関数の中で、下記のメソッドを対象の要素に対して実行しているからです。
.transition() .duration(1000)
改善する点はまだまだありますが、
ひとまず今回のサンプルは、これで完成です。
他にどのようなことができるか
D3.jsでは他にも様々なことができます。
・膨大な経度・緯度を含むデータから地図を作成し、表現に必要な変換を行う。
(地図を表示させるためのデータは、ネットで手に入れることができます。)
・縦棒グラフ、横棒グラフ、積み上げ棒グラフ、円グラフ、パイチャート、ヒストグラム、バブルチャート、ツリーマップ
など様々な表現を行うために必要なデータの変換を行う。
・・・・
Githubに用意されたギャラリーでは、複雑で面白い作例がたくさん集められています。
まとめ
D3.jsを使うのは初めてでしたが、javascriptやjQueryを使う機会が多い方にとっては、
簡単なグラフを作成することは容易だと思います。
サンプルではd3オブジェクトのメソッドの詳細や、細かな部分の説明が不足していますので、
より詳しく知りたい方は下記の「参考・関連」などをご確認ください。
今回のサンプルではデータをjsファイルに直接記述しましたが、
データを入力する仕組みを別に用意しておくと、
D3.jsを使って作成したグラフや地図に入力内容やユーザーの情報を即座にグラフに反映させることもできると思います。
また、表示し終えたグラフや地図についても、
ユーザーの入力や発生させたイベントを元にして表示を最適な状態に変化させることが可能なので、
表現方法の可能性が広がっていると思います。
Githubに用意されたギャラリーのようなものを作ることは大変難しいですが、
他にもいろいろ試しながら少しづつ理解を深めていきたいと思いました。
参考・関連
■Web
・D3 入門 | スコット・マレイ | alignedleft
・D3.js例文辞典
・D3.js の Data-Driven な DOM 操作がおもしろい - てっく煮ブログ
・可視化ライブラリD3.jsの設計が素晴らしすぎる。 | 三度の飯とエレクトロン
・データビジュアライゼーション(D3.js)を学ぶための教材まとめ - NAVER まとめ
・svg要素の基本的な使い方まとめ
・スタイル付け – SVG 1.1 (第2版)
■書籍
・インタラクティブ・データビジュアライゼーション――D3.jsによるデータの可視化
・D3をはじめよう
・データビジュアライゼーションのためのD3.js徹底入門