D3.js入門(棒グラフをつくってみる)

D3.jsとは

D3.jsというJavaScripのライブラリをご存知でしょうか。

http://ja.d3js.info/ (日本語の公式サイト)

csvや配列などのデータをもとに、グラフを描画することができるライブラリです。
グラフなどは、公式サイトを見ていただければわかる通り、比較的簡単に自由度の高く、綺麗な図が描画可能です。  日本ではまだ認知度はあまり高くないですが、海外では既に多くの事例があり、GithubのYour Contributions(コミット回数が表示される図)などでも使われています。

データ・ドリブン

JavaScriptといえば、マウス操作などのイベントが中心となって処理が行われるため、イベント駆動言語などと言われたりしますが、D3という名前は”Data Driven Document”の略であり、データをもとにドキュメントや図を描画します。

データ・ビジュアライゼーション

こちらのGalleryに沢山の綺麗な図がのっていますが、これらは全てD3.jsによって作られたもので、様々なビジュアライズが可能であることがわかるかと思います。

https://github.com/mbostock/d3/wiki/Gallery

D3.js 入門

はじめに

では早速始めてみようということで、参考になるWebページを探してみますが、英語のドキュメントはたくさんあるものの、まだまだ日本語のドキュメントが少ないのが現状です。有志の方々が少しずつ翻訳していただいていますが、詳しく解説したものがまだ少なく感じます。 (APIを翻訳すると言って出来ておらず、すみません・・・)

ということで、最近気象庁が気象データのCSV配布を開始しましたので、降水量のデータを用いて一番イメージしやすい棒グラフの作成チュートリアルを書いていきたいと思います。

まずはhtmlの準備

ベースとなるhtmlを作成します。 D3.jsは"http://d3js.org/d3.v3.js"でホスティングされているため、わざわざダウンロードして使う必要はありません。もしminified版が使いたい場合は、こちらを指定してください。

<html>
  <head>
    <title>meteorology</title>
</head>
<body>
  <output id="graph"></output>

  <script src="http://d3js.org/d3.v3.js"></script>
  <script src="./js/precipitation.js"></script>
  </body>
</html>

CSVファイルの準備

では利用するCSVファイルを準備します。 気象庁の情報は、以下のリンクから自分の好きなように項目を選んで取得することができます。 出力項目、地域、期間などが自由に選べるため、かなり便利です。

http://www.data.jma.go.jp/gmd/risk/obsdl/index.php

今回は過去10年間の平均気温と降水量をダウンロードして使用しています。 (平均気温はまだ使いません。)

CSVファイルの読み込み

D3.jsにはファイルを読み込む機能とテキストをCSVにパースする関数があります。

d3.text(data, function(file) { /* ... */ });

このようにかくことで、XMLHttpRequestでファイルを取得し、関数で処理することができます。
CSVファイルのヘッダー行は今回使わないので、d3.csv.parseRows()で取得したCSVの配列を5行目まで削除しています。
即時関数の最後でグラフの作図処理を呼び出しています。

(function(){
 d3.text("data/tempAvg_precipitation.csv", function(csvFile) {
   var csvData = d3.csv.parseRows(csvFile);
   // delete header lines from csv 
   csvData.splice(0, 5)
   presipitation(csvData);  // グラフ作図関数
   });
})();

作図エリアの確保

では続いてグラフを作図するエリアを指定していきます。 グラフを書く場合、グラフの目盛りや単位を書いたりすると思います。
そのため、1000 * 400のグラフを作る場合、marginをとって目盛りを表示するスペースを確保しておきます。

// graph of precipitation / month
var presipitation = function(dataset) {
  // graph area
  var width = 1100,
       height = 500,
       margin = 50,
       w = width - (margin * 2),  //  400
       h = height - (margin * 2), // 1000
       barPadding = 2;           //棒グラフの間隔


  var svg = d3.select("#graph")
                      .append("svg")
                      .attr("class", "chart")
                      .attr("width", width)
                      .attr("height", height)
                      .append('g')
                      .attr('transform', 'translate(' + margin + ', ' + margin + ')');
                                                               
};

