D3.js – 折れ線グラフと棒グラフのポイント

2014.04.24

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

前回に引き続き、D3.jsについて学習しているt-hondaです。

D3.jsは「データ・ドリブン」でドキュメントを生成するため
できることが多いことや、ソース上で行うことが多い傾向があり
取っ付き難い点もあるかと思います。

今回は折れ線グラフと棒グラフを表示する上で共通となるポイントについて取り上げ
次にそれらのポイントを踏まえて公式サイトのサンプルソースを解析してみたいと思います。

ポイントについて

各ポイントについては、以下のサイトに分かりやすい説明が書かれているので
引用しながら説明していきたいと思います。

D3 入門 : スケール - スコット・マレイ

スケール

D3.jsでのスケールとは、入力した値を適切な(見やすい)値に変換するモノ(関数)だと言えると思います。

上記のサイトでは
データを視覚化した場合、必ずしも元の値がうまくデータ表示領域に収まるとは限りません。その場合、元の値を適切な値にマップすることでスムーズな視覚化が可能になります。そのための簡単な手段を提供するのがスケール関数です。 と説明されています。

軸とは スケールを視覚的に表現したもの です。

一般的なグラフでは、軸は「目盛り」として表示されます。

ですが、D3.jsにおける「目盛り」は「スケール」と異なる概念であり
D3 における目盛りとは「軸」の構成要素 であると説明されています。

入力ドメイン

プログラムでの用語である「ドメイン」と同じような概念であり スケールの入力ドメインとは、入力データの値の取りうる範囲のこと と説明されています。

出力レンジ

範囲のことであり
スケールの出力レンジとは、出力値の取りうる範囲のことです。 と説明されています。

この「出力レンジ」が「入力ドメイン」と異なることに注意してください。
「入力ドメイン」(=データ)の値をそのままグラフとして表示すると分かり難い場合
D3.jsでは「出力レンジ」(=範囲)に収まるように加工して表示します。
(例えば、データが0〜1億まで分布していた場合、そのまま表示すると幅が広すぎるグラフができてしまう)

svgの要素

上記のD3.jsのポイントの他に、svg(Scalable Vector Graphics)上にグラフを表示する場合
以下のsvgの要素についても理解しておく必要があります。

g要素

複数の要素をグループ化するためのタグです。

path要素

図形の外形線の定義

パス – SVG 1.1 (第2版)より

のことであり、内部に曲線、直線、折れ線など表示し、図形を描画することができます。

サンプルソース

上記のポイントを踏まえ、公式サイトに乗せられている
Line Chart
Bar Chart
を解析し、コメントを追加しました。

これらは以前の弊社のブログでも取り上げられていたものですが
コメントについては新たに記述しております。

■折れ線グラフ(LineChart)

<!DOCTYPE html>
<meta charset="utf-8">
<style>

    body {
        font: 10px sans-serif;
    }

    .axis path,
    .axis line {
        fill: none;
        stroke: #000;
        shape-rendering: crispEdges;
    }

    .x.axis path {
        display: none;
    }

    .line {
        fill: none;
        stroke: steelblue;
        stroke-width: 1.5px;
    }

