こんにちは。エンジニアの GM です。
D3.js が話題になって久しい今日この頃、遅ればせながら私も D3.js を触ってみました。ギャラリー を見ると短いコードでクールなグラフが実装されており、簡単に似たようなものを作ることができるのですが、使用するデータや、細かい表示の差異によってある程度カスタマイズする必要があります。今回は基礎的なことについて簡単に触れた後、2階層の円グラフを描画するためのコードを例に出し、D3.js の基本的な使い方を紹介したいと思います。

目次

  • データ可視化の基礎

  • 2階層の円グラフの描画

  • まとめ
    • データ可視化の基礎

      早速基本的な使い方から説明したいと思います。ざっくりとした手順は以下のとおりです。

      1. SVG要素を作成する
      2. 作成した要素にデータをバインドする(data())

      D3.js の便利機能は他にいくらでもあるのですが、これが D3.js の目玉機能として一番最初に覚えるべき項目です。以上のことを行う準備として

      1. SVG 描画
      2. データのバインド

      について簡単に触れておきたいと思います。

      SVG のおさらい(円弧の描き方)

      SVG を用いて様々な図形を描画することができますが、今回用いる円弧に絞っておさらいしておきたいと思います。
      その他の図形については Mozillaによるドキュメント が充実しているのでそちらを参照されるとよいと思います。

      以下のコードは扇形を描画するものです。

      実際の描画についてはJSFiddleでご確認いただけます。
      path 要素の d 属性により定義します。M x, y で始点を定義し、A rx ry x-axis-rotation large-arc-flag sweep-flag x y で円弧の半径、軸の傾き、弧の描く部分、終点を定義します。さらに L x, y で 円弧の終点から (x, y) へ直線を描画します。

      データのバインド

      D3.js では data() というメソッドを使って DOM とデータをむすびつけることができます。DOM とデータをむすびつけるとはどういうことか、簡単なコードを使って説明します。

      以下の HTML に描画することを想定しています。

      実際の挙動については JSFiddle でご確認いただけます。上記コード内にもコメントしていますが、これに加えてコメントしておきます。
      5行目で div 要素を選択し、ここに HTML の要素を追加していきます。7行目で li 要素を選択し、data() を使うことで li とデータをむすびつけています。次の8行目でデータとむすびついた li 要素を作成しています。さらに8行目で append() を実行することではじめて描画されます。さらに12行目では、要素に結びついたデータを用いて属性を定義しています。

      ここまでの流れをおさらいしておきます。

      • データとむすびつけたい要素を選択する
      • data() で要素とデータをむすびつける
      • enter() でデータとむすびつけた要素を作成する

      以上がデータバインドの基礎です。既に存在する要素とデータのバインドはまた別のメソッドを使う必要がありますが、まず最初にグラフを表示することを目的としているので、データのバインドについてはこのあたりで終わりにしておきます。なお、このあたりのことについては下記チュートリアルで丁寧に説明されています。

      参考: D3 Tutorial Binding data

      2階層の円グラフの描画

      さて、初歩の初歩が終わったところで、より実践的なサンプルプログラムを作ってみます。ここでは、Backlog などから取得したデータを用いて、各プロジェクトでどのように時間が使われているか(タスク or バグフィックス or その他)を2階層の円グラフで表現するためのプログラムを作ることにします。以下に全体のコードを記載しておきます。

      ここでは以下のデータを描画します。

      基本的な方針

      基本的な方針としては、SVG 領域内に path 要素を作成し、d 属性で円弧、直線、円弧と定義することで、扇形の根本から小さな扇形を切り取った形(何と呼ぶのか知りませんが、簡単のため以下扇形と呼ぶことにします)を描画していきます。ただ、これを普通にやろうとするとまず全体の大きさを計算して各要素の割合を算出し、それに応じて中心角の大きさを求め、円弧の始点と終点を求め・・・と大変面倒なことになります。
      D3.js にはそのような面倒を大幅に削減してくれる便利機能が多数揃っています。今回のような複雑な円グラフを描くために Partition Layout なる関数群が準備されているので、それを使うことにします。

      複雑な円グラフを描くための Partition Layout

      Partition Layout を使うためには、ちょっとした準備が必要です。それを行っているのがl.11 – 20 です。

      この中ではまずchildren() を用いて元データ(今回の例では project.json)の構造を明示しています。今回の例では data というプロパティの中に子要素が含まれることを示しています。つまり、

      • data
        • 子要素1
        • 子要素2

      という構造のデータであることを伝えています。
      続いて、value() で各要素の値を定義します。今回の例では time を指定しています。上記のとおり、children と value を指定したうえで

      とすると、以下のプロパティを持つデータが生成されます。

      • children
      • depth
      • dx
      • dy
      • value
      • x
      • y

      元の JSON では最下層の要素のみ

      のように値が定義されているのですが、value() で値を指定したことにより、その親要素も value というプロパティを持つようになります。そしてその値は、その子要素の value の合計値となっています。今回の例でいうと、以下のようなデータが作られることになります。

      • {“project”: “humming bird”, “value”: 205, …}
      • {“type”: “task”, “value”: 100, …}
      • {“type”: “bug fix”, “value”: 100, …}
      • {“type”: “other”, “value”: 5, …}

      ここで得られた値を用いて path 要素を定義していくのですが、そのままでは扇形を描画することができません。扇形を描画するため、size()を用いて x を 2π 倍しています。

      svg.arc を用いた扇形の描画

      svg.arc() を使うことで、円弧を描くための path 要素の d 属性値を得ることができます。partition.nodes(data) で得られるプロパティ x, y, dx, dy を次のように用いることで、扇形を描画することでできます。

      データのロードからSVG要素の描画

      細かい点は他にもいろいろありますが、以上で円グラフを描くための準備ができました。あとは「データのバインド」で学んだことを行うだけです。一点異なるのはデータのロードの方法です。 d3.json() でデータをロードし、続く処理をそのコールバック関数として実装します。

      続いて g 要素にデータをバインドします。

      さらにバインドされたデータに基づき path 要素の属性値を指定して描画します。

      実際の挙動

      今回紹介したコードの実際の挙動についてはJSFiddleでご確認いただけます。

      2階層の円グラフ描画方法まとめ

      以上で2階層の円グラフが描画されます。今回のプログラムで行ったことをまとめます。

      • partition.children(), partition.value() でデータの親子関係を特定し、子要素の value の合計値を親要素の value に持たせる
      • partition.nodes() で path 要素の属性値を定義するためのパラメータ(x, y, dx, dy, …)を計算する
      • path 要素とデータを結びつける
      • svg.arc() で partition.nodes()から得られたパラメータ(x, y, dx, dy)を path 要素の d 属性値に変換する

      上記各種メソッドを使うことで、特に面倒な計算をすることなく、元のデータを反映した DOM を描画することができました。

      まとめ

      ここまで、初歩的なところから始めて2階層の円グラフを描くことができるようになりました。今回行ったことを一般化してみます。

      • 元データから DOM を描画するためのデータを生成する
      • データと DOM をむすびつける
      • むすびつけられたデータから DOM の属性値を生成する

      今回、2階層の円グラフを描くために、Partition Layout を用いて元データから DOM を描画するためのデータを生成し 、svg.arc を用いてデータから DOM の属性値を生成しました。D3.js には、円グラフに限らず複雑なデータ可視化を実現するための便利機能が多数準備されていますが、基本的な使い方は変わりません。サンプルもたくさんありますのでこれを機に D3.js を使ったデータ可視化に取り組まれてみてはいかがでしょうか。