細かすぎて伝わらないd3 ver.4の話

65
D3 v4 Created by Shimizu Masayuki

Upload: -

Post on 15-Jan-2017

1.090 views

Category:

Data & Analytics


0 download

TRANSCRIPT

細かすぎて伝わらないD3 v4の話Created by Shimizu Masayuki

プロフィール

清水正行 FB TW

群馬県高崎市在住

GUNMA GIS GEEK - Blog2015/11 - 転職毎日D3.jsを書いています

コンプライアンスにより社名は秘匿。(申請書めんどい)

ご注意

文字とコードを読むプレゼンです。

重要なのは初めの10分くらいです。

質問があれば逐次受けつます。

トイレとかにいってもOKです。

気楽にやりましょう。

では、はじめます。

6月28日、データビジュアライゼーションライブラリとして有名な「D3.js」がメジャーバージョンアップしました。

Releasesv3

2012年12月22日 - 3.0.02016年5月5日 - 3.5.17

v4

2016年1月5日 - 4.0.0 α2016年6月24日 - 4.0.0 rc2016年6月28日 - 4.0.02016年10月27日 - 4.3.0

メジャーバージョンアップによって、どのような変化があったのか。

阿鼻叫喚

結構、大きな変更がありました。

v4になって変わったところ。

モジュール化

D3 v3は一つの巨大なライブラリでしたが、v4では機能ごとのモジュールに変更されました。モジュールはそれ単体でインストール

して利用することができます。

npm install d3‐array

<script src="https://d3js.org/d3‐array.v1.min.js"></script>

webpackやrollup.jsを使って、必要なモジュールだけを選んでカス

タムビルドすることもできるようになりました。

でも、モジュールが別モジュールに依存している場合がある。

依存関係

名前空間

モジュルー化に伴い名前空間の見直しが行われています。

スケール

d3.scale.linear   ‐>  d3.ScaleLineard3.scale.sqrt    ‐>  d3.scaleSqrt

レイアウト

d3.layout.cluster  ‐>  d3.clusterd3.layout.hierarchy  ‐>  d3.hierarchyd3.layout.pack   ‐>  d3.pack

つまり?

全滅

全滅

でっでも、ドットが抜けただけでしょ?

まったく別名になったものもあるよ!

スケール

d3.scale.ordinal().rangePoints()  ‐> d3.scalePoint() d3.scale.ordinal().rangeBand()    ‐> d3.scaleBand() 

レイアウト

d3.layout.force()   ‐> d3.forceSimulation()

Axis

d3.svg.axis().orient('left')    ‐>  d3.axisLeft()

細かい仕様の変更もある

オブジェクトリテラルを渡してのアップデートはできなくなった

v3 enable

d3.select("cicle")  .attr({    "cx": 10,    "cy": 20,    "r":10  })

v4 only

d3.select("cicle")  .attr("cx", 10)  .attr("cy", 20)  .attr("r", 10)

v3 -> v4

細かい変更をあげたらきりがないほどに変わりました。

今のところv4に対応した参考書は、日本で出版されたものはもとより、海外でもほとんどありません。

じゃあ、どうやって学べばいいのさ!

D3 v4を学ぶには?

チェンジログ読め。

CHANGES.md (長い)リファレンス読め。

D3 API Reference (膨大)下記スライドを読め。

D3 V4 - What's new? (103枚)

つらい。

しかし、喜びの声もある。

細かすぎてつたわらない、

ver.4の素晴らしい進化。

d3-selection

より柔軟なセレクター操作が可能に。

セレクター基本操作

//セレクトvar selected = d3.select("circle")

//データバインドselected.data([1, 10, 100, 100])

//足りない要素を追加var appented = selected.enter().append("circle")

//多すぎる要素を削除var deleted = selected.exit().remove()

//アップデートselected.attr("fill", "red")appentede.attr("fill", "red")

セレクタの戻り値が変わりました

v3

> var svg = d3.select("svg")> svg[Array[1]]

