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 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"); }
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 では、まずX軸とY軸を作成した後で、本体のグラフを作成します。
この軸の作成が、コーディングの大部分を占めます。
D3.js での入力データ形式には、通常配列と連想配列があります。通常配列のほうが簡素ですが、D3.js の説明では連想配列を例にしていることが多いのです。おそらく、他のシステムとの連携を重視しているのでしょう。
データの特徴として、X軸の値が数値の場合と質的データの場合があります。数値の場合は昇順になっている必要がありますが、不等間隔でも構いません。
ここでは、入力データはX軸データ x と2系列のY軸データ y1, y2 から成っていますが、一部を除き x と y1 のグラフにしています。x と y2 のグラフにしたいときは、プログラム中の y1 を t2, d[1] を d[2] とします。
svg1 通常配列・X軸=数値(昇順) |
svg2 連想配列・X軸=数値(昇順) |
svg3 通常配列・X軸=質的 |
svg4 連想配列・X軸=質的 |
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軸ラベル); |
通常配列と連想配列で下の赤の部分が異なるだけで、それ以外はすべて共通です。
// データの指定で次の変数を定義 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]);