D3によるデータビジュアライゼーション 2013.09.13
-
Upload
minoru-chikamune -
Category
Software
-
view
352 -
download
2
Transcript of D3によるデータビジュアライゼーション 2013.09.13
ウルシステムズ株式会社http://www.ulsystems.co.jp
mailto:[email protected]
Tel: 03-6220-1420 Fax: 03-6220-1402
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
D3によるデータビジュアライゼーション
2013/9/13
講師役:近棟 稔
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
はじめに
1
Webブラウザーの進歩は速く、パソコンやスマートフォンやタブレットや電子書籍リーダーに搭載されている多くのモダンなWebブラウザーは、既にインラインSVGをサポートしています。インラインSVGとは、HTML内にSVGタグを埋め込むことにより、その領域内にベクター形式の図形を描画することの出来る仕様です。(SVG: Support Vector Graphics)
このインラインSVGが多くのデバイスで利用可能になったことにより、従来のHTML表現の限界は大きく引き上げられました。
一方、データビジュアライゼーション分野が発展・普及してきました。データによっては、単に数値を羅列しても人間が直感的にそのデータの傾向を把握することが難しいことがあります。そのため、従来から棒グラフや円グラフ、散布図といった標準的なデータの表現方法が要所要所で用いられてきています。データビジュアライゼーションの分野では、既存のデータ表現に加えて、様々な表現方法が考案され、それが実際に用いられるようになって来ています。
そして現在、データビジュアライゼーションとインラインSVGが融合し、Webページ上でインタラクティブに操作可能なデータビジュアライゼーション方式が登場しました。可視化したいデータをJavaScriptで保持し、そのデータをJavaScriptで加工し、それをJavaScriptからインラインSVGを用いて描画します。描画されたものをマウスで操作すると、見たい角度からデータを見たり、加工したりする事が出来るようになります。
このような、HTML上でインタラクティブなデータビジュアライゼーションを行うライブラリーとして有名になったのが、D3(Data-Driven Documents)です。
この勉強会ではSVGの基礎からはじめ、D3を用いたデータビジュアライゼーション方法の一部をご紹介します。D3を用いたデモンストレーションは以下のURLに沢山掲載されていますので、勉強会に参加されない方も、「今ならWebブラウザー上でこんな事が可能になっている」という事を知ることが出来ると思います。
(キーの←や→でスライドがめくれます)http://mbostock.github.io/d3/talk/20111018/#3http://bost.ocks.org/mike/https://github.com/mbostock/d3/wiki/Gallery
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
インラインSVG入門:Web画面にSVGで点を描く
インラインSVGを使えば、HTMLを記述するのと同じ要領で画面に点を描画できます。
2
<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style type="text/css">svg {border: dashed 1px blue;}</style>
</head><body>
<svg width="100" height="100"><circle cx="50" cy="50" r="10"></circle>
</svg></body>
</html>
ここがインラインSVGの部分
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
D3入門:Web画面にD3で点を描く
D3を用いて点を描いてみると、以下のようになります。D3はSVGにおけるjQueryのような感触です。
3
<!DOCTYPE html><html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style type="text/css">svg {border: dashed 1px blue;}</style>
</head><body>
<svg width="100" height="100"><circle cx="50" cy="50" r="10"></circle>
</svg></body>
</html>
<!DOCTYPE html><html>
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style type="text/css">svg {border: dashed 1px blue;}</style>
</head><body>
<script type="text/javascript" src="js/d3.v3.js"></script><script type="text/javascript">
d3.select("body").append("svg").attr("width", 100).attr("height", 100).append("circle").attr("cx", 50).attr("cy", 50).attr("r", 10);
</script></body>
</html>
元の姿
D3での書き方
d3.select("body")でbodyのDOMをつかむ
<svg width="100" height="100">と同じ。
<circle cx="50" cy="50" r="10">と同じ。
描画結果はどちらも同じ
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
D3入門:基本のAPIを使ってsinカーブを描画
4
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style type="text/css">svg {border: dashed 1px blue;}</style>
</head><body><div id="area"></div><script type="text/javascript" src="js/d3.v3.js"></script><script type="text/javascript">var svg = d3.select("#area").append("svg").attr("width", 360).attr("height", 100);for (var deg = 0; deg < 360; deg++) {svg.append("circle").attr("cx", deg).attr("cy", 50 - 40 * Math.sin(deg * Math.PI / 180)).attr("r", 1);
}</script>
</body></html>
circleを用いてsinカーブを描画してみる事も簡単にできます。
上記ソースの面倒な点:SVGで簡単に点を描画できるのは良いけれども、描画の原点が左上にあり、数学でよく使う座標系とは異なっていて、変換が面倒。また、sinカーブの場合はy軸のオフセットや拡大率の計算も面倒。軸の描画も無いのでさみしい。
SVGの原点
オフセット 拡大率
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
D3入門:基本のAPIを使ってsinカーブを描画その2:スケールの導入
座標系の変換処理は、D3の「スケール」を使えば楽になります。座標系の種類は、ここで利用しているリニアスケール以外にも、対数軸なども利用可能です。
5
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><style type="text/css">svg {border: dashed 1px blue;}</style>
</head><body><div id="area"></div><script type="text/javascript" src="js/d3.v3.js"></script><script type="text/javascript">var svg = d3.select("#area").append("svg").attr("width", 360).attr("height", 100),
x = d3.scale.linear().domain([0,360]).range([0,360]),y = d3.scale.linear().domain([-1,1]).range([90,10]);
for (var deg = 0; deg < 360; deg++) {svg.append("circle").attr("cx", x(deg)).attr("cy", y(Math.sin(deg * Math.PI / 180))).attr("r", 1);
}</script>
</body></html>
domain:データの取りうる範囲range:描画の範囲→これをリニアに変換してくれる
xやyの関数は、データ値が与えられたら、描画時の座標を返却するような関数。
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
以後、HTMLからCSSとJSを分離します
6
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" href="sample.css"></link>
</head><body><div id="d3area"></div><script type="text/javascript" src="js/d3.v3.js"></script><script type="text/javascript" src="sample.js"></script>
</body></html>
svg {border: dashed 1px blue;}
var svg = d3.select("#d3area").append("svg").attr("width", 360).attr("height", 100),x = d3.scale.linear().domain([0,360]).range([0,360]),y = d3.scale.linear().domain([-1,1]).range([90,10]);
for (var deg = 0; deg < 360; deg++) {svg.append("circle").attr("cx", x(deg)).attr("cy", y(Math.sin(deg * Math.PI / 180))).attr("r", 1);
}
使用するHTMLsample.html→以降、これに固定
sample.css
sample.js
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
DOMノードにデータを持たせる
D3の最も特徴的なアーキテクチャは、HTMLのDOMノードに対して __data__ というキー名でデータを保持する事にあります。これを徐々に説明していきます。まず、今までのsinカーブの各circleにデータを付けてみます。
7
var svg = d3.select("#d3area").append("svg").attr("width", 360).attr("height", 100),x = d3.scale.linear().domain([0,360]).range([0,360]),y = d3.scale.linear().domain([-1,1]).range([90,10]);
for (var deg = 0; deg < 180; deg++) { // 今後の説明のために描画は180度までとするsvg.append("circle")
.attr("class","dot") // circle一つ一つに「dot」クラスを設定
.attr("cx", x(deg))
.attr("cy", y(Math.sin(deg * Math.PI / 180)))
.attr("r", 1);}// 上記circleにデータを付与するvar data = d3.range(360); // [0,1,2, ... ,359] つまり、データ側は359度までsvg.selectAll(".dot").data(data); // これでDOMノードに __data__ が付与される
データを持たせる前
データを持たせた後
jQueryのように、 selectAll関数を用いれば様々なセレクション方法でDOMノードを掴む事ができます。
その掴んだDOMノードに対してデータを割り振ることも出来ます。
ちゃんとデータが付いています
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
DOMノードに付けたデータを活用する
データを持たせたセレクション結果に対して、まとめてDOM操作を行うことが出来ます。ここではDOM操作が可視化出来るようにするためにアニメーション処理を入れ、cosカーブに変更してみます。
8
var svg = d3.select("#d3area").append("svg").attr("width", 360).attr("height", 100),x = d3.scale.linear().domain([0,360]).range([0,360]),y = d3.scale.linear().domain([-1,1]).range([90,10]);
for (var deg = 0; deg < 180; deg++) { // 今後の説明のために描画は180度までとするsvg.append("circle")
.attr("class","dot") // circle一つ一つに「dot」クラスを設定
.attr("cx", x(deg))
.attr("cy", y(Math.sin(deg * Math.PI / 180)))
.attr("r", 1);}// 上記circleにデータを付与するvar data = d3.range(360); // [0,1,2, ... ,359] つまり、データ側は359度までsvg.selectAll(".dot").data(data) // これでDOMノードに __data__ が付与される.transition() // DOMの書き換えをアニメーションさせる指定。この指定がなければ一瞬で変化する.delay(function(d){return 10 * d;}) // DOMの書き換えをX座標に依存して遅らせる指定.duration(function(d){return 500 + 10 * d;}) // アニメーション速度の指定.style("fill","green").attr("r",2) // CSSスタイルを変更.attr("cy",function(d){return y(Math.cos(d * Math.PI / 180));}); // cosカーブに変更
attr関数やstyle関数やdelay関数やduration関数など、多くのDOM操作を伴う関数は、引数として実際の値の他に、関数を渡すことが可能です。
関数の第一引数には __data__ が渡され、第二引数にはインデックス番号が渡されます。
このような仕組みとなっているため、D3ではDOMノードに持たせたデータを様々な局面で有効活用できます。ここがD3の強みです。
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
既存のDOMノードの数がデータより少ない状況で、新規DOMノードを追加する(D3のenter関数を理解する)
DOMのセレクション結果とデータを関連付けた後、データ側が余った場合に、DOMに関連付け出来なかったデータを元に新規のDOMノードを追加する操作が可能です。
9
var svg = d3.select("#d3area").append("svg").attr("width", 360).attr("height", 100),x = d3.scale.linear().domain([0,360]).range([0,360]),y = d3.scale.linear().domain([-1,1]).range([90,10]);
for (var deg = 0; deg < 180; deg++) { // 今後の説明のために描画は180度までとするsvg.append("circle").attr("class","dot") // circle一つ一つに「dot」クラスを設定
.attr("cx", x(deg))
.attr("cy", y(Math.sin(deg * Math.PI / 180)))
.attr("r", 1);}// 上記circleにデータを付与するvar data = d3.range(360); // [0,1,2, ... ,359] つまり、データ側は359度までsvg.selectAll(".dot").data(data) // これでDOMノードに __data__ が付与される
.enter() // DOMのセレクション結果よりdata側が多かった場合、そのはみ出した部分の処理に入る
.append("circle").attr("class","dot")
.attr("cx",function(d){return x(d);})
.attr("cy",function(d){return y(Math.sin(d * Math.PI / 180));})
.attr("r",2).style("fill","red");
実はこの部分もenterで処理可能です。
ここがenterによって追加されました。
svg.selectAll(".dot").data(d3.range(180)).enter().append("circle").attr("class","dot").attr("cx",function(d){return x(d);}).attr("cy",function(d){return y(Math.sin(d * Math.PI / 180));}).attr("r",1);
書き換えてみるとこうなります。
この方式ではセレクション結果が空っぽの状態から開始します。データをDOMに紐付けるdataメソッドの処理も、「すべてのデータがDOMとは紐付かない」という結果になります。enter関数で新規DOMを作成すると、全データに対するDOMノードが作成されます。
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
enterの理解を深める その1
D3でのenterの仕組みは、なかなか理解できないことで有名です。ステップに分けて説明します。
10
svg.selectAll(".dot").data(d3.range(180)).enter().append("circle")
svg.selectAll(".dot").data(d3.range(180)).enter()
svg.selectAll(".dot").data(d3.range(180))
svg.selectAll(".dot") クラス「dot」が付いているDOMノードをDOMツリーから検索します。
クラス「dot」が付いているDOMノードの一覧と、リスト構造のデータを結合します。DOMノード一覧 = ◯ ◯ ◯ ◯データのリスト = ◇ ◇ ◇ ◇ ◇ ◇ ◇ ◇ ←これらを先頭から組にする
DOMに関連づいていないデータの一覧に絞ったデータの配列+D3ロジックが返ります。DOMノード一覧 = ◯ ◯ ◯ ◯データのリスト = ◇ ◇ ◇ ◇ ◇ ◇ ◇ ◇ ←DOMに関連づいていないデータ
svg.selectAll(".dot").data(d3.range(180)).enter().append("circle").attr("class","dot")
DOMに関連づいていなかったデータに対してcircleのDOMが生成され、その配列が返ります。DOMノード一覧 = ◯ ◯ ◯ ◯ ◯ ◯ ◯ ◯データのリスト = ◇ ◇ ◇ ◇ ◇ ◇ ◇ ◇この処理の中で、生成したDOMノードに __data__ プロパティを付与してデータを関連付ける処理も行われます。
circleのDOM配列に対してclassを指定します。典型的にはselectAllのセレクションで指定したクラスを指定しますが、そうでなくても構いません。つまり、プログラムの都合で違うものを付与しても構いません。attr関数の戻り値は、thisです。よって、引続きattr関数やstyle関数やtext関数などを使ってDOMの属性をセットアップしていくことが出来ます。
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
enterの理解を深める その2
以下の書き方はD3ではよくあるけれども、どうもしっくりこない・・・
疑問1:何もセレクションされないことが分かっている時にselectAllを真面目に書く必要があるか?→その必要はないです。
疑問2:ノードにクラス指定する事が多いけど、D3ではクラス指定は必須なの?→その必要はないです。単に後々セレクションなどで使えて便利だからやっているだけです
11
svg.selectAll(".dot").data(d3.range(180)).enter().append("circle").attr("class","dot").attr("cx",function(d){return x(d);}).attr("cy",function(d){return y(Math.sin(d * Math.PI / 180));}).attr("r",1);
svg.selectAll().data(d3.range(180)).enter().append("circle").attr("class","dot").attr("cx",function(d){return x(d);}).attr("cy",function(d){return y(Math.sin(d * Math.PI / 180));}).attr("r",1);
svg.selectAll().data(d3.range(180)).enter().append("circle").attr("cx",function(d){return x(d);}).attr("cy",function(d){return y(Math.sin(d * Math.PI / 180));}).attr("r",1);
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
既存のDOMノードの数がデータより多い状況で、既存DOMノードを削除する(D3のexit関数を理解する)
既存のDOMのセレクション結果とデータを関連付けた後、既存DOMノード側の数が多く、余った場合に、その余ったDOMノードを対象に処理をする事が可能です。
12
var svg = d3.select("#d3area").append("svg").attr("width", 360).attr("height", 100),x = d3.scale.linear().domain([0,360]).range([0,360]),y = d3.scale.linear().domain([-1,1]).range([90,10]);
for (var deg = 0; deg < 180; deg++) { // 今後の説明のために描画は180度までとするsvg.append("circle").attr("class","dot") // circle一つ一つに「dot」クラスを設定
.attr("cx", x(deg)).attr("cy", y(Math.sin(deg * Math.PI / 180))).attr("r", 1);}// 上記circleにデータを付与する。DOMノードは余る。var data = d3.range(90); // [0,1, ... ,89]svg.selectAll(".dot").data(data).exit().transition() // DOMの書き換えをアニメーションさせる指定。この指定がなければ一瞬で変化する.duration(function(_,i){return 10*i;}) // アニメーション速度の指定.style("opacity",0).remove();
データがアサインされなかったDOMノードが徐々に消えます
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
軸の導入
13
svg {border: dashed 1px blue; font-size: 12px;}.axis path,.axis line {
fill: none;stroke: #000;shape-rendering: crispEdges;
}
var data = d3.range(360).map(function(deg){return {x:deg,y:Math.sin(deg * Math.PI / 180)};});var margin = {top: 15, right: 10, bottom: 25, left: 40},
width = 500 - margin.left - margin.right,height = 200 - margin.top - margin.bottom,x = d3.scale.linear().domain(d3.extent(data.map(function(d){return d.x;}))).range([0,width]),y = d3.scale.linear().domain(d3.extent(data.map(function(d){return d.y;}))).range([height,0]),g = d3.select("#d3area").append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
g.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(d3.svg.axis().scale(x).orient("bottom"));
g.append("g").attr("class", "y axis").call(d3.svg.axis().scale(y).orient("left"));
g.selectAll(".dot").data(data).enter().append("circle").attr("class","dot").attr("cx", function(d){return x(d.x);}).attr("cy", function(d){return y(d.y);}).attr("r", 1);
SVGのtransformの指定をすると、座標系をずらす事が可能です
軸の描画に関しては、D3がそのような図形描画処理を提供してくれています
d3.extentを利用すると、データの最小値と最大値が算出可能です。
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
おまけ:sinカーブをcosカーブに「にゅーっ」と変形
14
var data = d3.range(360).map(function(deg){return {x:deg,y:Math.sin(deg * Math.PI / 180)};});var margin = {top: 15, right: 10, bottom: 25, left: 40},
width = 500 - margin.left - margin.right,height = 200 - margin.top - margin.bottom,x = d3.scale.linear().domain(d3.extent(data.map(function(d){return d.x;}))).range([0,width]),y = d3.scale.linear().domain(d3.extent(data.map(function(d){return d.y;}))).range([height,0]),g = d3.select("#d3area").append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
g.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(d3.svg.axis().scale(x).orient("bottom"));
g.append("g").attr("class", "y axis").call(d3.svg.axis().scale(y).orient("left"));
g.selectAll(".dot").data(data).enter().append("circle").attr("class","dot").attr("cx", function(d){return x(d.x);}).attr("cy", function(d){return y(d.y);}).attr("r", 1);
var newData = d3.range(360).map(function(deg){return {x:deg,y:Math.cos(deg * Math.PI / 180)};});g.selectAll(".dot").data(newData).transition().duration(function(d){return 500+x(d.x);}).delay(function(d){return 10*x(d.x);}).attr("cy", function(d){return y(d.y);});
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
イベント処理
D3はjQueryと同じようにon関数によって各種イベントを扱うことが出来るようになっています。ここではclickイベントを拾って円の形を変える例を示します。
15
var svg = d3.select("#d3area").append("svg").attr("width",100).attr("height",100);svg.append("circle").attr("cx",50).attr("cy",50).attr("r",10)
.on("click",function(){svg.selectAll("circle")
.transition().duration(300).attr("r",50)
.transition().duration(300).attr("r",10)
.transition().duration(300).attr("cx",80)
.transition().duration(300).attr("cx",20)
.transition().duration(900).attr("cx",50);});
書き方はjQueryと同じ
ちなみに・・・
D3はDOMノードの処理やイベント処理を記述可能ですので、jQueryの代替として使う人も増えてきています。jQueryと役割が重なる部分が多いのです。
ちなみにD3はSVGに特化したものではなく、実はHTMLのDOMノードやイベント全般が扱えます。
クリック
色々動く
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
イベント処理 その2
イベントの処理例として、もう一つ、mousemoveイベントを拾って輪を描画するサンプルを紹介します。
16
var color = d3.scale.category20c(),i = 0;
var svg = d3.select("#d3area").append("svg").attr("width",640).attr("height",480).style("pointer-events", "all").on("mousemove", function(){
svg.append("circle").attr("cx", d3.event.x).attr("cy", d3.event.y).attr("r", 1e-6).style("fill","none").style("stroke", color(i++)).style("stroke-opacity", 1).transition().duration(5000).ease(Math.sqrt).attr("r", 100).style("stroke-opacity", 1e-6).remove();
});
書き方はjQueryと同じ
[処理内容]マウスポインタが動く際に発生するイベントに反応して動きます。
circleを作って、色はd3.scale.category20cという色生成関数で生成した色を指定しています。
transition指定をして徐々に半径が広がるように、また、不透明度(opacity)が下がるように、つまり透明になっていくように指定し 、最後に透明になった後、DOMノードも消えるように指定しています。
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
散布図
散布図の実現は簡単です。単純に軸を描画し、circleで点を描くだけです。
17
var data = [[3,5],[7,2],[1,10],[9,12],[5,13]];
var margin = {top: 15, right: 10, bottom: 25, left: 40},width = 500 - margin.left - margin.right,height = 200 - margin.top - margin.bottom,x = d3.scale.linear().domain(d3.extent(data,function(d){return d[0];})).range([0,width]),y = d3.scale.linear().domain(d3.extent(data,function(d){return d[1];})).range([height,0]),g = d3.select("#d3area").append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
g.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(d3.svg.axis().scale(x).orient("bottom"));
g.append("g").attr("class", "y axis").call(d3.svg.axis().scale(y).orient("left"));g.selectAll().data(data).enter().append("circle").attr("cx",function(d){return x(d[0]);}).attr("cy",function(d){return y(d[1]);}).attr("r",5);
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
散布図で見るD3(およびJavaScript)のパフォーマンス
100,000個(10万個)のデータを散布図でプロットしました。HTMLの描画部分も含めて、全体で約2秒でプロット可能でした。なお、現在関わっているプロジェクトでは約10万件のデータをJavaScriptのメモリー上に保持し、さまざまな処理をしていますが、やはり問題にはなっていません。なお、100万件程度になると、インタラクティブな操作が出来なくなるほど遅くなると思われます。
18
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
円グラフ
以下に円グラフの例を示します。
19
var data = [["A",4],["B",3],["C",2],["D",1]];
var width = 400,height = 400,radius = 200,arc = d3.svg.arc().innerRadius(0.3*radius).outerRadius(0.9*radius);
var c = d3.scale.category20c();var g = d3.select("#d3area").append("svg").attr("width",width).attr("height",height)
.append("g").attr("transform","translate(" + width / 2 + "," + height / 2 + ")")
.selectAll().data(d3.layout.pie().sort(null).value(function(d){return d[1];})(data))
.enter().append("g").attr("class","arc");g.append("path").style("fill",function(_,i){return c(i);}).style("stroke","black").attr("d",arc);g.append("text").attr("transform",function(d){return "translate(" + arc.centroid(d) + ")"; }).attr("dy","0.35em").style("text-anchor","middle").style("font-size","30px").text(function(d){return d.data[0];});
ここが円グラフの弧の部分の形を生成するコア部分
ここは円グラフ用のデータを作るために必要な部分。ここも重要。
<sunburst>
円グラフのプリミティブである円弧を組合せていくと、sunburstというモダンな表現も可能になります。
プリミティブの円弧
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
Chord diagram に挑戦
以降のスライドでChord diagramを扱ってみます。通常目にする散布図、折れ線グラフ、棒グラフ、円グラフなどとは違うものです。見た目は以下の様なものです。
20
このような円弧は円グラフ(パイチャート)で使った円弧の表現を応用します。ラベル部分も円グラフと同じです。オフセットが異なるだけです。
Chord diagram 特有のこの円弧は、D3が提供する d3.svg.chord() で描画可能なものになります。
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
Chord diagram: bigramをChord diagramで表現してみます
Chord diagramは、相互に数値的な関係のあるノード間の強さを表現します。なかなか具体例がないと説明出来ないため、ここではbigramを例に用いて説明します。
bigram(バイグラム)とは
英単語の「the」を用いて説明します。theはtの後にhが来て、hの後にeが来ます。こうなる確率は以下の通りです。このような、英文字間の遷移確率データをbigramと呼びます。
英語でtの後にhの来る確率=33%
英語でhの後にeの来る確率=52%
bigramのデータを表形式で表現することも出来ます。たとえば以下のようなものになります。
bigramを3次元グラフで描画することも可能です。(下記はExcelで描画したもの)
21
t h e33% 52%
aが来る bが来る cが来る dが来る eが来る ・・・
aのあとに 20 3279 4961 5105 65
bのあとに 1754 164 112 13 6150
cのあとに 8507 8 865 50 5204
dのあとに 3254 133 129 1304 13219
・・・
← 数字は、ある英文テキストにおける出現回数
← 横軸は「aのあとに」などの最初の文字奥行き方向の軸は「bが来る」などの次の文字棒の高さは、そのようなパターンの出現回数この中で飛び抜けて高い棒は「th」のパターン。
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
Chord diagram: bigramはネットワーク構造をしたデータ
bigramは、「エッジに数値の付いた有向サイクリックグラフ」です。ここで言うグラフは、ネットワーク構造の事です。bigramを素直にノードとエッジを用いたグラフ構造として表現すると以下のようになります。なお、すべてのノードについて記述するとクモの巣状になってしまうので、4文字のみ取り上げました。
22
t h e51626 34984
21256
3004 312
13969
i186341
1233
6159
00
数字は、あるドキュメントでそのような遷移が出現した回数。本当にaからzまでのすべての単語について記述するとなると、1つのノードからaからzまでの26ノードへの関連が発生する。本当に描画してしまうとグチャグチャに・・・→ そこでChord diagram!
3177
2
71
3893
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
Chord diagram: D3を用いてbigramをChord diagramで表現する
以下はD3を用いてbigramをChord diagramで表現してみたものです。
23
たとえば 「t」 のノードにフォーカスすると、アルファベット「t」の次に来やすいアルファベットに対して線が引かれます。
ノード「t」に接続している側の線の太さは、遷移のしやすさを意味します。たとえば「t」の次は「h」が来やすいため太い線として表現されています。
逆に「h」のノードから「t」のノードに行く事を考えると、「h」の側は細く描画されていることが分かります。そのため「ht」のような並びは英語ではレアであることが分かります。
太い
細い
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
Chord diagram のソースコード
24
var width = 500, height = 500,innerRadius = 0.35 * width,outerRadius = 1.2 * innerRadius;
var color = d3.scale.category20();// 座標の原点をSVGのエリアの中心にvar svg = d3.select("body").append("svg").attr("width", width).attr("height", height)
.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");// chord用データを生成var chord = d3.layout.chord().padding(.05).sortSubgroups(d3.descending).matrix(matrix);// chord特有の弧の部分を描画svg.selectAll().data(chord.chords).enter()
.append("path").attr('class','chord-path')
.attr("d", d3.svg.chord().radius(innerRadius))
.style("fill",function(d){return color(d.target.index);})
.style('stroke',function(d){return color(d.target.index);})
.style('stroke-width','1').style("opacity", 0.8).style("stroke-opacity",1);// 周囲の円弧の土台を作成してデータを投入var g = svg.selectAll().data(chord.groups).enter().append("g").attr('class','chord');// 周囲の円弧を描画g.append("path").style("fill", function(d){return color(d.index);})
.style("stroke", function(d){return color(d.index);})
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
.on("mouseover", fade(0.1,0.1))
.on("mouseout", fade(0.8,1));function fade(opacity,s_opacity) {
return function(g, i) {svg.selectAll(".chord-path")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition().style("opacity",opacity).style("stroke-opacity",s_opacity);};
}// 周囲のテキストを描画g.append("text")
.attr("dy","0.35em").style("text-anchor","middle").style("font-size","14px")
.text(function(d,i){return String.fromCharCode('a'.charCodeAt()+i);})
.attr("transform",function(d){return "translate(" + d3.svg.arc().innerRadius(outerRadius)
.outerRadius(1.15*outerRadius).centroid(d) + ")";});
ULS Copyright © 2013 UL Systems, Inc. All rights reserved.
他にも色々・・・。data visualization の世界は研究分野の1つなので本当に沢山のアイディアがあるようです。 D3を使えば、その新しいアイディアをより楽に実装できます。
25
http://bl.ocks.org/mbostock/4063269 http://bl.ocks.org/mbostock/raw/1044242/ http://bl.ocks.org/mbostock/4063570
http://bl.ocks.org/mbostock/1062288 http://bl.ocks.org/mbostock/950642 http://bl.ocks.org/mbostock/4063582 http://bl.ocks.org/mbostock/4060366
http://bl.ocks.org/mbostock/4063530
[データビジュアライゼーションのセオリー:人は可視化されたデータをどう見るか]人は、オブジェクトの面積に敏感です。データのスカラー値を面積に比例させると、人はデータ間の相対的な比較を感覚的に行えます。古典的にはこのような人の性質を用いて円グラフや棒グラフを作っていました。
人は、色をある程度認識できます。しかし、色の認識は面積ほどではありません。人に傾向を読み取らせるために有向に使える場合があります。たとえばヒートマップは色の使い方の有名な例です。
人は連続的な変化を好みます。そのため、似たデータは近くに配置するなどの工夫が有効な場合があります。また、アニメーションも連続的な変化の一種なので、アニメーションもデータを理解させるツールとして使うと効果的なことがあります。