D3.jsの概要と使い所について

個人的に初めてD3.js（以下、D3と呼びます）を使う機会があったので、その時に得られたD3の大まかな概要やD3（もしくはSVG）の使い所を纏めておきたいと思います。特にD3を使った事がない人向けですが、ここではD3についてハンズオン的な事はしないのでご了承ください。

# D3とは

![FireShot Capture 74 - D3.js - Data-Driven Documents - https___d3js.org_.png](https://qiita-image-store.s3.amazonaws.com/0/57442/580dd498-afaf-437c-c698-fb58c99ac328.png)

>D3 (or D3.js) is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML. D3 combines powerful visualization and interaction techniques with a data-driven approach to DOM manipulation, giving you the full capabilities of modern browsers and the freedom to design the right visual interface for your data.

上記は、[公式サイト](https://d3js.org/)からの引用です。

以下はD3の[Gallery](https://github.com/d3/d3/wiki/Gallery)の一部ですが、D3を使う事でかなり複雑な処理でも**比較的容易**に実装する事ができます。勿論、モノによっては自分で計算が必要なケースもあるので、全てのサンプルが魔法のように実現できる訳ではありません。

<img width="708" alt="スクリーンショット 2017-04-08 20.49.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/57442/10d18897-2476-cb27-fb55-81930a548094.png">


## GitHubのStar数から見るD3

2017/5/7時点でD3のGitHub上のStar数は`62,821`で、全リポジトリ`1,877,429`の中では`facebook/react`に次いで5番目にStar数が多いリポジトリになっています。

![FireShot Capture 75 - Search · stars_}1_ - https___github.com_search.png](https://qiita-image-store.s3.amazonaws.com/0/57442/560ca363-d76c-53db-8278-3f5d15495be1.png)


## GoogleTrendsから見るD3

D3の[v1.0.0](https://github.com/d3/d3/releases/tag/v1.0.0)がリリースされたのは2011/2/18になりますが、2015/4をピークに安定した人気を保っているようです。

<img width="1165" alt="スクリーンショット 2017-04-08 20.12.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/57442/392f9ed8-b011-7755-7950-5e475cc83fb3.png">



## 2016年に約4年ぶりのメジャーアップデート

2016/6/29にメジャーバージョンがアップデートされ、[v4.0.0](https://github.com/d3/d3/releases/tag/v4.0.0)がリリースされました。現在（2017/5/7時点）では、[v4.8.0](https://github.com/d3/d3/releases/tag/v4.8.0)が最新バージョンとなっています。

v3→v4のアップデートにより名前空間が大きく変更されたため、それまでの書籍や記事に書かれたコードは使えなくなってしまいました。しかし、アルゴリズムの見直しによる性能の向上や便利な関数が追加されているので、これからD3を学ぶ人はv4の方が恩恵が大きいと思います。
v4の変更点については以下のスライドがとても参考になります。

- [D3.js version.4を学ぶための学習リソースの紹介。](https://shimz.me/blog/d3v4/5295)
- [細かすぎて伝わらないD3 ver.4の話](https://www.slideshare.net/xxshimizuxx/d3-ver4)

## D3の誤解

[D3.jsにあてはまらないこと](http://postd.cc/what-d3js-is-not/)の記事からD3の誤解について幾つか取り上げます。

### D3はチャートライブラリではない

D3のGalleryを見ると、あたかもチャートを表示するためのライブラリに見えてしまいますが、D3はチャートを含むデータのビジュアライズを比較的容易にしてくれるためのライブラリであり、[Chart.js](http://www.chartjs.org/)のようなチャート専用のライブラリではありません。
因みにD3をベースにしたチャートライブラリとして、[C3.js](http://c3js.org/)があります。

### SVGだけでなくHTML Elementも操作できる

D3で描かれるチャートは基本的にSVGで描画されますが、div要素やinput要素などのHTML Elementsも操作する事ができます。v4からCanvasのレンダリングもサポートしたようです。

### SVGのポリフィルではない

最近はレガシーブラウザも対応しなくて良い時期にきているので、あまり問題にならないと思いますが、D3はSVGのポリフィルではないので、SVGに対応していない環境（IE8やAndroidバージョン3xなど）では期待しているような使い方は出来ない可能性があります。
以下は[Can I use](https://caniuse.com/#feat=svg)のSVGのサポート状況を示したものです。

![スクリーンショット 2017-04-08 23.17.11.png](https://qiita-image-store.s3.amazonaws.com/0/57442/23cebc33-b10a-3618-3a44-767dc6ec18ef.png)

IE8のようなレガシーブラウザでベクターグラフィックスを利用可能にしてくれるライブラリとして[Raphael.js](http://dmitrybaranovskiy.github.io/raphael/)があります。

## D3の旨味

### jQueryライクにSVG操作

D3でDOM操作を行う場合は、jQueryの場合と似ているため、jQueryを利用した事がある人は飲み込みやすいかもしれません。例えば、DOM生成処理は以下のようになります。

```js:D3とjQueryのDOM生成処理の比較
//D3
var $elements = d3.select('.parent')
  .append('div')
  .classed('child', true)
  .on('click', () => {
    console.log('click child.');
  });

//jQuery
var $elements = $('.parent')
  .append('<div>')
  .addClass('child')
  .on('click', () => {
    console.log('click child.');
  });
```

他の関数に関しては以下の記事が参考になります。

- [jQueryのあれ、D3.jsでどうやるの？(または、その逆)](http://qiita.com/itagakishintaro/items/51c89fe7e14702cb98e6)

### 要素にデータをバインドする事で反復処理が簡単に

D3の特徴の１つに要素にデータをバインドする事が挙げられます。要素にデータをバインドする事でfor文を書かずに反復処理が行えるのでコードの記述がよりシンプルになったり、増減があったデータのみに対してアニメーションの処理を加えるといった事が簡単に行えます。

例えば、以下のような縦棒グラフを表示する場合を考えます。

<img width="353" alt="スクリーンショット 2017-04-13 20.54.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/57442/8ac4f6a0-6aa8-9324-1908-c26bc8765ca6.png">


```js:縦棒グラフ用のデータ
var chartData = [
	{ value: 100, color: 'blue' },
	{ value: 20, color: 'red' },
	{ value: 10, color: 'green' }
];
```

```js:棒グラフのサンプル
const SVG_HEIGHT = 150;
const BAR_WIDTH = 100;
const BAR_INTERVAL = 20;

let $bars = d3.select('.svg')
	.selectAll('rect')
	.data( chartData )
	.enter()
	.append('rect')
	.attr('x', (d, i) => {
		return i * BAR_WIDTH + i * BAR_INTERVAL;
	})
	.attr('y', (d) => {
		return SVG_HEIGHT - d.value;
	})
	.attr('width', BAR_WIDTH)
	.attr('height', (d) => {
		return d.value;
	})
	.attr('fill', (d) => {
		return d.color;
	});
```

上記の処理は、D3を使った事がない人にとっては一見分かりづらいかもしれませんが、処理の仕組みが分かれば決して難しくありません。
要点だけ説明すると、`d3`オブジェクトの`data`関数を使用して`.svg`要素に対してデータをセットし、`append`関数で`rect`要素をビューに追加しています。`enter`関数はセットされたデータの一部を取得するものですが、少しややこしいのでここでは割愛します。

特に注目すべき点は、`attr`関数の第二引数にコールバックを指定している点です。
このコールバック関数はデータの数だけ繰り返し実行されます。つまり、`chartData`配列の長さ＝３回実行されるのですが、コールバック関数の戻り値が`attr`関数の第一引数で指定した属性に設定されます。

```js:d3.attr関数
var $bars = d3.select('.svg')
	//~割愛~
	.attr('y', (d) => {
		return SVG_HEIGHT - d.value;
	})
```

さらにコールバック関数の第一引数`d`には要素に紐づけられたデータが入るので、データに応じて柔軟に設定する値を変える事が出来ます。上記の場合は、データの`value`値によって`y`属性の値を変更しています。このように要素にデータをバインドする事でfor文を記述せずに反復処理を行う事が出来ます。

上記のような処理を利用する事でデータに応じて要素のサイズを変えたり、スタイルを変えたりする事が可能になり、データの可視化を行いやすくしてくれます。

## 補足：SVGとCanvasの比較

HTML5を使ってグラフィカルな表現を行う手段としてSVGとCanvasがありますが、２つの技術は根本的に異なる技術を使って描画しています。
SVGはベクターグラフィックスと呼ばれる方式で画像描画を行なっており、元となるデータは描画する要素それぞれの相対的なパス情報や色といったパラメータを扱っているため、パラメータを変更すれば大きさ等も容易に変更する事が出来ます。結果として要素を大きく描画しようが小さく描画しようが元となるデータのサイズにほとんど影響がありませんが、要素の数が多い場合は描画コストが大きくなります。

一方、Canvasはラスターグラフィックスと呼ばれる方式で画像描画を行なっており、元となるデータにはピクセル単位で色情報が格納されています。ピクセル単位で色描画した結果、1枚の絵が出来上がるような形なので、描画する絵がシンプルだろうが複雑だろうが変わりはありませんが、絵が大きい程描画コストが大きくなります。

<img width="879" alt="スクリーンショット 2017-04-13 21.44.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/57442/909a2cda-34d9-2a92-bbf6-95db79e3a69b.png">

![svg_canvas_パフォーマンス比較.png](https://qiita-image-store.s3.amazonaws.com/0/57442/668abe6b-206e-782f-8359-b91084a16e92.png)

上図の引用元：[SVG と Canvas: どちらを選ぶか](https://msdn.microsoft.com/ja-jp/library/gg193983(v=vs.85).aspx)

## まとめ

- D3はチャート専用のライブラリではない
- D3を使うと比較的簡単にデータの可視化が行える
- 描画領域のサイズと描画要素の数によってSVGとCanvasを使い分ける

v4の情報はまだ多くないかもしれませんが、D3自体の情報は少し調べればたくさん出てくるので、興味がある方は試しに使ってみると良いかもしれません。

余談ですが、以前D3を使って[GitHubのスター数を可視化した時の記事](http://qiita.com/m_ohsumi/items/caadedceae7cb38bc7d3)もあるので、良ければご覧ください。