読者です 読者をやめる 読者になる 読者になる

プログラミングの実験場

Haskell/Webアプリ/画像処理/可視化/ITによる生産性向上 など

D3.jsの基本パターン

D3.jsをいじっているが、最初に期待していたのよりも難しい。

公式サイトには資料が結構多くあるのだが、あまり整理されていない感じがある。

サイトに載っている実装例が大変に華麗でmotivatingなのだが、その分ちょっと複雑なソースコードが多い。

なので、以下で基本操作を最短でマスターするための道のりについての覚え書き。

データとDOM要素の対応付け

まず、データとDOM要素(主にSVG)を対応させる方法を理解する必要がある。以下のパターンがデータからDOM生成する基本である。

var dat = [{r: 10, x: 100, name: "Alice"},{r: 8, x: 150, name: "Bob"},{r: 15, x: 200, name: "Chris"}];

var sel = d3.select("body").append("svg").attr('width',300).attr('height',300);

sel.selectAll("circle")
    .data(dat, function(d){return d.name;})
    .enter()
    .append("circle")
    .attr('fill','pink')
    .attr('r',function(d){return d.r;})
    .attr('cx', function(d){return d.x;})
    .attr('cy', function(d,i){return i*50;});

第2文はjQueryみたいな感じで直感的である。selはbody内に追加されたsvg要素を示す。

attr()の第2引数には値として、定数あるいはコールバック関数を指定できる。 コールバック関数は

  • 第1引数: データポイント(数値、オブジェクト、配列)の配列
  • 第2引数: 配列のインデックス

を受け取る。

第3文のシーケンスはちょっとあれ?となると思う。これを理解するには、以下の記事が役に立つ。

selectAllというのは仮想的なプレースホルダをselの中に生成する操作である。selectとselectAllの違いはここにある。一見ちょっとややこしいが、selectAllは階層構造を潰して平たい配列を作るが、selectは階層構造を保持する、という違いがある。

enter(), exit()という関数は、data()でデータを割り当てたあとにメソッドチェインして、DOMを更新する関数。

ここで、データには、data()の第2引数でコールバック関数を渡すことで、identity keyを設定することができる。このkeyが対応するDOM要素にも記憶されるので、データをdata()で再度割り当てた時に、DOM要素をそれに応じて更新できる。

もし割り当てた新しいデータが、選択された既存のDOM要素と一致しない場合:

  • enter().append('DOM要素') を呼ぶと、足りない分のDOMが追加される。
  • exit().remove() を呼ぶと、新しいデータに対応物が存在しないDOM要素が削除される。

このデータとDOM要素のマッチングの際に、上記のidentity keyが使われる。もしdata()でidentity keyを指定していなかった場合は、単に配列のインデックスが使われる。つまり、data(dat,function(d,i){return i;})としたのと同じことになる。

それを例示しているのが、以下の例

トランジション

transition()をメソッドチェインの中に挟むと、それ以降のattr()やstyle()での変更がアニメーションになる。また、

  • delay()でアニメーション開始時刻を設定できる。
  • duration()でアニメーションにかかる時間を設定できる

この2つとも引数として関数を指定できるので、データポイントに依存した設定が可能。以下に例を示す。

   sel.transition()
    .delay(function(d,i){return 1000*i;})
    .duration(1000)
    .attr('r',function(d,i){return d.r*2});

スケーリング

データの値と描画座標の間の変換を設定できる。これを有効活用すれば、自分でデータから描画座標を計算する必要はあまり無いはず。 線形、指数、対数のスケールや、座標の離散化、quantileでの座標変換を設定することもできる。 また、数値→座標だけでなく、

  • 時間から座標を計算するtime scale や
  • 系列データから座標を計算するordinal scale

というのもある。

その他

  • http://bl.ocks.org/mbostock/4062085

    • nest()を使ったデータをグループ化する方法が分かる。
  • グラフなどを書くための高レベルAPI

    • d3.layout.***の関数が用意されている。