D3.js 散布図グラフを段階的に描画して説明 - scaleLinear(), domain(), range(), axisBottom()

散布図グラフを段階ごとに描画する方法について説明する。

散布図グラフの作成方法

簡単なグラフのサンプルとして、散布図を描いてみる。

散布度または変散度は、変量が散らばっている程度を1つの数で表した値である。

座標点を打つ

今回は次のデータを使用する。

var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

[ ]は配列であり、[[x座標1, y座標1], [x座標2, y座標2], ...]のような形式の2次元配列データである。

まず、座標を円を表示するcircleで表示してみる。

svg.selectAll("circle") 
  .data(dataset) 
  .enter()
  .append("circle")
  .attr("cx", function(d) { return d[0]; })
  .attr("cy", function(d) { return d[1]; })
  .attr("r", 4);

全体のコードは次のとおりである。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Test</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </head>
  <body>
    <script>
      var svg = d3.select("body").append("svg").attr("width", 700).attr("height", 100);
      var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                      [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

      svg.selectAll("circle") 
        .data(dataset) 
        .enter()
        .append("circle")
        .attr("cx", function(d) { return d[0]; })
        .attr("cy", function(d) { return d[1]; })
        .attr("r", 4);
    </script>
  </body>
</html>

circleに割り当てたデータは2次元配列なので、circleには[x座標, y座標]のデータが割り当てられる。たとえば最初のcircleには[5, 20]が割り当てられる。このデータをcxcy属性にそれぞれ設定している。

座標点をテキストで表示する

次に、座標位置をx座標、y座標の形式でテキスト表示してみる。

svg.selectAll("text") 
  .data(dataset) 
  .enter()
  .append("text")
  .attr("x", function(d) { return d[0]; })
  .attr("y", function(d) { return d[1]; })
  .attr("fill", "red")
  .text(function(d) {
    return d[0] + "," + d[1];
  });

全体のコードは次のとおりである。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Test</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </head>
  <body>
    <script>
      var svg = d3.select("body").append("svg").attr("width", 700).attr("height", 100);
      var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                      [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

      svg.selectAll("circle") 
        .data(dataset) 
        .enter()
        .append("circle")
        .attr("cx", function(d) { return d[0]; })
        .attr("cy", function(d) { return d[1]; })
        .attr("r", 4);

      svg.selectAll("text") 
        .data(dataset) 
        .enter()
        .append("text")
        .attr("x", function(d) { return d[0]; })
        .attr("y", function(d) { return d[1]; })
        .attr("fill", "red")
        .text(function(d) {
          return d[0] + "," + d[1];
        });
    </script>
  </body>
</html>

x座標は右へ向かって増えるが、y座標は下へ向かって増えることに注意する。

スケール変換

スケール変換には、次のD3関数を使用する。

var scale = d3.scaleLinear()
    .domain([0, 500])
    .range([0, 100]);

この関数は、domainで指定した範囲をrangeで指定した範囲へ変換する関数を、scaleという変数に設定する。

たとえば次のように呼び出すことができ、呼び出すと20が返される。

scale(100);

これは[0,500]の範囲を[0,100]の範囲へ変更した、つまり座標を5分の1にした結果である。100 / 5 = 20

これをグラフに適用してみる。

グラフの幅と高さを次のように設定する。

var width = 400;
var height = 300;

比率を4:3に設定した。

var xScale = d3.scaleLinear()
  .domain([0, d3.max(dataset, function(d) { return d[0]; })])
  .range([0, width]);

var yScale = d3.scaleLinear()
  .domain([0, d3.max(dataset, function(d) { return d[1]; })])
  .range([height, 0]);

ここではデータの最大値を求めるためにd3.max(..)を使用した。d3.maxは第1引数に配列を指定し、第2引数に戻り値を返す関数を指定できる。
y座標は反転させたいので、rangeの第1引数に大きな値を入れている。

この関数を使用して再描画する。

  svg.selectAll("circle") 
     .data(dataset) 
     .enter()
     .append("circle")
     .attr("cx", function(d) { return xScale(d[0]); })
     .attr("cy", function(d) { return yScale(d[1]); })
     .attr("r", 4);

全体のコードは次のとおりである。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Test</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </head>
  <body>
    <script>
      // ここにコードを記述する。
      var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

      var width = 400;
      var height = 300;

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

      var xScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[0]; })])
        .range([0, width]);

      var yScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[1]; })])
        .range([height, 0]);

      svg.selectAll("circle") 
        .data(dataset) 
        .enter()
        .append("circle")
        .attr("cx", function(d) { return xScale(d[0]); })
        .attr("cy", function(d) { return yScale(d[1]); })
        .attr("r", 4);

      svg.selectAll("text") 
        .data(dataset) 
        .enter()
        .append("text")
        .attr("x", function(d) { return xScale(d[0]); })
        .attr("y", function(d) { return yScale(d[1]); })
        .attr("fill", "red")
        .text(function(d) {
          return d[0] + "," + d[1];
        });
    </script>
  </body>
</html>

y座標は下側が小さくなり、縦横比も4:3になった。

軸を描画する

次に軸を描画する。これにはD3の関数が用意されている。

var axisX = d3.axisBottom(xScale);
svg.append("g")
  .call(axisX);

先ほどのscaleと一緒に使用する。下側の軸を表すd3.axisBottomのほかに、d3.axisTopd3.axisRightd3.axisLeftがある。これをcall関数で呼び出すと、次のように軸を描画してくれる。

上のコードを適用した全体のコードは次のとおりである。軸があるため、text表示は削除した。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Test</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </head>
  <body>
    <script>
      var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

      var width = 400;
      var height = 300;

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

      var xScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[0]; })])
        .range([0, width]);

      var yScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[1]; })])
        .range([height, 0]);

      svg.selectAll("circle") 
        .data(dataset) 
        .enter()
        .append("circle")
        .attr("cx", function(d) { return xScale(d[0]); })
        .attr("cy", function(d) { return yScale(d[1]); })
        .attr("r", 4);

      var axisX = d3.axisBottom(xScale);
      svg.append("g")
        .call(axisX);
    </script>
  </body>
</html>

座標点と軸位置の調整

前の例では、位置と軸を表示するための幅を考慮していない。そのため次のように位置を移動する。

var axisX = d3.axisBottom(xScale);
var padding = 30;
svg.append("g")
    .attr("transform", "translate(" + 0 + "," + (height-padding) + ")")
    .call(axisX);

これを考慮すると、グラフを描画するコードは次のようになる。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Test</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </head>
  <body>
    <script>
     var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                      [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

      var width = 400;
      var height = 300;
      var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);

      var padding = 30;
      var xScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[0]; })])
        .range([padding, width - padding]);

      var yScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[1]; })])
        .range([height - padding, padding]);

      var axisx = d3.axisBottom(xScale);
      var axisy = d3.axisLeft(yScale);
      svg.append("g")
        .attr("transform", "translate(" + 0 + "," + (height - padding) + ")")
        .call(axisx);

      svg.append("g")
        .attr("transform", "translate(" + padding + "," + 0 + ")")
        .call(axisy);

      svg.selectAll("circle") 
        .data(dataset) 
        .enter()
        .append("circle")
        .attr("cx", function(d) { return xScale(d[0]); })
        .attr("cy", function(d) { return yScale(d[1]); })
        .attr("fill", "SkyBlue")
        .attr("r", 4);
    </script>
  </body>
</html>

これで散布図が完成した。円の色も空色に変更してみた。