v4

> var svg = d3.select("svg")> svg{_groups: Array[1], _parents: Array[1]}

セレクターオブジェクト TIPS> var g = d3.select("svg").selectAll("g")> g.data([100, 200, 300]).enter().append("g")> g{  _groups: Array[1],  _parents: Array[1]}

> g.data([1000, 2000, 3000, 4000])> g{  _groups: Array[1],  _parents: Array[1],   _enter: Array[1],  _exit: Array[1]}

セレクションマージ

セレクタとセレクタをマージして、両方を含む新たなセレクタを

生成できます。

var selected = d3.select("circle")  .data([1, 100, 1000])

var appented = selected.enter().append("circle")

//マージして一括アップデートselected.merge(appented)  .attr("fill", "red")

セレクションフィルター

選択済みのセレクターをフィルタリングしてアップデートを適用

することができます。

var circles = d3.selectAll("circle")

circles.filter(function(d, i) { return i%2 })  .transition()  .attr("cx", 0)

疑似クラスでの指定もできます。

circle.filter(":nth‐child(even)").attr("cx", 0)

d3-queue

非同期通信ユーティリティ

非同期通信をまとめて処理

d3.queue()  .defer(d3.tsv, 'data1.tsv')  .defer(d3.json, 'japan.geojson')  .await(function(error, tsv, geojson) {        console.log(tsv)    console.log(geojson)  })

d3.queue()  .defer(d3.tsv, 'data1.tsv')  .defer(d3.json, 'japan.geojson')  .awaitAll(function(error, results) {    console.log(results)  })

d3-array

便利な配列操作

v3 - d3.nest> var data = [    {name:"a", value:10},     {name:"b", value:20},     {name:"a", value:30}]

> var nested = d3.nest()    .key(function(d){ return d.name })    .map(data)    > nested{a: Array[2], b: Array[1]}

v4 - d3.nest戻り値が、d3コレクションオブジェクトになりました。

> var data = [    {name:"a", value:10},     {name:"b", value:20},     {name:"a", value:30}]

> var nested = d3.nest()    .key(function(d){ return d.name })    .map(data)

> nested{$a: Array[2], $b: Array[1]} // <‐ d3‐collection Object

d3-collection

便利な構造化データ形式

> var map = d3.map({foo:1})

> map.set("bar", 2)    .set("hoge", "aaaa")   > map.get("hoge")aaaa

> map.keys()["foo", "bar", "hoge"]

> map.values()[1, 2, "aaaa"]

> map.entries()[  {"key":"foo","value":1},  {"key":"bar","value":2},  {"key":"hoge","value":"aaaa"}]

d3-stratify

階層データジェネレーター

D3では階層構造のデータを必要とすることが多い

