$30 off During Our Annual Pro Sale. View Details »

d3.js demonstration

d3.js demonstration

muddydixon

March 28, 2023
Tweet

More Decks by muddydixon

Other Decks in Programming

Transcript

  1. σʔλՄࢹԽͱ͸ The main goal of data visualization is its ability

    to visualize data, communicating information clearly and effectivelty. Vitaly Friedman $ σʔλՄࢹԽͷ໨త͸ɺσʔλΛՄࢹԽ͠ɺ ৘ใΛ໌͔֬ͭޮ཰తʹ఻͑Δ͜ͱͰ͋Δ $
  2. %KTͱ͸d%%%d <      > < 

        > 正規化 図形化
  3. %KTͱ͸d%%%d <      > < 

        > 正規化 図形化 %BUB%SJWFO %PDVNFOUT
  4. %KTͱ͸d%%%d  4FQBM-FOHUI 4FQBM8JEUI 1FUBM-FOHUI 1FUBM8JEUI 4QFDJFT   

      TFUPTB      TFUPTB      TFUPTB      TFUPTB      TFUPTB      WFSTJDPMPS      WFSTJDPMPS      WFSTJDPMPS      WFSTJDPMPS      WFSTJDPMPS      WJSHJOJDB      WJSHJOJDB      WJSHJOJDB      WJSHJOJDB      WJSHJOJDB
  5. %KTͱ͸d%%%d  4FQBM-FOHUI 4FQBM8JEUI 1FUBM-FOHUI 1FUBM8JEUI 4QFDJFT   

      TFUPTB      TFUPTB      TFUPTB      TFUPTB      TFUPTB      WFSTJDPMPS      WFSTJDPMPS      WFSTJDPMPS      WFSTJDPMPS      WFSTJDPMPS      WJSHJOJDB      WJSHJOJDB      WJSHJOJDB      WJSHJOJDB      WJSHJOJDB
  6. %KTͱ͸d%%%d  4FQBM-FOHUI 4FQBM8JEUI 1FUBM-FOHUI 1FUBM8JEUI 4QFDJFT   

      TFUPTB      TFUPTB      TFUPTB      TFUPTB      TFUPTB      WFSTJDPMPS      WFSTJDPMPS      WFSTJDPMPS      WFSTJDPMPS      WFSTJDPMPS      WJSHJOJDB      WJSHJOJDB      WJSHJOJDB      WJSHJOJDB      WJSHJOJDB %BUB%SJWFO %PDVNFOUT
  7. %KTͷྲྀΕ)5.- جຊ͸͜ΕͰͣͬͱ͍͖·͢ <!doctype html> <html> <head> <meta charset="utf-8"> <title>D3.js on

    GTUG Girls Tokyo</title> <style> .axis path, .axis line { fill: none; stroke: grey; } </style> </head> <body> <script type="text/javascript" src="//code.jquery.com/jquery-2.1.1.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/ d3.min.js"></script> <script type="text/javascript" src="./src/main.js"></script> </body> </html>
  8. %KTͷྲྀΕ8FC 8FC্͓͛ͯ͘ͱ"KBYͱ͔΋࢖͑ͯศརͰ͢ python -m http.server 5000 python -m SimpleHTTPServer 5000

    plackup -MPlack::App::Directory -e 'Plack::App::Directory->new(root => ".")->to_app' ruby -run -e httpd -- --port=5000 . ruby -rwebrick -e 'WEBrick::HTTPServer.new(:Port => 5000, :DocumentRoot => ".").start' http://blog.kamipo.net/entry/2013/02/20/122225
  9. %KTͷྲྀΕσʔλ σʔλΛ࡞Γ·͢ ͜͏͍͏ͷ࡞͓ͬͯ͘ͱศར // データ生成ユーティリティ var dataGenerator = function(len, gen){

    var array = [], i = 0; for(i = 0; i < len; i++){ array.push(gen ? gen(i) : i); } return array; }; // データ生成 var data = dataGenerator(100, function(i){ return { id: i, name: "name "+ i, x: 0|Math.random() * width, y: 0|Math.random() * height }; });
  10. %KTͷྲྀΕ47( ౔୆ TWH ࡞Γ // var svg = d3.select("body").append("svg") .attr({width:

    WIDTH, height: HEIGHT}); var main = svg.append("g") .attr({ width: width, height: height, transform: “translate("+margin+","+margin+")" }); 初出 * (selection) d3.select * (selection) selection.append * (selection) selection.attr
  11. %KTͷྲྀΕσʔλͱ%0.ͷඥ෇͚ σʔλͷඥ෇͚ var circle = // 現状の取得 main.selectAll("g.circle") // データのヒモ付

    .data(data) // データ > 現状(DOM)の部分を取得 .enter() // "g"要素を追加し、classをセット .append("g").attr({"class": "circle"}); 初出 * (selection) selection.selectAll * (selection) selection.data * (selection) selection.enter
  12. %KTͷྲྀΕDPMPS ৭Λऔಘͯ͠ΈΑ͏ var color = d3.scale.category20(); circle.append("circle").attr({ r: 10, cx:

    function(d){ return d.x; }, cy: function(d){ return d.y; }, fill: function(d, i){ return color(i); } }); 初出 * (scale) d3.scale.category20
  13. 4FMFDUJPO ྫ͑͹ ඥ෇͚ͨσʔλ طଘͷ%0. FYJU TFMFDUJPO FOUFS ଐੑɾελΠϧɾςΩ ετͷมߋͳͲΛߦ͏ ৽نʹ%0.Λ௥Ճ

    ͠ɺಉ࣌ʹଐੑɾελ ΠϧɾςΩετΛηο τ͢Δ طଘͷ%0.Λ࡟আ͠ ͨΓɺಁ໌౓Λ্͛ͨ ΓΛߦ͏
  14. ๮άϥϑ σʔλ࡞੒ // データ生成 var data = dataGenerator(10, function(i){ return

    { id: i, name: "name "+ i, // height よりも大きい! y: 0|Math.random() * 1000 }; });
  15. ๮άϥϑ σʔλ੔ཧ ΞΫηαΛࢦఆ͢Δ͜ͱͰɺ഑ྻ͔Βॊೈ ʹ࠷େɾ࠷খɾൣғΛऔΓग़ͤΔ ଞʹ΋ฏۉͱ͔தԝ஋ͳͲ΋Մೳ // データの調査 var min =

    d3.min(data, function(d){ return d.y;}), max = d3.max(data, function(d){ return d.y;}), extent = d3.extent(data, function(d){ return d.y;}); アクセサ 初出 * (number) d3.min / d3.max * ([number, number]) d3.extent
  16. ๮άϥϑ εέʔϧ࡞੒௒ॏཁʂTDBMFΛ੍͢͜ͱඞਢ var x = d3.scale.ordinal() .domain(data.map(function(d){ return d.id; }))

    .rangeBands([0, width], 0.2), y = d3.scale.linear() .domain([0, max]) .range([height, 0]); // [0, height]じゃない! EPNBJO ೖྗ SBOHF ग़ྗ ͷม׵ RVBOUJUBUJWFPSEJOBMͷछྨ RVBOUJUBUJWF͸ʮൣғൣғʯ PSEJOBM͸ʮ*%ൣғʯ ൣғ৘ใͷఏڙ 初出 * (scale) d3.scale.linear / d3.scale.ordinal
  17. ๮άϥϑ TDBMFͷSBOHF͕< IFJHIU>Ͱ͸ͳ͘ <IFJHIU >ͩͬͨཧ༝ ϒϥ΢β্Ͱ͸ࠨ্͕    ӈʹߦ͚͹

    Y  ɺԼʹߦ͚͹  Z  େ͖͍஋΄Ͳʮߴ͍ʯʮখ͍͞஋ʯ ߴ͞ͷ΄͏Λௐ੔ʮIFJHIUߴ͞ʯ
  18. ๮άϥϑ ཁૉͷඳըͷ४උ var bar = // 現状の取得 main.selectAll("g.bar") // データのヒモ付

    .data(data) // データ > 現状(DOM)の部分を取得 .enter() // "g"要素を追加し、classをセット .append("g") .attr({ "class": "bar", transform: function(d){ // コツ: 位置合わせをやっておく return “translate("+x(d.id)+","+height+")"; } });
  19. ๮άϥϑ ཁૉͷඳըͷ४උ var bar = // 現状の取得 main.selectAll("g.bar") // データのヒモ付

    .data(data) // データ > 現状(DOM)の部分を取得 .enter() // "g"要素を追加し、classをセット .append("g") .attr({ "class": "bar", transform: function(d){ // コツ: 位置合わせをやっておく return “translate("+x(d.id)+","+height+")"; } }); 描画上の一番下に合わせる height
  20. ๮άϥϑ ཁૉͷඳը bar.append("rect").attr({ width: x.rangeBand(), height: function(d){ return height -

    y(d.y); }, y: function(d){ return y(d.y) - height; }, fill: "red" }); // ラベルも付けておきます bar.append("text").text(function(d){ return d.y;});
  21. ๮άϥϑ ࣠ͷ࡞੒ var xaxis = main.append("g").call(d3.svg.axis().scale(x)).attr({ "class": "axis", transform: "translate(0,"+height+")"

    }); var yaxis = main.append(“g").call(d3.svg.axis().scale(y).orient("left")) .attr({ "class": "axis" }); 初出 * (svg) d3.svg.axis * (selection) selection.call
  22. 47(Ͱ࢖͑Δλά TWH H EFGT TZNCPM VTF QBUI MJOF SFDU DJSDMF

    FMMJQTF QPMZMJOF QPMZHPO ! ͋ͱ͸ϑΟϧλʔपΓ͕ͨ͘͞Μ͋Γ·͕͢ ΄ͱΜͲ࢖͏ػձ͸͋Γ·ͤΜ
  23. 1"5) ͢΂ͯͷඳըཁૉ HSBQIJDTFMFNFOU ͸͜ΕͷBMJBT ͩͱߟ͑Δ͜ͱ͕Ͱ͖·͢ EଐੑͰσʔλύεจࣈྻΛ౉͢͜ͱͰඳըͤ͞·͢ FH.--[   ͔Β։࢝

    . ͠ɺ   ʹҠಈ - ɺ   ʹҠಈͯ͠ɺด࿏Λ࡞Δ [  pMMଐੑด࿏ͷதΛࢦఆͨ͠৭ͰృΓͭͿ͢ TUSPLFଐੑύεΛࢦఆͨ͠৭Ͱඳ͘ TUSPLFXJEUIଐੑύεͷઢͷଠ͞Λࢦఆ͢Δ
  24. $*3$-& 3&$5 -*/& &--*14& DJSDMFཁૉ Sଐੑ൒ܘΛࢦఆ DYDZଐੑத৺ͷYZ࠲ඪΛࢦఆ SFDUཁૉ XJEUIIFJHIUଐੑۣܗͷ෯ߴ͞Λࢦఆ YZଐੑۣܗͷࠨ্ͷҐஔΛࢦఆ

    SYSZଐੑؙ֯ͷପԁ൒ܘΛࢦఆ MJOFཁૉ YYZZଐੑ୹఺ͷ࠲ඪΛࢦఆ FMMJQTFཁૉ DYDZଐੑପԁͷத৺Ґஔͷ࠲ඪΛࢦఆ SYSZଐੑପԁͷ൒ܘΛࢦఆ
  25. 5SBOTGPSNଐੑ USBOTGPSNଐੑʹ͸จࣈྻͰԼهΛࢦఆ͢Δ ͜ͱ͕Ͱ͖·͢ NBUSJYมܗߦྻ ཁૉ Λࢦఆ USBOTMBUFฒߦҠಈFHUSBOTGPSN 9 : 

    SPUBUFճసFHSPUBUF   TLFX9TLFX:܏ࣼ ฏߦ࢛ลܗΈ͍ͨʹ௵ ͢ FHTLFX9   TDBMF֦େॖখFHTDBMF  TDBMF  
  26. σʔλ࡞੒ // データ生成 var data = dataGenerator(10, function(i){ var now

    = Date.now(); return { id: i, name: "name "+ i, x: new Date(now + 60000 * i), // 一分おき y: 0|Math.random() * 1000 }; }); ંઢάϥϑ
  27. εέʔϧ࡞੒ // スケールの生成 var x = d3.time.scale() .domain(d3.extent(data, function(d){ return

    d.x; })) .range([0, width]), y = d3.scale.linear().domain([0, max]).range([height, 0]); ࣌ؒʹಛԽͨ͠εέʔϧ ࣗಈͰ࣌ؒͷ۠ؒΛௐ੔ͯ͘͠ΕΔͨΊɺBYJTʹར༻͠ ͨ࣌ʹศར ೥ɺ࢛൒ظɺ݄ɺ೔ɺ࣌ɺ෼ɺͳͲॊೈʂ 初出 * (scale) d3.time.scale ંઢάϥϑ
  28. ཁૉඳըϢʔςΟϦςΟ var line = d3.svg.line() .x(function(d){ return x(d.x); }) .y(function(d){

    return y(d.y); }); // PATHDATA=line([{x: X, y:Y},{}]) 配列を受けて、PathDataの文字列を返す ྨࣅͷTWHͱͯ͠BSFB BSD EJBHPOBMͳ Ͳ͕͋Δ 初出 * (function) d3.svg.line ંઢάϥϑ
  29. ඳը var serie = // 現状の取得 main.append("path") // データのヒモ付 .datum(data)

    .attr({ d: line, fill: "none", stroke: color(0) }); 初出 * (selection) selection.datum path要素に対して、配列を紐付ける ંઢάϥϑ
  30. σʔλͷ४උ ΦϒδΣΫτͷ഑ྻͷ഑ྻ // データ生成 var series = dataGenerator(3, function(i){ return

    { name: "series " + i, data: dataGenerator(10, function(j){ var now = Date.now(); return { id: j, name: "name "+ j, x: new Date(now + 60000 * j), // 一分おき y: 0|Math.random() * 1000 }; }) }; }); ෳ਺ͷંઢάϥϑ
  31. σʔλͷൣғΛऔಘ ճNJONBYΛ࢖͏ var maxY = d3.max(series, function(serie){ return d3.max(serie.data, function(d){

    return d.y;}); }); var minX = d3.min(series, function(serie){ return d3.min(serie.data, function(d){ return d.x;}); }), maxX = d3.max(series, function(serie){ return d3.max(serie.data, function(d){ return d.x;}); }); ෳ਺ͷંઢάϥϑ
  32. ඥ෇͚ͯඳը var serie = // 現状の取得 main.selectAll("g.serie") // データのヒモ付 .data(series)

    .enter() .append("g").attr({"class": "serie"}); ! serie.append("path").attr({ d: function(d){ return line(d.data); }, fill: "none", stroke: function(d){ return color(d.name); } }); ෳ਺ͷંઢάϥϑ
  33. 0OFNPSFUIJOH ֤ϙΠϯτʹυοτͱ਺஋Λ var serie = // 現状の取得 main.selectAll("g.serie") // データのヒモ付

    .data(series) .enter() .append("g").attr({ "class": "serie", fill: function(d){ return color(d.name); } }); 子要素すべてのfillを指定
  34. ඥ෇͚ͯඳը var point = serie.selectAll("g.point") .data(function(d){ return d.data;}).enter() .append("g").attr({ "class":

    "point", transform: function(d){ return "translate("+x(d.x)+","+y(d.y)+")"; } }); point.append("circle").attr({r: 5}); point.append("text").text(function(d){ return d.y; }).attr({dy: -8}); 0OFNPSFUIJOH
  35. "OJNBUJPO ߋ৽ॲཧؔ਺ લ൒ var updateLine = function(){ // スケールのドメイン更新 x.domain(d3.extent(data,

    function(d){ return d.x; })); var max = d3.max(data, function(d){ return d.y;}); y.domain([0, max]); ! // 折線の変形アニメーション serie.transition() .attr({ d: line }); ! これでアニメーションを利用できる!
  36. "OJNBUJPO ߋ৽ॲཧؔ਺ த൫ // 新たにデータが増えたのでselection撮り直し var point = main.selectAll("g.point").data(data); //

    データ追加分の処理 var newpoint = point.enter().append(“g") .attr({"class": "point", fill: color(0), transform: function(d){ return "translate("+x(d.x)+","+y(d.y)+")"; } }); newpoint.append("circle").attr({ r: 5 }); newpoint.append("text").text(function(d){return d.y;}).attr({dy:-8}); // 既存データのアニメーション point.transition().attr({ transform: function(d){ return "translate("+x(d.x)+","+y(d.y)+")"; } });
  37. σʔλՄࢹԽͷϚϯτϥ Overview First, Zoom and Filter, Then Details-on-Demand Overview First,

    Zoom and Filter, Then Details-on-Demand Overview First, Zoom and Filter, Then Details-on-Demand Ben Shneiderman $