SELECTED ENTRIES
CATEGORIES
ARCHIVES
SPONSORED LINK
MOBILE
qrcode
LINKS
PROFILE
OTHERS

03
--
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
--
>>
<<
--

Agata's Blog

Python+pandasとD3.jsを使ってデータ解析とデータビジュアライゼーションに挑戦する某エンジニアのBlog
過去30日間に世界で発生した地震を可視化する その2
0
    以前このブログで、アメリカ地質調査所 (USGS: United States Geological Survey) の公開データをもとに、世界で過去30日間に発生したマグニチュード2.5以上の地震を可視化しました。今回はこのときのプログラムを改良して、特定地域で発生した地震のみをデータから抽出して表示できる、インタラクティブなウェブページを作ってみます。

    まず、基礎となるプログラムについては、以前の記事 (過去30日間に世界で発生した地震を可視化する) を参照して下さい。抽出したデータに従ってSVG要素を操作する方法については、「データの変化をSVG要素に反映する」を参考にして下さい。ツールチップの作成については、過去の記事 (ツールチップの作成方法) に詳しく紹介していますので、そちらもあわせて参照してもらえると、より理解が深まります。

    サンプルページ



    // 各ラジオボタン選択時に実行する関数
    function selectAll(){
        updateData("All");
    }
    
    function selectJapan(){
        updateData("Japan");
    }
    
    function selectAlaska(){
        updateData("Alaska");
    }
    
    function selectChile(){
        updateData("Chile");
    }
    
    function selectEcuador(){
        updateData("Ecuador");
    }
    
    // データ配列の初期化
    var dataSet = [];
    
    // X,Y軸を表示できるようにグラフの周囲にマージンを確保する
    var margin = {top: 40, right: 20, bottom: 40, left: 20};
    var width = 1200 - margin.left - margin.right;
    var height = 500 - margin.top - margin.bottom;
    
    // ツールチップ用の設定
    var div = d3.select("body")
      .append("div")
      .attr("class", "tooltip")
      .style("opacity", 0);
    
    // SVGの表示領域を生成
    var svg = d3.select("#MyGraph")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
    
    // 横軸のスケール関数を常に使えるようグローバル変数として定義
    var time_scale;
    
    // 初期状態を描画
    d3.csv("./js/30Days_MagnitudeOver2.5.csv", function(error, dataSet){
      // CSVファイル内の時刻情報をJavaScriptのDate形式にパース
      var format = d3.time.format("%Y-%m-%dT%X.%LZ");
      time_start = new Date(format.parse(dataSet[0].time));
      time_end = new Date(format.parse(dataSet[dataSet.length - 1].time));
      // 時間軸のスケール関数を生成
      time_scale = d3.time.scale()
        .domain([time_start, time_end])
        .range([0, width]);
      // 地震の規模を楕円のサイズで表すためのスケール関数
      var r_scale = d3.scale.linear()
        .domain([0.0,10.0])
        .range([0.0, 200.0]);
      // 散布図を描画
      svg.selectAll("ellipse")
        .data(dataSet, function(d){
          return d.time;
        })
        .enter()
        .append("ellipse")
        .attr("rx", 0)
        .attr("ry", 0)
        .attr("cx", function(d,i){
          var tmp = new Date(format.parse(d.time));
          return time_scale(tmp);
        })
        .attr("cy", 150)
        .attr("stroke","#A0A0A0")
        .attr("fill", function(d,i){
          if (d.place == "Japan"){return "orange"}
          else{return "#D0D0D0"};
        })
        .on("mouseover", function(d){ // マウスオーバー時にツールチップを表示
          div.transition()
            .duration(500)
            .style("opacity", 1.0);
          div.html("Location: " + d.place + "
    Magnitude: " + d.mag) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }) .on("mouseout", function(d) { // マウスアウトするとツールチップを非表示 div.transition() .duration(500) .style("opacity", 0.0); }) .transition() .delay(function(d,i){ return i*2; }) .attr("rx", 5) .attr("ry", function(d,i){ return r_scale(d.mag) }); // 時間軸を描画 svg.append("g") .attr("class", "axis") .attr("transform", "translate(0,320)") .call(d3.svg .axis() .scale(time_scale) .orient("bottom") .tickFormat(function(d,i){ var fmtFunc = d3.time.format("%b-%d"); return fmtFunc(d); }) ) .selectAll("text") .attr("transform", "rotate(45)") .attr("dy", 10) .attr("dx", 10) .style("text-anchor", "start"); }); // ラジオボタン選択時に実行する関数 function updateData(selecter){ d3.csv("./js/30Days_MagnitudeOver2.5.csv", function(error, dataSet){ // ラジオボタンで洗濯された地域のデータのみをCSVから抽出 if(selecter != "All"){ var dataSet = dataSet.filter(function(item, index){ if (item.place == selecter) return true; }); }; // CSVファイル内の時刻情報をJavaScriptのDate形式にパース var format = d3.time.format("%Y-%m-%dT%X.%LZ"); time_start = new Date(format.parse(dataSet[0].time)); time_end = new Date(format.parse(dataSet[dataSet.length - 1].time)); // 地震の規模を楕円のサイズで表すためのスケール関数 var r_scale = d3.scale.linear() .domain([0.0,10.0]) .range([0.0, 200.0]); // 散布図を描画 var ellipse = svg.selectAll("ellipse") .data(dataSet, function(d){ return d.time; }); ellipse.enter() .append("ellipse") .attr("rx", 0) .attr("ry", 0) .attr("cx", function(d,i){ var tmp = new Date(format.parse(d.time)); return time_scale(tmp); }) .attr("cy", 150) .attr("stroke","#A0A0A0") .attr("fill", function(d,i){ if (d.place == "Japan"){return "orange"} else{return "#D0D0D0"}; }) .on("mouseover", function(d){ // マウスオーバー時にツールチップを表示 div.transition() .duration(500) .style("opacity", 1.0); div.html("Location: " + d.place + "
    Magnitude: " + d.mag) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }) .on("mouseout", function(d) { // マウスアウトするとツールチップを非表示 div.transition() .duration(500) .style("opacity", 0.0); }) .transition() .delay(function(d,i){ return i*2; }) .attr("rx", 5) .attr("ry", function(d,i){ return r_scale(d.mag) }); // データと紐づけされていないSVG要素を削除 ellipse.exit() .transition() .duration(1000) .attr("rx", 0) .attr("ry", 0) .remove(); }); };

     
    このエントリーをはてなブックマークに追加
    | D3.js | 00:15 | comments(0) | - | - |
    【D3.js】Chordダイアグラムの作成
    0
      JUGEMテーマ:JavaScript

      D3.js には Chordダイアグラム (Chord diagram) を作成するための d3.layout.chord() というメソッドがあるのですが、そもそも Chordダイアグラム自体に馴染みがないというかたが多いのではないでしょうか。そこで、Chordダイアグラムについて簡単に説明したうえで、D3.jsによる作成方法を紹介します。

      Chordダイアグラムは、グループ間の関連性を図示するときによく使われます。例えば、ある集団を「男性/女性」という2つのグループに分けた場合と、「犬好き/猫好き」という2つのグループに分けた場合について考えてみましょう。現実の世のなかには「犬も猫も好き」な人もいれば、「どちらもキライ!」という人もいますが、ここでは簡単のために、みんな犬か猫のどちらか一方が好きだとします。さて、こうするとこの集団のメンバーは、4 種類 (「男性で犬好き」「男性で猫好き」「女性で犬好き」「女性で猫好き」) に分類できることになりますね。具体的に考えるために、この集団は男性12人と女性4人から成り、それぞれ以下のように犬好きと猫好きに分かれているとします。
       
      犬好き 猫好き 合計
      男性 7 5 12
      女性 1 3 4
      合計 8 8

      さて、このようなデータを可視化するときに、Chord ダイアグラムが役立ちます。Chord ダイアグラムを使うと、こういうグループ間の関連性を一目で把握できるようになります。上の表をもとに、実際に作成したChordダイアグラムが下図になります。



      ここでは図の右側、青色とオレンジ色の円弧が「男性/女性」のグループを表しています。また、左側の緑色と赤色の円弧が「犬好き/猫好き」のグループを表しています。そしてそれぞれのグループ間を橋渡ししている赤と緑の帯が、それぞれの関連性 (「男性で犬好き」など) を表現しています。このように、Chordダイアグラムを使うと、先の表にあるような2種類のグループ間の関連や連携が、見事に1枚の図に表現されていることが分かります。

      サンプルページ

      // D3.jsによる基本10色
      var color = d3.scale.category10();
      
      // Chord diagramの元となるデータ
      var matrix = [
          [0, 0, 7, 5],
          [0, 0, 1, 3],
          [7, 1, 0, 0],
          [5, 3, 0, 0]
      ];
      
      // D3.layout.chord()メソッドを使ってChordダイアグラムを作成
      var chord = d3.layout.chord()
          .padding(.05)
          .matrix(matrix);
      
      var width = 600,
          height = 500,
          innerRadius = Math.min(width, height) * 0.4,
          outerRadius = innerRadius * 1.1;
      
      var svg = d3.select("#MyGraph")
          .attr("width", width)
          .attr("height", height)
          .append("g")
          .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
      
      // Chord diagramを描画
      svg.append("g").selectAll("path")
          .data(chord.groups)
          .enter()
          .append("path")
          .style("fill", function(d){ return color(d.index); })
          .style("stroke", function(d) { return color(d.index); })
          .style("opacity", 0.8)
          .attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
          .on("mouseover", fade(.1)) // マウスをのせた部分を強調表示
          .on("mouseout", fade(0.8)); // マウスを移動すると描画を元に戻す
      
      // 目盛りを描画
      var ticks = svg.append("g").selectAll("g")
          .data(chord.groups)
          .enter()
          .append("g")
          .selectAll("g")
          .data(groupTicks)
          .enter()
          .append("g")
          .attr("transform", function(d) {
            return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
                + "translate(" + outerRadius + ",0)";
          });
      
      ticks.append("line")
          .attr("x1", 1)
          .attr("y1", 0)
          .attr("x2", 5)
          .attr("y2", 0)
          .style("stroke", "#000");
      
      // 目盛りの数字を描画
      ticks.append("text")
          .attr("x", 8)
          .attr("dy", ".35em")
          .attr("transform", function(d) {
              return d.angle > Math.PI ? "rotate(180)translate(-16)" : null;
          })
          .style("text-anchor", function(d) {
              return d.angle > Math.PI ? "end" : null;
          })
          .text(function(d) { return d.label; });
      
      svg.append("g")
          .attr("class", "chord")
          .selectAll("path")
          .data(chord.chords)
          .enter()
          .append("path")
          .attr("d", d3.svg.chord().radius(innerRadius))
          .style("fill", function(d) { return color(d.target.index); })
          .style("opacity", 0.9);
      
      // 描画する目盛りの数字と角度を返す関数
      function groupTicks(d) {
        var k = (d.endAngle - d.startAngle) / d.value;
        return d3.range(0, d.value).map(function(v, i) {
          return {
            angle: v * k + d.startAngle,
            label: i % 5 ? null : v
          };
        });
      }
      
      // mouseover/mouseout 時のSVG要素の透明度を決定する関数
      function fade(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);
        };
      }
      



       
      このエントリーをはてなブックマークに追加
      | D3.js | 23:07 | comments(0) | - | - |
      過去30日間に世界で発生した地震を可視化する
      0
        JUGEMテーマ:JavaScript

        アメリカ地質調査所 (USGS: United States Geological Survey) の公開データをもとに、世界で過去30日間に発生したマグニチュード2.5以上の地震を可視化しました。楕円のサイズが地震の規模を示しています。日本で発生した地震はオレンジで表しています。



        サンプルページ

        var dataSet = [];
        
        // X,Y軸を表示できるようにグラフの周囲にマージンを確保する
        var margin = {top: 40, right: 20, bottom: 40, left: 20};
        var width = 1200 - margin.left - margin.right;
        var height = 500 - margin.top - margin.bottom;
        
        // ツールチップ用の設定
        var div = d3.select("body")
          .append("div")
          .attr("class", "tooltip")
          .style("opacity", 0);
        
        d3.csv("./js/30Days_MagnitudeOver2.5.csv", function(error, dataSet){
          // SVGの表示領域を生成
          var svg = d3.select("#MyGraph")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
          
          // CSVファイル内の時刻情報をJavaScriptのDate形式にパース
          var format = d3.time.format("%Y-%m-%dT%X.%LZ");
          time_start = new Date(format.parse(dataSet[0].time));
          time_end = new Date(format.parse(dataSet[dataSet.length - 1].time));
          
          // 時間軸のスケール関数を生成
          var time_scale = d3.time.scale()
            .domain([time_start, time_end])
            .range([0, width]);
        
          // 地震の規模を楕円のサイズで表すためのスケール関数
          var r_scale = d3.scale.linear()
            .domain([0.0,10.0])
            .range([0.0, 200.0]);    
                
          // 散布図を描画
          svg.selectAll("ellipse")
            .data(dataSet)
            .enter()
            .append("ellipse")
            .attr("rx", 0)
            .attr("ry", 0)
            .attr("cx", function(d,i){
              var tmp = new Date(format.parse(d.time));
              return time_scale(tmp);
            })
            .attr("cy", 150)
            .attr("stroke","#A0A0A0")
            .attr("fill", function(d,i){
              if (d.place == "Japan"){return "orange"}
              else{return "#D0D0D0"};
            })
            .on("mouseover", function(d){ // マウスオーバー時にツールチップを表示
              div.transition()
                .duration(500)
                .style("opacity", 1.0);
              div.html("Location: " + d.place + "
        Magnitude: " + d.mag) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }) .on("mouseout", function(d) { // マウスアウトするとツールチップを非表示 div.transition() .duration(500) .style("opacity", 0.0); }) .transition() .delay(function(d,i){ return i*2; }) .attr("rx", 5) .attr("ry", function(d,i){ return r_scale(d.mag) }); // 時間軸を描画 svg.append("g") .attr("class", "axis") .attr("transform", "translate(0,320)") .call(d3.svg .axis() .scale(time_scale) .orient("bottom") .tickFormat(function(d,i){ var fmtFunc = d3.time.format("%b-%d"); return fmtFunc(d); }) ) .selectAll("text") .attr("transform", "rotate(45)") .attr("dy", 10) .attr("dx", 10) .style("text-anchor", "start"); })
        このエントリーをはてなブックマークに追加
        | D3.js | 11:08 | comments(0) | - | - |
        折れ線グラフをアニメーションで表示する
        0
          JUGEMテーマ:JavaScript

          前回作成したグラフですが、せっかくなので表示に動きをつけてみます。散布図をアニメーションで表示する方法については以前このブログで取り上げました。今回は、折れ線グラフをアニメーションで表示する方法を紹介します。

          D3.js を使った折れ線グラフの作成では、まず d3.svg.line() メソッドを使って path を生成します (詳しくは以前の記事を参照)。この path をアニメーションで表示させるには、stroke-dasharray と stroke-dashoffset という、2種類のプロパティを操作します。前者の stroke-dasharray プロパティを使うと、パスを破線で描画するときの線と間隙の長さを指定することができます。また後者の stroke-dashoffset プロパティは、破線が表示される開始位置を指定することができます。このあたり、言葉で説明してもなかなか分かりにくいので、下記の図を参考にして下さい。



          サンプルページとプログラムは下記の通りです。まず、40行目では getTotalLength() メソッドを使って、27〜37行目で作成した path の長さを取得しています。そのうえで、43〜48行目の部分で、上記2種類のプロパティを操作して、4秒かけてこのパスを表示しています。

          サンプルページ

          var dataSet = [];
          
          // X,Y軸を表示できるようにグラフの周囲にマージンを確保する
          var margin = {top: 40, right: 40, bottom: 40, left: 100};
          var width = 800 - margin.left - margin.right;
          var height = 500 - margin.top - margin.bottom;
          
          d3.csv("./js/WorldPopulation.csv", function(error, dataSet){
          
            // SVGの表示領域を生成
            var svg = d3.select("#MyGraph")
              .attr("width", width + margin.left + margin.right)
              .attr("height", height + margin.top + margin.bottom)
              .append("g")
              .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
              
            // スケール関数を作成。
            var xScale = d3.scale.linear()
              .domain([1000,2200])
              .range([0,width]);
          
            var yScale = d3.scale.log()
              .domain([0.1,10])
              .range([height,0]);
          
            // 折れ線グラフを生成
            var line = d3.svg.line()
              .x(function(d,i){
                return xScale(d.Year);
              })
              .y(function(d,i){
                return yScale(d.Population);      
              });
          
            var path = svg.append("path")
              .attr("class", "line")
              .attr("d", line(dataSet));
            
            // パスの長さを取得
            var pathLength = path.node().getTotalLength();
            
            // "stroke-dasharray" と "stroke-dashoffset" を使ってパスをアニメーションで描画
            path.attr("stroke-dasharray", pathLength + " " + pathLength)
              .attr("stroke-dashoffset", pathLength)
              .transition()
              .duration(4000)
              .ease("linear")
              .attr("stroke-dashoffset", 0);
                
            // 散布図を描画
            svg.selectAll("circle")
              .data(dataSet)
              .enter()
              .append("circle")
              .attr("r", 0)
              .attr("cx", function(d){
                return xScale(d.Year);
              })
              .attr("cy", function(d){
                return yScale(d.Population);
              })
              .attr("fill","orange")
              .transition()
              .delay(function(d,i){
                return 2000+(i*100);
              })
              .attr("r", 5);
          
            // Y軸を描画
            svg.append("g")
              .attr("class", "axis")
              .call(d3.svg
                .axis()
                .scale(yScale)
                .orient("left")
                .tickValues([0.1,0.2,0.4,0.6,0.8,1,2,4,6,8,10])
                .tickPadding(10) // 軸と数値の間のスペースを指定
                .tickFormat(d3.format(".1f"))
            );
            
            // Y軸のグリッドを描画
            svg.append("g")
              .attr("class", "axis")
              .call(d3.svg
                .axis()
                .scale(yScale)
                .orient("left")
                .innerTickSize(-width)
                .tickFormat("")
            );
          
            // X軸を描画
            svg.append("g")
              .attr("class", "axis")
              .attr("transform", "translate(0," + height + ")")
              .call(d3.svg
                .axis()
                .scale(xScale)
                .orient("bottom")
                .innerTickSize(-height) // X軸のグリッドを描画
                .tickPadding(10) // 軸と数値の間のスペースを指定
                .tickFormat(function(d){return d;})
            );
            
            // 凡例
            svg.append("g")
              .attr("class", "axis")
              .append("text")
              .attr("x", -40)
              .attr("y", -20)
              .text("[単位: 10億人]");
          
            svg.append("g")
              .attr("class", "axis")
              .append("text")
              .attr("x", width/2)
              .attr("y", margin.top + height)
              .text("Year");
          
          })
          
          

           
          このエントリーをはてなブックマークに追加
          | D3.js | 22:00 | comments(0) | - | - |
          グラフにグリッド線を追加する
          0
            D3.js でグリッド線を追加する方法はいくつかありますが、今回はいちばん簡単な方法を紹介します。

            それでは前回作成したグラフにグリッド線を追加してみましょう。まず、html ファイルの <style>〜</style> タグ内 (CSS) に以下を追加します。

              .tick line{
                opacity: 0.2;
              }

            次に、軸の目盛り線の長さを指定する innerTickSize() メソッドでマイナスの値を指定して、目盛り線をグラフの内側に伸ばしてしまいます。内側に伸ばす線の長さをグラフのサイズにすることで、グラフのグリッド線にしてしまうというわけです。

            軸の描画とグリッド線の描画を分けることで、軸の目盛り用に表示する数値の数とグリッド線の数とを個別に指定することもできます。今回の例では、Y軸に対して軸の描画 (54行目〜63行目) とグリッド線の描画 (66行目〜74行目) を個別に指定しています。

            軸の目盛り用に表示する数値の数とグリッド線の数とが一致する場合には、軸の描画とグリッド線の描画を同時にすることができます。今回の例では、X軸に対してこれらを同時に実行しています (78行目〜88行目)。

            サンプルページ



            var dataSet = [];
            
            // X,Y軸を表示できるようにグラフの周囲にマージンを確保する
            var margin = {top: 40, right: 40, bottom: 40, left: 100};
            var width = 800 - margin.left - margin.right;
            var height = 500 - margin.top - margin.bottom;
            
            d3.csv("./js/WorldPopulation.csv", function(error, dataSet){
            
              // SVGの表示領域を生成
              var svg = d3.select("#MyGraph")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
                
              // スケール関数を作成。
              var xScale = d3.scale.linear()
                .domain([1000,2200])
                .range([0,width]);
            
              var yScale = d3.scale.log()
                .domain([0.1,10])
                .range([height,0]);
            
              var line = d3.svg.line()
                .x(function(d,i){
                  return xScale(d.Year);
                })
                .y(function(d,i){
                  return yScale(d.Population);      
                });
            
              // 折れ線グラフを生成
              svg.append("path")
                .attr("class", "line")
                .attr("d", line(dataSet));
                
              // 散布図を描画
              svg.selectAll("circle")
                .data(dataSet)
                .enter()
                .append("circle")
                .attr("r", 5)
                .attr("cx", function(d){
                  return xScale(d.Year);
                })
                .attr("cy", function(d){
                  return yScale(d.Population);
                })
                .attr("fill","orange");
            
              // Y軸を描画
              svg.append("g")
                .attr("class", "axis")
                .call(d3.svg
                  .axis()
                  .scale(yScale)
                  .orient("left")
                  .tickValues([0.1,0.2,0.4,0.6,0.8,1,2,4,6,8,10])
                  .tickPadding(10) // 軸と数値の間のスペースを指定
                  .tickFormat(d3.format(".1f"))
              );
              
              // Y軸のグリッドを描画
              svg.append("g")
                .attr("class", "axis")
                .call(d3.svg
                  .axis()
                  .scale(yScale)
                  .orient("left")
                  .innerTickSize(-width)
                  .tickFormat("")
              );
            
              
              // X軸を描画
              svg.append("g")
                .attr("class", "axis")
                .attr("transform", "translate(0," + height + ")")
                .call(d3.svg
                  .axis()
                  .scale(xScale)
                  .orient("bottom")
                  .innerTickSize(-height) // X軸のグリッドを描画
                  .tickPadding(10) // 軸と数値の間のスペースを指定
                  .tickFormat(function(d){return d;})
              );
              
              // 凡例
              svg.append("g")
                .attr("class", "axis")
                .append("text")
                .attr("x", -40)
                .attr("y", -20)
                .text("[単位: 10億人]");
            
              svg.append("g")
                .attr("class", "axis")
                .append("text")
                .attr("x", width/2)
                .attr("y", margin.top + height)
                .text("Year");
            
            })
            
            


             
            このエントリーをはてなブックマークに追加
            | D3.js | 11:08 | comments(0) | - | - |
            世界の総人口の推移を片対数グラフで描画する
            0
              JUGEMテーマ:JavaScript

              急速に増加するようなデータを可視化するには、対数グラフが便利です。今回は D3.js で片対数グラフを作成する方法を紹介します。

              一例として、国連 (United Nations) による世界の総人口の推計値データをグラフ化してみました。まず、データをCSV形式で保存します。

              Year,Population
              1000,0.31
              1250,0.4
              1500,0.5
              1750,0.79
              1800,0.98
              ・・・

              このCSVファイルを読み込んで、片対数グラフで表示するプログラムを作成します。CSVファイルを読み込んでグラフ化する基本的な方法については、過去の記事 (D3.js を使ったCSVファイルの読み込み) を参照して下さい。

              片対数グラフを作成するには、これまで使ってきた線形 (linear) のスケール関数 d3.scale.linear() の代わりに、対数 (log) のスケール関数 d3.scale.log() を使います。それでは具体的な作成例とプログラムを見てみましょう。

              サンプルページ



              対数のスケール関数は、22行目から24行目で作成しています。これだけでも片対数グラフを作成できるのですが、より見やすくするために、いくつか手を入れることにしました。まず、Y軸の目盛りの数字をすべて表示すると数字が重なって見にくくなるため、60行目で ticsValues() メソッドを使って表示する目盛りの数値を指定しています。また、61行目で ticsFormat() メソッドを使って、目盛りの数値を小数点以下1桁で表示するように指定しました。

              var dataSet = [];
              
              // X,Y軸を表示できるようにグラフの周囲にマージンを確保する
              var margin = {top: 40, right: 40, bottom: 40, left: 100};
              var width = 800 - margin.left - margin.right;
              var height = 500 - margin.top - margin.bottom;
              
              d3.csv("./js/WorldPopulation.csv", function(error, dataSet){
              
                // SVGの表示領域を生成
                var svg = d3.select("#MyGraph")
                  .attr("width", width + margin.left + margin.right)
                  .attr("height", height + margin.top + margin.bottom)
                  .append("g")
                  .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
                  
                // スケール関数を作成。
                var xScale = d3.scale.linear()
                  .domain([1000,2200])
                  .range([0,width]);
              
                var yScale = d3.scale.log()
                  .domain([0.1,10])
                  .range([height,0]);
              
                var line = d3.svg.line()
                  .x(function(d,i){
                    return xScale(d.Year);
                  })
                  .y(function(d,i){
                    return yScale(d.Population);      
                  });
              
                // 折れ線グラフを生成
                svg.append("path")
                  .attr("class", "line")
                  .attr("d", line(dataSet));
                  
                // 散布図を描画
                svg.selectAll("circle")
                  .data(dataSet)
                  .enter()
                  .append("circle")
                  .attr("r", 5)
                  .attr("cx", function(d){
                    return xScale(d.Year);
                  })
                  .attr("cy", function(d){
                    return yScale(d.Population);
                  })
                  .attr("fill","orange");
              
                // Y軸を描画
                svg.append("g")
                  .attr("class", "axis")
                  .call(d3.svg
                    .axis()
                    .scale(yScale)
                    .orient("left")
                    .tickValues([0.1,0.2,0.4,0.6,0.8,1,2,4,8,10])
                    .tickFormat(d3.format(".1f"))
                );
                
                // X軸を描画
                svg.append("g")
                  .attr("class", "axis")
                  .attr("transform", "translate(0," + height + ")")
                  .call(d3.svg
                    .axis()
                    .scale(xScale)
                    .orient("bottom")
                    .tickFormat(function(d){return d;})
                );
                
                // 凡例
                svg.append("g")
                  .attr("class", "axis")
                  .append("text")
                  .attr("x", 20)
                  .attr("y", 20)
                  .text("[単位: 10億人]");
              
                svg.append("g")
                  .attr("class", "axis")
                  .append("text")
                  .attr("x", width/2)
                  .attr("y", margin.top + height)
                  .text("Year");
              
              })
              
              


               
              このエントリーをはてなブックマークに追加
              | D3.js | 09:51 | comments(0) | - | - |
              データの変化をSVG要素に反映する
              0
                JUGEMテーマ:JavaScript

                データの要素数や要素の順序が変化する様子を、アニメーションで可視化したい場合があります。今回はそんな場面で役に立つ、セレクションの仕組みについて見ていきます。

                d3.selectAll については、これまでにもしばしば使ってきました。しかしこれまでは、データの要素数や順序が変化しない、いわば「静的」なデータの可視化方法の説明にとどまっていました。データの要素数や要素の順序の変化に応じてDOM要素やSVG要素をダイナミックに操作するためには、D3.js では Enter, Update, Exit という3つの操作を行います。これらの操作は、最初はなかなか理解しにくいのですが、いったん動きを理解してしまうと、D3.js プログラミングの見通しが一気に広がります。

                Enter, Update, Exit という3つの操作によって、それぞれ以下の動作を実現します。
                • Enter:与えられたデータに対応するDOM/SVG要素がないときに、DOM/SVG要素を追加する
                • Update:データに応じてDOM/SVG要素を更新する
                • Exit:DOM/SVG要素に対応するデータがないときに、該当するDOM/SVG要素を削除する
                今回のサンプルプログラムでは、10〜60までの6個の数値を要素として持つ配列 (14行目の dataSet) をもとに、(3つの小円という有名なチュートリアル記事に敬意を表して) まず 6 個の円 (circle要素) を表示しますその後、3秒ごとにこの配列の要素の順番をシャッフルしたうえで要素数もランダムに変化させた配列 (60行目の data) を生成します。そのうえで、配列の要素数が変化していれば表示する円の数も変化させ、また各要素の順番が変化していればそれに応じて円の表示位置も変化させるようにします。

                サンプルページ



                プログラムは以下のとおりです。

                まず、18〜21行目で配列の各要素と circle 要素を紐づけています。紐づけには、data() メソッドのコールバック関数を定義することで、配列の各要素の値を使うよう指定しています。このブログにこれまで掲載したプログラムでは、data() メソッドのコールバック関数は特に指定していませんでした。それでも問題なく動いていたのは、data() メソッドはコールバック関数が指定されないと自動的に配列のインデックスによって各要素と紐づけるよう実装されているからなのです。そのうえで、23行目以降では Update, Enter, Exit 処理を順に実施しています。こうすることで、データの要素数や要素の順序の変化を、DOM要素やSVG要素に反映することができます。

                // 周囲にマージンを確保する
                var margin = {top: 40, right: 40, bottom: 80, left: 80};
                var width = 800 - margin.left - margin.right;
                var height = 500 - margin.top - margin.bottom;
                
                // SVGの表示領域を生成
                var svg = d3.select("#MyGraph")
                  .attr("width", width + margin.left + margin.right)
                  .attr("height", height + margin.top + margin.bottom)
                  .append("g")
                  .attr("transform", "translate(" + margin.left + ", " + height/2 + ")");
                
                // データセットの初期値
                var dataSet = [10, 20, 30, 40, 50, 60];
                
                function update(data){
                  // データセットをSVG要素に紐づける
                  var circle = svg.selectAll("circle")
                    .data(data, function(d){
                      return d;
                    });
                  
                  // Update: SVG要素を更新する
                  circle.attr("class", "update")
                    .transition()
                    .duration(1000)
                    .attr("cx", function(d,i){
                      return 120*i;
                    });
                  
                  // Enter: SVG要素を表示する
                  circle.enter()
                    .append("circle")
                    .attr("class", "enter")
                    .attr("cy", 0)
                    .attr("cx", function(d, i){
                      return 120*i;
                    })
                    .style("fill", "royalblue")
                    .attr("r", 0)
                    .transition()
                    .duration(1000)
                    .attr("r", function(d, i){
                      return d;
                    });
                        
                  // Exit: 不要になったSVG要素を削除する
                  circle.exit()
                    .transition()
                    .duration(1000)
                    .attr("r", 0)
                    .remove();
                }
                
                // 初期状態
                update(dataSet);
                
                // 3秒ごとにdataSetの要素をシャッフルし要素数も変化させて表示を更新する
                setInterval(function() {
                  var data = d3.shuffle(dataSet).slice(0, Math.floor(Math.random() * 7));
                  console.log(data);
                  update(data);
                }, 3000);
                

                 
                このエントリーをはてなブックマークに追加
                | D3.js | 22:35 | comments(0) | - | - |
                ツールチップの作成方法
                0
                  JUGEMテーマ:JavaScript

                  今回は、D3.jsを使ってグラフにツールチップ (Tooltip) を加える方法を見ていきます。

                  ツールチップとは画面の表示要素の一種で、対象にマウスポインタを合わせたときに出現させる小さな領域のことを指します。データ可視化では、グラフ上のデータ点にマウスポインタを合わせたときに詳細な数値や注釈などを表示させる方法としてよく使われるテクニックです。このツールチップを D3.js で実現する方法はいくつかありますが、今回は div 要素を使って実現する方法を紹介します。それでは以前作成した散布図のプログラムをベースに、各データ点にマウスポインタを合わせるとデータの詳細がツールチップ上に表示されるように改修してみましょう。

                  サンプルページ



                  まず、html ファイルの <style>〜</style> タグ内に以下を追加します。
                   

                  <style>
                    div.tooltip {
                      position: absolute;
                      text-align: center;
                      width: 80px;
                      height: 30px;
                      padding: 2px;
                      font: 14px sans-serif;
                      background: lightsteelblue;
                      border: 0px;
                      border-radius: 8px;
                      pointer-events: none;
                    }
                  </style>


                  次に、以前のプログラムにツールチップ表示用のコードを追加します。具体的には、47行目から59行目を追加しています。ツールチップ表示用のイベントハンドラとして mouseover を、またツールチップ非表示用のイベントハンドラとして mouseout を、それぞれ使っています。
                  var dataSet = [];
                  
                  // X,Y軸を表示できるようにグラフの周囲にマージンを確保する
                  var margin = {top: 40, right: 40, bottom: 40, left: 100};
                  var width = 800 - margin.left - margin.right;
                  var height = 500 - margin.top - margin.bottom;
                  
                  // ツールチップ用の設定
                  var div = d3.select("body")
                    .append("div")
                    .attr("class", "tooltip")
                    .style("opacity", 0);
                  
                  d3.csv("./js/population.csv", function(error, dataSet){
                    console.log(dataSet);
                  
                    // SVGの表示領域を生成
                    var svg = d3.select("#MyGraph")
                      .attr("width", width + margin.left + margin.right)
                      .attr("height", height + margin.top + margin.bottom)
                      .append("g")
                      .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
                      
                    // X軸用のスケール関数を作成
                    var xScale = d3.scale.linear()
                      .domain([1920,2000])
                      .range([0,width]);
                  
                    // Y軸用のスケール関数を作成
                    var yScale = d3.scale.linear()
                      .domain([0,130000])
                      .range([height,0]);
                  
                    // 散布図を描画
                    svg.selectAll("circle")
                      .data(dataSet)
                      .enter()
                      .append("circle")
                      .attr("r", 5)
                      .attr("cx", function(d){
                        return xScale(d.Year);
                      })
                      .attr("cy", function(d){
                        return yScale(d.Population);
                      })
                      .attr("fill","rgba(0, 0, 255, 0.0)")
                      .on("mouseover", function(d) { // マウスオーバー時にツールチップを表示
                        div.transition()
                          .duration(500)
                          .style("opacity", 1.0);
                        div.html(d.Year + "年
                  " + d.Population + "千人") .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }) .on("mouseout", function(d) { // マウスアウトするとツールチップを非表示 div.transition() .duration(500) .style("opacity", 0.0); }) .transition() // 最初に散布図をアニメーションで表示 .delay(function(d,i){ return i*20; }) .duration(500) .attr("fill","rgba(0, 0, 255, 1.0)"); // Y軸を描画 svg.append("g") .attr("class", "axis") .call(d3.svg .axis() .scale(yScale) .orient("left") ); // X軸を描画 svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + height + ")") .call(d3.svg .axis() .scale(xScale) .orient("bottom") ); // 凡例 svg.append("g") .append("text") .attr("x", 20) .attr("y", 20) .text("[単位:千人]"); })

                   
                  このエントリーをはてなブックマークに追加
                  | D3.js | 23:47 | comments(0) | - | - |
                  【D3.js】 バラ曲線 (Rose curve) を描画する
                  0
                    JUGEMテーマ:JavaScript

                    前回は D3.js を使って、プルダウンメニューで選択した数値によって変化するリサージュ曲線 (Lissajous curve) を描画しました。リサージュ曲線と同じように sin, cos を使った曲線として、バラ曲線 (Rose curve) があります。そこで今回は、このバラ曲線 (別名「正葉曲線」とも呼ばれます) を描画するプログラムを作成してみました。

                    バラ曲線は、局座標表示で下式によって現される曲線です。

                    r = sin((a/b)θ)

                    とてもシンプルな方程式ですが、変数 a, b を変化させることで、驚くほど多様な変化を見せてくれます。

                    前回リサージュ曲線を描画したときの html ファイルはそのまま流用して、JavaScript プログラムだけを下記のように変更します。変数 a, b を変化させて、ぜひバラ曲線が変化する様子を楽しんでみて下さい。

                    サンプルページ



                    // グラフの周囲にマージンを確保する
                    var margin = {top: 40, right: 40, bottom: 80, left: 80};
                    var width = 600 - margin.left - margin.right;
                    var height = 500 - margin.top - margin.bottom;
                    
                    // SVGの表示領域を生成
                    var svg = d3.select("#MyGraph")
                      .attr("width", width + margin.left + margin.right)
                      .attr("height", height + margin.top + margin.bottom)
                      .append("g")
                      .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
                    
                    var n = 2001;
                    
                    var dt = 20.0*Math.PI/n;
                    var t = d3.range(0, 20.0*Math.PI, dt);
                    var x = d3.range(n);
                    var y = d3.range(n);
                    
                    // X軸のスケール関数を生成
                    var x_scale = d3.scale.linear()
                      .domain([-1, 1])
                      .range([(width/2) - 200, (width/2) + 200]);
                        
                    // Y軸のスケール関数を生成
                    var y_scale = d3.scale.linear()
                      .domain([-1,1])
                      .range([(height/2) - 200, (height/2) + 200]);  
                      
                    // (x,y) の軌跡を描画するための関数
                    var line = d3.svg.line()
                      .x(function(d, i){ return x_scale(x[i]);})
                      .y(function(d, i) { return y_scale(y[i]);})
                      .interpolate("linear");
                    
                    var polyline = svg.append('path')
                      .attr("class", "line")
                      .attr('stroke', 'black')
                      .attr('stroke-width', '1')
                      .attr('fill', 'transparent');
                    
                    // 初期値として a = 1, b = 1のグラフを描画
                    update(1,1);
                    updateLine();
                    
                    // 対象となる html 内のプルダウン要素を選択 
                    var select_a = document.getElementById( 'listbox_a' );
                    var select_b = document.getElementById( 'listbox_b' );
                    
                    var a = 1;
                    var b = 1;
                    
                    // プルダウンメニューが更新されるとグラフを更新する
                    select_a.onchange = function(){
                      // プルダウンで選択されているoption要素を取得する
                      var selectedItem = this.options[ this.selectedIndex ];
                      a = selectedItem.value;
                      update(a, b);
                      updateLine();
                    }
                    
                    select_b.onchange = function(){
                      // プルダウンで選択されているoption要素を取得する
                      var selectedItem = this.options[ this.selectedIndex ];
                      b = selectedItem.value;
                      update(a, b);
                      updateLine();
                    }
                    
                    // バラ曲線 (Rose curve) を計算する
                    function update(a, b){
                      for (var i = 0; i < n; i++){
                        x[i] = Math.sin((a/b)*t[i])*Math.cos(t[i]);
                        y[i] = Math.sin((a/b)*t[i])*Math.sin(t[i]);
                      }
                    }
                    
                    // バラ曲線を更新する
                    function updateLine(){
                      polyline
                        .transition()
                        .duration(1500)
                        .ease("bounce")
                        .attr('d', line(y));
                    }
                    
                    // X軸を描画
                    svg.append("g")
                      .attr("class", "axis")
                      .attr("transform", "translate(0," + height/2 + ")")
                      .call(d3.svg
                        .axis()
                        .scale(x_scale)
                        .orient("bottom")
                      );
                      
                    // Y軸を描画
                    svg.append("g")
                      .attr("class", "axis")
                      .attr("transform", "translate(" + width/2 + ",0)")
                      .call(d3.svg
                        .axis()
                        .scale(y_scale)
                        .orient("left")
                      );
                      
                    

                     
                    このエントリーをはてなブックマークに追加
                    | D3.js | 21:03 | comments(0) | - | - |
                    【D3.js】 リサージュ曲線 (Lissajous curve) を描画する
                    0
                      JUGEMテーマ:JavaScript

                      前回は D3.js を使って、プルダウンメニューで選択した数値に従って変化するグラフを作成しました。今回はその応用として、リサージュ曲線 (Lissajous curve) を描画してみます。

                      リサージュ曲線とは、x = cos(at), y = sin(bt)t を変化させたときに x-y 平面上に描かれる (x, y) の軌跡です。今回は 2 つのプルダウンメニューを用意して、a, b それぞれの数値をユーザが選択すると、画面上のリサージュ曲線がそれに従って変化するページを作ってみます。

                      まず準備として、html ファイルに以下を追加します。ここでは D3.js で描画するグラフの線として、幅2ピクセルのオレンジ色の線を指定しています。また、プルダウンメニューとして a, b それぞれ 1 〜 10 まで選択可能としました。

                      サンプルページ


                       

                      <style> 
                        .line{ 
                          fill: none; 
                          stroke: orange; 
                          stroke-width: 2px; 
                        } 
                      </style> 

                      (中略)

                      <p>
                      a = <select id="listbox_a">
                        <option value="1">1</option>
                        <option value="2">2</option>
                        <option value="3">3</option>
                        <option value="4">4</option>
                        <option value="5">5</option>
                        <option value="6">6</option>
                        <option value="7">7</option>
                        <option value="8">8</option>
                        <option value="9">9</option>
                        <option value="10">10</option>
                      </select>
                      , b = <select id="listbox_b">
                        <option value="1">1</option>
                        <option value="2">2</option>
                        <option value="3">3</option>
                        <option value="4">4</option>
                        <option value="5">5</option>
                        <option value="6">6</option>
                        <option value="7">7</option>
                        <option value="8">8</option>
                        <option value="9">9</option>
                        <option value="10">10</option>
                      </select>
                      </p>
                      <svg id="MyGraph"></svg>


                      ここで、対象となるプルダウンメニューを特定できるよう、id = "listbox_a", "listbox_b" として、それぞれのプルダウンに ID を設定しています。
                      次に、プルダウンメニューで選択された a, b の値を取得して、リサージュ曲線を描画する D3.js プログラムを以下に示します。

                      // X,Y軸を表示できるようにグラフの周囲にマージンを確保する
                      var margin = {top: 40, right: 40, bottom: 80, left: 80};
                      var width = 600 - margin.left - margin.right;
                      var height = 500 - margin.top - margin.bottom;
                      
                      // SVGの表示領域を生成
                      var svg = d3.select("#MyGraph")
                        .attr("width", width + margin.left + margin.right)
                        .attr("height", height + margin.top + margin.bottom)
                        .append("g")
                        .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
                      
                      var n = 800;
                      
                      var dt = 2.0*Math.PI/n;
                      var t = d3.range(0, 2.0*Math.PI, dt);
                      var x = d3.range(n);
                      var y = d3.range(n);
                      
                      // X軸のスケール関数を生成
                      var x_scale = d3.scale.linear()
                        .domain([-1, 1])
                        .range([(width/2) - 200, (width/2) + 200]);
                          
                      // Y軸のスケール関数を生成
                      var y_scale = d3.scale.linear()
                        .domain([-1,1])
                        .range([(height/2) - 200, (height/2) + 200]);  
                        
                      // (x,y) の軌跡を描画するための関数
                      var line = d3.svg.line()
                        .x(function(d, i){ return x_scale(x[i]);})
                        .y(function(d, i) { return y_scale(y[i]);})
                        .interpolate("linear");
                      
                      var polyline = svg.append('path')
                        .attr("class", "line")
                        .attr('stroke', 'black')
                        .attr('stroke-width', '1')
                        .attr('fill', 'transparent');
                      
                      // 初期値として a = 1, b = 1のグラフを描画
                      update(1,1);
                      updateLine();
                      
                      // 対象となる html 内のプルダウン要素を選択 
                      var select_a = document.getElementById( 'listbox_a' );
                      var select_b = document.getElementById( 'listbox_b' );
                      
                      var a = 1;
                      var b = 1;
                      
                      // プルダウンメニューが更新されるとグラフを更新する
                      select_a.onchange = function(){
                        // プルダウンで選択されているoption要素を取得する
                        var selectedItem = this.options[ this.selectedIndex ];
                        a = selectedItem.value;
                        update(a, b);
                        updateLine();
                      }
                      
                      select_b.onchange = function(){
                        // プルダウンで選択されているoption要素を取得する
                        var selectedItem = this.options[ this.selectedIndex ];
                        b = selectedItem.value;
                        update(a, b);
                        updateLine();
                      }
                      
                      
                      // リサージュ曲線 (Lissajous curve) を計算する
                      function update(a, b){
                        for (var i = 0; i < n; i++){
                          x[i] = Math.cos(a*t[i]);
                          y[i] = Math.sin(b*t[i]);
                        }
                      }
                      
                      // グラフを更新する
                      function updateLine(){
                        polyline
                          .transition()
                          .duration(300)
                          .attr('d', line(y));
                      }
                      
                      // X軸を描画
                      svg.append("g")
                        .attr("class", "axis")
                        .attr("transform", "translate(0," + height/2 + ")")
                        .call(d3.svg
                          .axis()
                          .scale(x_scale)
                          .orient("bottom")
                        );
                        
                      // Y軸を描画
                      svg.append("g")
                        .attr("class", "axis")
                        .attr("transform", "translate(" + width/2 + ",0)")
                        .call(d3.svg
                          .axis()
                          .scale(y_scale)
                          .orient("left")
                        );
                      
                      このエントリーをはてなブックマークに追加
                      | D3.js | 19:20 | comments(0) | - | - |