スタートページJavascriptSVG

D3.js 概要 全体の構成


D3.jsとは

D3:Data-Driven Documents(データ駆動型ドキュメント)
D3.js は SVG(ベクター画面)を対象にした JavaScript のライブラリで、棒グラフや折線グラフの作成を主な分野にしています。
Chart.jsがCANVASでの描画をするのに対して、D3.jsは SVG での描画をします。

D3.js のCDN: <script src="https://d3js.org/d3.v7.min.js"></script>


SVG要素の設定

単純な例として、SVG要素に青丸を表示するコードを示します。

HTML内に既に SVG要素が定義されている場合 

ここに、次の記述があります。
  <svg id="svg0" width="300" height="200" style="border: solid 1px black"></svg>


function test0() {
    //  既存 SVG要素を svg とする
  var svg = d3.select("#svg0")
    //  図形の作成
    var zukei = svg
        .append("circle")         // D3.js が提供している circle を図形とする
        .attr("cx", 100)          // circle の属性を指定
        .attr("cy", 50)
        .attr("r", 30)
        .attr("fill", "blue");
}

SVGを新規設定する場合

HTML内に対象とする SVG要素がなく、D3.js で生成します。
次のプログラムが記述されている場所(実行された場所に SVG要素が生成されます。

    //  SVG要素の創成
    var svg = d3.select("body")
        .append("svg")
        .attr("width", 200)
        .attr("height", 100)
        .style("border", "solid 1px black");
    //  図形の作成
    var zukei = svg
        .append("circle")
        .attr("cx", 100)
        .attr("cy", 50)
        .attr("r", 30)
        .attr("fill", "blue");

このシリーズでは、HTML における SVG要素の場所を固定するという理由だけで「既にSVG要素が定義されている場合」だけにします。


D3.js でのグラフ作成の特徴

D3.js では、まずX軸とY軸を作成した後で、本体のグラフを作成します。
この軸の作成が、コーディングの大部分を占めます。
D3.js での入力データ形式には、通常配列と連想配列があります。通常配列のほうが簡素ですが、D3.js の説明では連想配列を例にしていることが多いのです。おそらく、他のシステムとの連携を重視しているのでしょう。
データの特徴として、X軸の値が数値の場合と質的データの場合があります。数値の場合は昇順になっている必要がありますが、不等間隔でも構いません。

ここでは、入力データはX軸データ x と2系列のY軸データ y1, y2 から成っていますが、一部を除き x と y1 のグラフにしています。x と y2 のグラフにしたいときは、プログラム中の y1 を t2, d[1] を d[2] とします。

データ形式とX軸の設定

svg1 通常配列・X軸=数値(昇順)
var 入力データ = [
   [10, 45, 30],
   [20, 30, 20],
   [25, 15, 30],
   [40, 10, 20],
   [50, 15, 20]
  ];


svg2 連想配列・X軸=数値(昇順)
var 入力データ = [
   {x:10, y1:45, y2:30},
   {x:20, y1:30, y2:20},
   {x:25, y1:15, y2:30},
   {x:40, y1:10, y2:20},
   {x:50, y1:15, y2:20}
  ];


svg3 通常配列・X軸=質的
var 入力データ = [
   ["A", 45, 30],
   ["B", 30, 20],
   ["C", 15, 30],
   ["D", 10, 20],
   ["E", 15, 20]
  ];


svg4 連想配列・X軸=質的
var 入力データ = [
   {x: "A", y1: 45, y2: 30},
   {x: "B", y1: 30, y2: 20},
   {x: "C", y1: 15, y2: 30},
   {x: "D", y1: 10, y2: 20},
   {x: "E", y1: 15, y2: 20}
  ];


X軸作成

// ================= データの指定
var X軸ラベル = "県";
var ラベルサイズ = "10pt";
var ラベルの色 ="black";
var X目盛数 = 5;
var 最小値X = 10;  // X軸の目盛りに影響するのでキリのよい値にする
// =================  SVG画面の設定
var svg = d3.select("#svg1")
var width = 250; var height = 200; 
var マージン上 = 40; var マージン下 = 40;
var マージン左 = 60; var マージン右 = 20;

// ========== X軸

var X軸スケール = d3.scaleLinear()
   .domain([0, d3.max(入力データ, function(d) { return d[0]; }) ])
                                   // 入力データが連想配列のときは d.xにする
   .range([マージン左, width - マージン右]);
var X軸 = d3.axisBottom(X軸スケール)
    .ticks(X目盛数)
    .tickSize(マージン上+マージン下 - height);
svg.append("g")
   .attr("transform", "translate(" + 0 + ", " + (height - マージン下) + ")")
   .call(X軸)
   .append("text")
   .attr("fill", ラベルの色)
   .attr("x", (width-マージン左-マージン右)/2+マージン左)
   .attr("y", マージン下/2)
   .attr("text-anchor", "middle")
   .attr("font-size", ラベルサイズ)
   .attr("font-weight", "middle")
   .text(X軸ラベル);

X軸作成

// ================= データの指定
var X軸ラベル = "県";
var ラベルサイズ = "10pt";
var ラベルの色 ="black";
var X目盛数 = 5;

// =================  SVG画面の設定
var svg = d3.select("#svg3")
var width = 250; var height = 200; 
var マージン上 = 40; var マージン下 = 40;
var マージン左 = 60; var マージン右 = 20;

// ========== X軸
var X目盛間隔 = (width - マージン左 -マージン右) / (X目盛数 + 0.5);
var X軸スケール = d3.scaleBand()
   .domain(入力データ.map(function(d) { d[0]; }))
                                   // 入力データが連想配列のときは d.xにする
   .range([マージン左 + (X目盛間隔/2), width - マージン右]);
var X軸 = d3.axisBottom(X軸スケール)
    .ticks(X目盛数)
    .tickSize(マージン上+マージン下 - height);
svg.append("g")
   .attr("transform", "translate(" + (-X目盛間隔/2) + "," + (height - マージン下) + ")")
   .call(X軸)
   .append("text")
   .attr("fill", ラベルの色)
   .attr("x", (width - マージン左 - マージン右) / 2 + マージン左)
   .attr("y", マージン下/2)
   .attr("text-anchor", "middle")
   .attr("font-size", ラベルサイズ)
   .attr("font-weight", "middle")
   .text(X軸ラベル);

Y軸の表示

通常配列と連想配列で下の赤の部分が異なるだけで、それ以外はすべて共通です。

    // データの指定で次の変数を定義
    var 最小値Y = 0;  // Y軸スケールに関係するので、実際の値ではなく、キリのよい値にする。
    var Y軸ラベル = "人数";
    var Y目盛数 = 5; // 補助線数と考えてもよい。D3.js が自動的に変更することもある。
    // ========== Y軸
    var Y軸スケール = d3.scaleLinear()
       .domain([最小値Y, d3.max(入力データ, function(d) { return d.[1]; })])
                                   // 入力データが連想配列のときは d.y1にする
                                   // 複数グラフのときは、入力データで値が最大の系列を用いる
       .range([height - マージン下, マージン上]);
    var Y軸 = d3.axisLeft(Y軸スケール)
       .ticks(Y目盛数)
       .tickSize(マージン左+マージン右 -width);
    svg.append("g")
       .attr("transform", "translate(" + マージン左 + "," + 0 + ")")
       .call(Y軸)
       .append("text")
       .attr("fill", ラベルの色)
       .attr("text-anchor", "middle")
       .attr("x", -(height - マージン上 - マージン下) / 2 - マージン上)
       .attr("y", -マージン左/2)
       .attr("transform", "rotate(-90)")    // 縦書きにする
       .attr("font-size", ラベルサイズ)
       .text(Y軸ラベル);

グラフの作成

本体のグラフを作成するのは、むしろ簡単です。
ここでは、折線グラフですが、棒グラフではかなり異なる記述になります。
入力データが通常配列と連想配列の違いは、下の赤字の部分だけです。

    svg.append("path")        // 折線グラフ
       .datum(入力データ)
       .attr("fill", "none")
       .attr("stroke", 線の色)
       .attr("stroke-width", 線の太さ)
       .attr("d", d3.line()
       .x(function(d) { return X軸スケール(d[0]); })
       .y(function(d) { return Y軸スケール(d[1]); }));
            // 連想配列のときは d.x. d.y1 とする

複数のグラフにするときは、上を d[1], d[2] として2つ記述すればよい。for ~ はうまくいかない。

タイトル

すべてに共通ですが、右端の svg4 だけに組み込みました。
ここではグラフの上・中央に表示していますが、グラフの下部に表示することもできます。
マージン上のサイズを調整する必要があります。

    var タイトル = "県別男女人数";
    var タイトルの色 = "red";
    var タイトルサイズ = "14pt";

    svg.append("text")
       .attr("transform", "translate(" + (width+マージン左-マージン右)/2 + "," + (マージン上)/2 + ")")
             // X座標:(width+マージン左-マージン右)/2 グラフの中央
             // Y座標:(マージン上)/2
       .attr("text-anchor", "middle")  // 上の位置に文字列の中央を合わせる
       .attr("fill", タイトルの色)
       .attr("font-size", タイトルサイズ)
       .text(タイトル);

凡例

すべてに共通ですが、右端の svg4 だけに組み込みました。

    var 系列名 = ["男性", "女性"];
    var 線の色 = [4, 2]; // むしろ「系列の色」としたほうが適切かも
    var 凡例サイズ = "10pt";

    // 系列1
    var y0 = height - 10; // 画面の最低部に横に並べる
                          // グラフの直上なら マージン上 - 10
    var x0 = マージン左;  // 系列文字列の先頭をここに合わせる
    svg.append('text')
       .attr("x", x0)
       .attr("y", y0)
       .attr("font-size", 凡例サイズ) 
       .attr("fill", 線の色[0])
       .text(系列名[0]);
    // 系列2    系列の数だけ繰り返す。 for ~ はうまくいかない
    x0 = x0 + 100; // 右に100 移動 100 = 系列文字列の長さ + α
    svg.append('text')
       .attr("x", x0)
       .attr("y", y0)
       .attr("font-size", 凡例サイズ) 
       .attr("fill", 線の色[1])
       .text(系列名[1]);