スコット・マレイ
コード・アーティスト

Tutorials > D3 > 軸

最終更新日 2012年12月30日(原文)  2013年05月11日(翻訳 / h.sakai

前章では D3 のスケールをマスターしました。

Large, scaled scatterplot

この最終章では水平軸と垂直軸の使い方を学び、このグラフのいささか見苦しい赤い数字の羅列とおさらばいたしましょう。

軸の導入

スケール関数とまったく同様、D3 のの実体は関数であり、自分でパラメータを定義して使います。スケールと違うのは、軸関数は呼ばれたときに値を返すのではなく、線やラベル、目盛りなどからなる軸という視覚要素を生成する点です。

軸関数は SVG 要素を生成する SVG 専用の関数です。また、軸関数が扱えるのは量的スケールのみです。ordinal(序数)スケールには対応していません。

軸のセットアップ

一般的な軸関数の生成には d3.svg.axis() を使います。

var xAxis = d3.svg.axis();

軸関数には最低限どのスケールが作用するのかを指定する必要があります。ここでは散布図のコードで生成した xScale を指定します。

xAxis.scale(xScale);

軸から見てどの位置にラベルを表示するかも指定できます。デフォルトではこの値は bottom、つまり軸の下側にラベルが表示されます。デフォルト値を明示的に指定しても問題はありません。

xAxis.orient("bottom");

もちろんこのように別々に書かず、一行にまとめることもできます。

var xAxis = d3.svg.axis()
                  .scale(xScale)
                  .orient("bottom");

最後に、実際に軸を生成し、こまごまとしたラインやラベル等の要素を SVG に挿入します。そのためには xAxis 関数を呼ぶ( call )ことが必要です。今回はこのコードをスクリプトの最後に置きます。SVG の他のすべての要素の後に軸を生成するためです。

svg.append("g")
   .call(xAxis);

D3 の call() 関数 は(メソッドチェインの上流から)セレクションを受け取り、そのセレクションを任意の関数へ渡します。このコードでは "g" で新しいグループ要素を追加( append )し、その中にこれから生成するすべての軸要素を挿入します。g は必ずしも必要ではありませんが、"g"を用いることで要素の構成を一貫的なものにでき、また、すぐ次の例のようにグループ全体にクラスを適用することが可能になります。

新しく生成されたグループ要素 g がメソッドチェインの次のリンクへ渡されるセレクションとなります。call() メソッドはそのセレクションを xAxis 関数へと渡します。その結果、新しい g 要素の中に軸が生成されるのです。同じコードを次のようにも書けます。

svg.append("g")
   .call(d3.svg.axis()
               .scale(xScale)
               .orient("bottom"));

このコードでは軸関数の定義をすべて call() メソッドの中で行っています。しかし最初の例のように、まず関数として定義し、それを呼ぶ方がわかりやすいでしょう。

いずれにしろここまでのサンプル画面はこのようになります

Simple, but ugly axis

見た目の改善

確かにこれは軸には違いありませんが、パッとしない上にあまり効果的でもありません。この見た目を改善するために、まずグループ要素 gaxis というクラスを適用し、そのクラスに CSS を記述します。

svg.append("g")
   .attr("class", "axis")  // "axis" クラスを定義
   .call(xAxis);

ブランクだった head セクションの CSS style 中に最初のスタイルを記述します。

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

.axis text {
   font-family: sans-serif;
   font-size: 11px;
}

shape-rendering プロパティは SVG 属性の一つで、crispEdges を指定することによりアンチエイリアスを無効にし、ぼやけた軸を鮮明なエッジにします。

Cleaner axis

多少マシになりました。しかし肝心な軸本体が切れてしまっています。そもそも水平軸はグラフの下側にあるべきでしょう。transform(トランスフォーム:変形)属性を使えば、軸グループ全体を下側に移動することができます。

svg.append("g")
   .attr("class", "axis")
   .attr("transform", "translate(0," + (h - padding) + ")")
   .call(xAxis);

transform 属性に指定した値 translate(トランスレイト:移動する)の一つ目の引数が SVG 領域の左上を原点とする水平方向の移動量、二つ目の引数が垂直方向の移動量です(いずれもピクセル単位)。水平方向の移動量は 0、すなわち移動しません。垂直方向の移動量は h - padding です。すなわち軸グループの上端の位置を、SVG 領域全体の高さ h から、最初に指定した内側余白 padding を引いた値にセットしています。

Nice, clean axis

だいぶ良くなりましたね。ここまでのサンプル画面はこちらです

目盛りを調整する

D3 の目盛りは情報を伝えるためのものです。パソコンのメモリは多ければ多いほど良いものですが、軸の目盛りはそうでもありません。ある一線を越えるとグラフはかえって見苦しいものになってしまいます。お気づきの方もいるでしょうが、上のコードでは、軸にいくつ目盛りを配置するのか、あるいは目盛り間隔をいくつにするのかは、どこにも指定していません。明示的な指定が無い場合、D3 は自動的にスケール xScale を調べ、それに基づいて目盛りの個数や目盛り間隔を判断します(ここでは 50 ピクセル間隔)。

御想像の通り、D3 の軸はあらゆる面からカスタマイズ可能になっています。まずは ticks() メソッドを使い、大雑把に目盛りの個数を設定するところから始めましょう。

var xAxis = d3.svg.axis()
              .scale(xScale)
              .orient("bottom")
              .ticks(5);  // 大雑把に目盛りの個数を設定

Fewer ticks

サンプル画面です

お気づきでしょうか?コードでは目盛りの個数を 5 に設定しましたが、D3 は独自に判断し、これを 7 つへと増やしています。理由は以下の通りです。D3 は設定の裏の意味を読み取り、目盛りが 5 個だけでは入力ドメインの区切りが半端な数になってしまうと判断します。このケースでは、0、150、300、450、600となってしまうのです。

実は D3 は ticks() に設定された値を単なるサジェッションとしか受け取りません。もしそのサジェッションの値より明確で可読性が高くなると判断した場合、それが元の値より多少の増減に収まる範囲であれば、別の値で置き換えてしまうのです。このケースでは目盛り間隔 100 で置き換えました。

これは本当に素晴らしい機能で、デザインのスケーラビリティを大いに高めてくれます。もし将来データセットが変更になり、入力ドメインが拡大したり縮小したとしても、 このグラフの目盛りラベルの明確さ、読みやすさは引き続き維持されることが保証されるからです。

お次は Y 軸

次は垂直軸の番です。xAxis 関数用に書いたコードをコピーし、少し手直しして、コードの先頭近くに追加します。

// Y 軸の定義
var yAxis = d3.svg.axis()
              .scale(yScale)
              .orient("left")
              .ticks(5);

そして次のコードを一番最後に追加します。

// Y 軸の生成
svg.append("g")
   .attr("class", "axis")
   .attr("transform", "translate(" + padding + ",0)")
   .call(yAxis);

軸の向き( orient )が left になっている点と、yAxis グループ g が内側余白( padding )の量だけ右側に移動( translate )している点に注意してください。

Initial Y axis

いよいよ本物らしくなってきました。しかしよく見ると yAxis のラベルが少し切れています。左側にうまく収まるように、padding の値を 20 から 30 へと増やしてみましょう。

var padding = 30;

もちろんそれぞれの軸ごとに xPaddingyPadding と別の変数に分け、レイアウトをもっと細かく管理することもできます。

こちらがサンプル画面です。以下のように表示されているでしょうか?

Scatterplot with Y axis

仕上げ

この章で作った軸が動的でスケーラブルなものであることを確かめるために、データセットを静的なデータから、動的にランダムに生成するデータへと変更してみたいと思います。

// 動的でランダムなデータセット
var dataset = [];
var numDataPoints = 50;
var xRange = Math.random() * 1000;
var yRange = Math.random() * 1000;
for (var i = 0; i < numDataPoints; i++) {
   var newNumber1 = Math.round(Math.random() * xRange);
   var newNumber2 = Math.round(Math.random() * yRange);
   dataset.push([newNumber1, newNumber2]);
}

このコードは最初に空配列を初期化し、ループを 50 回まわして、ループのたびに 2 つのランダムな数値を生成し、その数値の組を dataset 配列に追加( "push" )しています。二つの入力レンジ( xRangeyRange )の範囲自体もランダムに設定していることに注意してください。

Scatterplot with random data

サンプル画面です。サンプル画面を読み込むたびにデータの値は変化します。重要なことは、読み込むたびに変化するのはデータセットだけではなく、それに応じて軸のスケールや目盛、ラベルの値も変化することです。

ここまで来れば、もう目障りな赤ラベルを捨て去っても問題はないでしょう。コードの関連行をコメントアウトします。

Scatterplot with random data and no red labels

完成です!最終版のサンプル画面です。繰り返しリロードしてみてください。

目盛りラベルのフォーマット

最後にもう一点。このチュートリアルで扱ったのは整数だけでした。整数は便利で扱いやすい数値形式です。しかし往々にしてもっと面倒なデータを扱う必要に迫られることもあります。そういうケースではもっと自由に軸ラベルのフォーマットを設定したくなることでしょう。

そこで tickFormat()の登場です。この関数は、文字通り目盛りラベルの数値をどのようにフォーマットするかを設定します。たとえば小数点以下3桁まで表示したい、あるいは数値をパーセント表示したい、あるいはその両方、等です。

使い方は、例えば最初に d3.format() を用いて数値のフォーマット関数を定義します。次の例では数値を小数点一桁のパーセント値として扱います(より詳しいオプションについては the reference entry for d3.format() をご覧ください)。

var formatAsPercentage = d3.format(".1%");

そしてそのフォーマット関数を tickFormat() メソッドを用いて軸関数に適用し、目盛りをフォーマットします。

xAxis.tickFormat(formatAsPercentage);

開発時のコツです。フォーマット関数をテストするのに一番簡単な方法は JavaScript コンソール内だと思います。例を挙げましょう。まず D3 を使っている適当なページを開きます。先ほどの散布図の完成画面で構いません。そしてコンソール画面からフォーマット規則を打ち込みます。次に、他の関数をテストする場合と同様に、なにか値を入力してテストします。

Testing format() in the console

上の図のように、0.54321 という値が 54.3% と表示されましたか?OKです!

このフォーマット関数のコードを実際に適用したのがこちらのサンプル画面です。データセットは整数値のままですので、パーセントフォーマットを適用してもこのままでは意味がありません。練習の積りで、ランダムに生成されるデータセットを整数以外の数値形式にしたり、あるいはフォーマット関数自体を書き換えてみてください。

以上でこのチュートリアルは終了です。ただし将来内容を追加するかもしれません。更新情報についてはこちらでお知らせします。本チュートリアルの訳文、内容について疑問点・分かりにくい点・質問等ありましたらd3.js 日本ユーザーグループでお尋ねください。簡単な内容でしたらツイッターでFoD5までどうぞ。

インタラクティブ・データ・ヴィジュアライゼーション このチュートリアルの書籍版の翻訳がオライリー・ジャパンより発売されました。タイトルは『インタラクティブ・データビジュアライゼーション ―D3.jsによるデータの可視化』です(画像をクリックするとアマゾンに飛びます)。

このチュートリアルを大幅に拡充し、3倍近い内容となっています。JavaScriptを中心に基礎編をさらに詳しく解説し(書籍版第3章)、応用編としてモーション、イベント、レイアウト、地図の作成法、データのエクスポート(PDFやSVG等)の章が追加されています(同9章~13章)。アマゾンのページで目次を見ることができます。

本チュートリアルがわかりにくいと感じられた方、あるいは本チュートリアルを終え、さらに応用力を身につけたいと思われた方のどちらにもお勧めの内容となっています。

翻訳はコンピュータ・プログラミング関連書籍を多数翻訳されている長尾高弘氏です。