{  "name": "Eve",  "children": [    {      "name": "Cain"    },    {      "name": "Seth",      "children": [        {          "name": "Enos"        },        {          "name": "Noam"        }      ]    },    ・・・}

フラットなオブジェクトを階層構造へ変換する

var data = [  {"name": "Eve",   "parent": ""},  {"name": "Cain",  "parent": "Eve"},  {"name": "Seth",  "parent": "Eve"},  {"name": "Enos",  "parent": "Seth"}]

var stratifyData = d3.stratify()  .id(function(d){ return d.name })  .parentId(function(d){ return d.parent })  (data)

csv -> 構造化データ

id,value1,value2日本,日本.群馬県,日本.群馬県.高崎市,100,2000日本.群馬県.前橋市,200,1000日本.群馬県.桐生市,130,3000日本.埼玉県,日本.埼玉県.さいたま市,140,3200日本.埼玉県.所沢市,210,2400日本.東京都,日本.東京都.千代田区,日本.東京都.千代田区.大手町,220,1000

var stratify = d3.stratify()  .parentId(function(d) {    return d.id.substring(0, d.id.lastIndexOf("."))  }) var root = stratify(tsv)  .sum(function(d) { return d.value1 })

d3-transition

より自在なアニメーションを実現。

イベントリスナー

トランジションの終了時にイベントが発行されるようになりまし

た。

var circle = d3.select("circle")

circle  .transition()  .duration(2000)  .attr("cx", 500)  .on("start", function(){    console.log("start!")  })  .on("end", function(){    console.log("end!")  })  

トランジションチェイン

AからBへ移動して、さらにCへと移動するなど複雑なアニメーシ

ョンが可能に。

var circle = d3.select("circle")

circle  .transition()  .duration(1000)  .attr("cx", 500) <‐‐x軸を移動  .transition()  .duration(1000)  .attr("cy", 500) <‐‐ y軸を移動

共通オブジェクト

メソッドチェインの数を減らし、複数のオブジェクトを同期して

アニメーションするなどが簡単にできるようになりました。

var t = d3.transition()  .duration(2000)    circle.transition(t)  .attr("cx", 1000)    rect.transition(t)  .attr("x", 1000)

イベントも共通のリスナーを設定できる。

var t = d3.transition()  .duration(2000)  .on("end", function(){      alert("トランジション終了")  })    circle.transition(t)  .attr("cx", 1000)    rect.transition(t)  .attr("x", 1000)

d3-geo

地図表示

Fit Extentd

v3 & v4

var projection = d3.geoMercator()  .scale(2000)  .center([139.5, 34])

v4.3.0

var projection = d3.geoMercator()  .fitExtent([[0, 0], [width, height]], geojson)

幅と高さを指定してgeojsonを渡すと、いい感じに表示するための

プロジェクションを返す。

レスポンシブな地図を描画しやすくなった。

canvas レンタリング

もうSVG専用だなんていわせない。

var line = d3.line()    .x(function(d) { return x(d.date); })    .y(function(d) { return y(d.value); })

SVGレンダリング

path.datum(data).attr("d", line)

canvasレンダリング

var context = d3.select("canvas").node().getContext("2d")line.context(context)(data)

地図もcanvasにレンタリグングできます

var context = d3.select("canvas").node().getContext("2d")

var projection = d3.geoMercator()    .fitExtent([[0, 0], [chartWidth, chartHeight]], geojson)

var geoPath = d3.geoPath().projection(projection)  .context(context)

geoPath(geojson) <‐ canvas Draw Maps

d3-force

力学モデルでグラフを描画

v3 Force Layout

おなじみのコード。

var force = d3.layout.force()  .nodes(nodes)  .links(links)  .size([width, height])  .gravity(0.1)  .charge(‐30)  .friction(0.95)  .linkDistance(220)  .linkStrength(1)

v4 Force Simulation

全然違う。

var simulation = d3.forceSimulation()    .force("link", d3.forceLink()      .id(function(d){ return d.index })        .distance(220))    .force("charge", d3.forceManyBody())    .force("center", d3.forceCenter(width / 2, height / 2))    .force("y", d3.forceY(0))    .force("x", d3.forceX(0))

サイズ指定ではなくセンター指定になった

V3

var force = d3.layout.force()  .size([width, height])

V4

var simulation = d3.forceSimulation()    .force("center", d3.forceCenter(width / 2, height / 2))

ノードの衝突判定

新たにノード同士の衝突判定を行う機能が追加

var simulation = d3.forceSimulation()    .force("collide",d3.forceCollide(function(d){       return d.r + 8 //半径に対して8pxのマージンを指定      }).iterations(16) )

座標に最も近いノードを見つける

var simulation = d3.forceSimulation()simulation.find(x, y, r)

マウスイベントと連動させる

svg.on("mousemove", function(){    var findNode = simulation.find(d3.event.x, d3.event.y)    console.log(findNode)})     

ところで。

疲れていませんか?

まとめ

v4になったことで、v3で不便であったことや一部統合性のとれていなかった設計などが修正され、非常に使いやすいライブラリになっています。座標計算等のアルゴリズムも最適化され処理速度も向上しているので、ぜひ使ってみてください。