</style>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>

    var margin = {top: 20, right: 20, bottom: 30, left: 50},
            width = 960 - margin.left - margin.right,
            height = 500 - margin.top - margin.bottom;

    var parseDate = d3.time.format("%d-%b-%y").parse;

    // スケールと出力レンジの定義
    var x = d3.time.scale()
            .range([0, width]);

    var y = d3.scale.linear()
            .range([height, 0]);

    // 軸の定義
    var xAxis = d3.svg.axis()
            .scale(x)
            .orient("bottom");

    var yAxis = d3.svg.axis()
            .scale(y)
            .orient("left");

    // 線の定義
    var line = d3.svg.line()
            .x(function(d) { return x(d.date); })
            .y(function(d) { return y(d.close); });

    // svgの定義
    var svg = d3.select("body").append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

    // データを読み込む
    d3.tsv("data.tsv", function(error, data) {
        // データをフォーマット
        data.forEach(function(d) {
            d.date = parseDate(d.date);
            d.close = +d.close;
        });

        // データを入力ドメインとして設定
        // 同時にextentで目盛りの単位が適切になるようにする
        x.domain(d3.extent(data, function(d) { return d.date; }));
        y.domain(d3.extent(data, function(d) { return d.close; }));

        // x軸をsvgに表示
        svg.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + height + ")")
                .call(xAxis);

        // y軸をsvgに表示
        svg.append("g")
                .attr("class", "y axis")
                .call(yAxis)
                .append("text")
                .attr("transform", "rotate(-90)")
                .attr("y", 6)
                .attr("dy", ".71em")
                .style("text-anchor", "end")
                .text("Price ($)");

        // path要素をsvgに表示し、折れ線グラフを設定
        svg.append("path")
                .datum(data)
                .attr("class", "line")
                .attr("d", line);
    });

</script>

■棒グラフ(BarChart)

<!DOCTYPE html>
<meta charset="utf-8">
<style>

    .bar {
        fill: steelblue;
    }

    .bar:hover {
        fill: brown;
    }

    .axis {
        font: 10px sans-serif;
    }

    .axis path,
    .axis line {
        fill: none;
        stroke: #000;
        shape-rendering: crispEdges;
    }

    .x.axis path {
        display: none;
    }

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

    var margin = {top: 20, right: 20, bottom: 30, left: 40},
            width = 960 - margin.left - margin.right,
            height = 500 - margin.top - margin.bottom;

    // スケールと出力レンジの定義
    var x = d3.scale.ordinal()
            .rangeRoundBands([0, width], .1);

    var y = d3.scale.linear()
            .range([height, 0]);

    // 軸の定義
    var xAxis = d3.svg.axis()
            .scale(x)
            .orient("bottom");

    var yAxis = d3.svg.axis()
            .scale(y)
            .orient("left")
            .ticks(10, "%");

    // svgの定義
    var svg = d3.select("body").append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    // データを読み込む
    d3.tsv("data.tsv", type, function(error, data) {
        // データを入力ドメインとして設定
        x.domain(data.map(function(d) { return d.letter; }));
        y.domain([0, d3.max(data, function(d) { return d.frequency; })]);

        // x軸をsvgに表示
        svg.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + height + ")")
                .call(xAxis);

        // y軸をsvgに表示
        svg.append("g")
                .attr("class", "y axis")
                .call(yAxis)
                .append("text")
                .attr("transform", "rotate(-90)")
                .attr("y", 6)
                .attr("dy", ".71em")
                .style("text-anchor", "end")
                .text("Frequency");

        // 棒グラフを表示
        svg.selectAll(".bar")
                .data(data)
                .enter().append("rect")
                .attr("class", "bar")
                .attr("x", function(d) { return x(d.letter); })
                .attr("width", x.rangeBand())
                .attr("y", function(d) { return y(d.frequency); })
                .attr("height", function(d) { return height - y(d.frequency); });

    });

    function type(d) {
        d.frequency = +d.frequency;
        return d;
    }

</script>

どちらも処理順序が

  • スケールと出力レンジの定義
  • 軸の定義
  • svgの定義
  • データを読み込む
  • x軸をsvgに表示
  • y軸をsvgに表示
  • グラフの表示・設定

となっており、ほぼ共通していることが分かるかと思います。

違いがあるのは、ほぼ折れ線・棒の表示方法になりますが
それぞれの詳細については以下のサイト等を参考にしてください。

svg要素の基本的な使い方まとめ
D3 入門 : 棒グラフの作成 - スコット・マレイ

まとめ

折れ線グラフ、棒グラフには共通するポイントがあり、それらを押さえることで
D3.jsのソースを理解する上での一助になればと思います。