さて、ようやくd3がでてきました。 D3.jsにはCSSのようなセレクタがあります。使い方はjQueryに似ています。メソッドチェインを使ってどんどん処理をつないでいくところもjQueryに似ていますね。

今回の作図はSVGを使っています。 SVGは、XML形式でベクター画像の描画を行います。
SVGの使い方については、最近ドットインストールでSVG入門のレッスンが追加されていました。 これを見ておくと、D3.jsをより扱いやすくなると思います。
http://dotinstall.com/lessons/basic_svg

SVGは属性情報を追加することで色やサイズ、描画位置などを指定していきます。 translateは描画位置を移動させます。目盛り用に縦・横それぞれにのmarginを取っています。 また、marginは棒グラフに適用したいため、”g”(グループ)を追加しています。棒グラフの要素をg要素以下に作成すると、g要素で指定された属性がそれ以下の要素にも反映されます。

棒グラフを描画する

さて、いよいよグラフの描画を行います。
まずは棒グラフの棒の描画位置を決めていきます。 その後、実際の棒グラフの高さと幅を指定していきます。 SVGはHTMLと同じで左上が(0,0)になります。

作図エリアは"chart"クラスにありますので、セレクタを使います。
今回はselectAllで要素を取得しています。selectとselectAllは返すデータのタイプが単一要素か配列かという違いがあります。 SVG要素の配列をselectAllで宣言し、data()を使ってcsvデータの配列にバインディングしています。

そしてバインディングされた要素をenter()することでデータを使う準備がされます。
詳しく知りたい方はhttp://ja.d3js.node.ws/document/tutorial/circle.htmlを読んでみてください。

  // set rect size
  svg.selectAll(".chart")
    .data(dataset)
    .enter()
    .append("rect")
    .attr("x", function(d, i) { return i * (w /  dataset.length) })
    .attr("y", function(d) { return h - d[4] ); });

棒グラフの棒はrectで描画していきます。
attr("x", function(d, i) { / ... / }) は、バインディングされたすべての配列要素に対してカウンタiを使いながらx軸の描画位置を設定しています。カウンタに1つの棒のサイズをかけてあげれば描画位置が出てきます。
y軸については少し注意点があります。
下の図の白い枠の棒の長さがy軸の指定する位置になり、そこから青色の棒が描画されます。
今は白い枠の棒の長さをy軸で表現しているので、全体の長さから青色の棒の長さを引いてあげます。 CSVファイルの5列目に降水量のデータがあるので、d[4]が長さで使いたいデータです。

ではようやく棒グラフのサイズを指定していきます。(上の図の青色の部分です。)

  // graphing each data of dataset
  svg.selectAll("rect")
    .attr("width", (w / dataset.length) - barPadding) 
    .attr("height", function(d) { return d[4] });

1つの棒の幅は「全体の幅 / データの数 - 棒と棒の間隔」で計算できます。 1つの棒の高さはデータの大きさそのものです。 なお、データの大きさが大きすぎるときはここで高さの調整をしてあげます。

さて、これで棒グラフっぽいものが完成しました。

グラフに色を付ける

せっかくなのでデータの大きさに応じて色を変えたいと思います。 色は0〜255のrgbで表現できます。 データの最大値が255になるように、まずはデータの最大値を255で割ってあげます。 その値を全てのデータに対してかけてあげればデータのサイズに応じた色を塗ることができます。

全てのデータから最大値を求めるには
d3.max(dataset, function(d) { return d[4] })
と書いてあげればよいです。

あとは、selectAllで棒を描画するrectに対して"fill"属性を追加し、rgbの値を入れていきます。

  // coloring
  var colorDivNum = d3.max(dataset, function(d) { return d[4] })  / 255;
  svg.selectAll("rect")
    .attr("fill", function(d) {
        return "rgb(0, 0," + Math.round(d[4] / colorDivNum) + " )";
    });

グラフのサンプル
どうでしょうか、これでそれっぽくなったと思います。

今回はここまでですが、実際に棒グラフを書く場合は目盛りなども欲しくなると思います。 そちらのほうもいずれ解説してみたいと思います。