このページの本文へ

グラフ描くだけだと思ってない?D3.jsのデータバインディングがやっとわかった

2016年06月24日 10時39分更新

文●Kevin Kononenko

  • この記事をはてなブックマークに追加
本文印刷
イケてるグラフが描けると人気のJSライブラリー「D3.js」。でも、グラフを描くライブラリーならほかにもありますよね。人気の背景にあるデータバインディングについて、なぜか野菜畑を使って初心者向けの解説を試みてみました。

D3.jsは、bubble chartsline and bar chartsなどのすばらしいチャートをたった数行のコードで作成できる強力なデータ・ビジュアライゼーション・ライブラリーです。

JavaScript初心者程度の知識があれば、配列やオブジェクトをカラフルなビジュアルに変換することができます。しかし、初心者は最初、データがDOMの実際の要素とどのように結びついているのかを理解するのに苦労しています。これは「データバインディング」や「データ結合」と呼ばれ、プロセス全体の第一段階にあたる、とても重要な部分です。

直感的に言うと、for()ループがあるとして、次のようにデータのアイテムを入れてループさせ、要素を1つ作成します。

var data = [{x: 100, y: 100}, {x: 200, y: 200}, {x: 300, y: 300}]

for(var i=0; i< data.length; i++){
    svg.append("circle")
        .attr("cx", function(data) { return data[i].x; })
        .attr("cy", function(data) { return data[i].y; })
        .attr("r", 2.5);
}

ただし、これは本来の動作ではありません! 実際のD3のコードにはfor()ループなどなく、同じような機能を持つコードが存在しています。

var data = [{x: 100, y: 100}, {x: 200, y: 200}, {x: 300, y: 300}]

svg.selectAll("circle")
    .data(data)
    .enter().append("circle")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; }) 
    .attr("r", 2.5);

上のコードで、SVGキャンバスに黒い円が3つ追加されます。そう、だからD3のプログラミングには宣言的スタイルを使うのです。for()ループはこのコードに暗に隠されています。

これには少し慣れが必要ですので、上のコードを1行ずつ見ていくことにしましょう。そうすれば、何が起こっているかきちんと理解できます。家庭菜園と同じで、これを読み終わった頃には、5行から10行の基本的なビジュアライゼーションを作成し、(簡単な部分の)スタイリングを始められるはずです。

このコンセプトについてより技術的な解説は、D3のドキュメントまたはScott Murrayのデータバインディング ガイドを参照してください。

Step 1:SVG/領域の作図

データ・ビジュアライゼーションを作成する位置を決めます。これは、作物を植える場所を決めるのに相当します。

>var svg = d3.select("body")
    .append("svg")
    .attr("width", '800px')
    .attr("height", '800px');

bodyとなる800px四方の領域を作成し、そこに要素を追加していくという大変シンプルな方法です。
01

Step 2:selectAll/穴掘り

あとで要素を入れるグループを作成するselectAll()文が必要です。ちょうど庭に穴を掘るようなものだと思ってください。D3では、あとで要素全体を一度に更新・削除できるようにするため、このような方法をとります。次に例を1つ紹介します。

svg.selectAll("circle")

まだ円の追加をしていないなら、この方法でやってみてください。その際、「円」はSVG仕様における基本形であることに気をつけてください。円をすでに追加してある場合でも、次のようにクラスを使用すれば大丈夫です。

svg.selectAll(".circleClass")

02

上の絵はちょっと誤解をまねくかもしれません。本当は、庭の作物を植える部分に無数の穴が開いているということを示したいのですが、限られたスペースにイメージとして表現するうまい方法がなかったので、このように表現しました。重要なのは、データ要素を植える領域に線引きをしているとイメージすることです。SVGの「矩形」要素を追加したければ、庭の別領域で追加します。コードのこの段階では、まだ実際に追加する要素数がはっきりしていません。それをこれから確認しましょう!

Step 3:Data/種

ここが一番重要な部分で、ビジュアライゼーションで利用するデータを決定します。JavaScriptではこのデータを配列やオブジェクトの形にして渡しますが、ここではデータをselectAll()で指定したDOM要素のタイプに「バインド」します。これが終われば、JavaScriptと同じように、アイテムの配列やオブジェクトで参照できるようになります。それには2、3の手順が必要です。次のケースでは、配列にアイテムが3つありますから、完成すればDOMに3つの要素が加わったことになります。

var data = [{x: 100, y: 100}, {x: 200, y: 200}, {x: 300, y: 300}]

svg.selectAll("circle")
    .data(data)

これは、庭に植える種の種類を決めるのに相当します。種にはそれぞれ特徴があり、決まった花を咲かせます。

03

Step 4:Enter/穴への種まき

.enter()コマンドは、selectAll文を配列やオブジェクト内の要素数にマッチさせ、作成しなければならない要素の数を決定します。これでもう広大な領域図は必要なくなりました! ここで領域図にある穴の数は、育てたい植物の本数にマッチしています。

svg.selectAll("circle")
    .data(data)
    .enter()

04

たとえばこのコードでは穴は3つで、それぞれの穴に対して1種類の種(トマトなど)があります。またこれにより、コードが自動的にする反復数(3回)が決定されます。

Step 5:Append/作物の支柱

.append()コマンドは、SVG基本形のうちのどれを使用するかを決定します。selectAll()文には多くの選択肢がありますが、このステップで選択できるシェイプは7つしかありません(「g」もありますが、これはより高度になります)。selectAll()はグループを指定し、append()は実際のシェイプを指定します。

svg.selectAll("circle")
    .data(data)
    .enter().append("circle")

これは、植わっている作物に支柱を補うのに似ています。どのような野菜を育てたいですか? トマトを育てたい人は、タワー型の支柱が必要になります。同様に、シェイプやデータ・ビジュアライゼーションが異なれば、それに適したデータセットも異なってくるのです。

05

データへのアクセス方法について

これでDOMに3つの円要素を追加できました。領域を決めて穴を掘り、種を植えて作物を育てる支柱を立てました。ここでは、各円に対する属性の選び方を説明します。

.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })

円の仕様から、cxとcyを使ってSVGキャンバス内に円の位置を決定できます。どちらの場合もfunction(d)を使って、元の配列にある各アイテムのプロパティにアクセスします。先ほど.enter()を使用したので、このコードは配列の各アイテムに対して実行され、合計3回実行されます。

dは、{x: 100, y: 100}のように、配列内の各アイテムを表します。d,iと書かれている場合、そのiは1番目のアイテムならインデックス0、2番目のアイテムならインデックス1というように変化します。d.xの返り値は各アイテムのxプロパティを表しており、ピクセルに変換されます。今回の場合は100ピクセルくらいになるでしょう。さあ、ここから普通のJavaScriptが使えます! if文や関数など何でも使えます。

最後に

D3を利用してすばらしいものを作成するには、データをDOM要素に変換する方法を理解しなければなりません。スタイリングは、データ部分に比べれば格段にやさしいものです。テキストの追加はシェイプの追加によく似ているので、データの扱い方が分かれば分かるようになるでしょう。

D3は、ほぼ自動的にいろいろなことをやってのけるフレキシブルなライブラリーです。このデータバインディング構造を利用すれば、たった1、2行のコードで複雑なアクションも書けます。さあ、外へ出て、ユーザーを「あっ」と言わせましょう!

(原文:A Beginner’s Guide to Data Binding in D3.js

[翻訳:Noriko O. Romano]
[編集:Livit

Web Professionalトップへ

WebProfessional 新着記事