読者です 読者をやめる 読者になる 読者になる

べにやまぶろぐ

技術寄りの話を書くつもり

D3.js でロケールを設定してグラフ軸のラベルの表示を変更する

JavaScript D3.js

バージョン : D3.js 3.4.5

たとえば Multi-Series Line Chart のような時系列のチャートを描画するとき、元データの期間を見て勝手にラベル(X軸の "April" など)を振ってくれるのは良いんですが場合によっては『1月』、『2月』とか、あるいは『睦月』、『如月』とかが良いよってことがあると思います。

前者の『1月』とか『2月』のような数字と固定文字列で表現できる場合、厳密には Time Formatting · mbostock/d3 Wiki · GitHub の書式で書けるときは

 var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom")
        .tickFormat(d3.time.format("%m月"));

とすれば OK です。

しかしながら『睦月』とか『如月』とかのときはそもそも % 記法で呼び出せる値を変更してやるのが良さそうです。例えば %B の full month name を呼ぶとデフォルトでは『January』となるところを『睦月』としたい。このとき、ロケールを変更します。

Localization · mbostock/d3 Wiki · GitHub

古いバージョンを対象にした記事を見ていると $LOCALE 環境変数を変更しなおして再コンパイルしろとか書いてあるんですが、開発者の Bostock さんのツイートによればバージョン 3.4 から動的に変更可能になった様子。これはありがたい!

Bountysource で書かれていた構想が実装された様子について Allow multiple locales. by mbostock · Pull Request #1652 · mbostock/d3 · GitHub で詳細に書かれていますが、これに倣って例えば

var japanese_locale = d3.locale(
    {
        "decimal": ".",
        "thousands": ",",
        "grouping": [3],
        "currency": ["", "円"],
        "dateTime": "%a %b %e %X %Y",
        "date": "%Y/%m/%d",
        "time": "%H:%M:%S",
        "periods": ["AM", "PM"],
        "days": ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"],
        "shortDays": ["日", "月", "火", "水", "木", "金", "土"],
        "months": ["睦月", "如月", "弥生", "卯月", "皐月", "水無月", "文月", "葉月", "長月", "神無月", "霜月", "師走"],
        "shortMonths": ["01月", "02月", "03月", "04月", "05月", "06月", "07月", "08月", "09月", "10月", "11月", "12月"]
    }
);

などとしてロケールを定義し、先ほど tickFormat() の中で d3.time.format() を呼んで設定していた書式を

var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom")
        .tickFormat(japanese_locale.timeFormat("%B(%b)"));

のようにすればこんなグラフが出来上がります(%B は full month、%b は short month)。

f:id:beniyama:20140409230900p:plain

Localization · mbostock/d3 Wiki · GitHub にある通り、

locale.timeFormat(specifier)

The locale’s equivalent of d3.time.format.

となっており、別途作成したロケールd3.time.format() 相当のメソッドを呼び出すことが可能になっています。重要なのは d3.locale()ロケールを新たに作成しても既存のそれには影響を与えないということです。なので d3.time.format() を呼べば変わらず en_US のロケールで結果が返ってきます。

時間以外にも d3.format() 相当の locale.numberFormat() も提供されており、数値のカンマの位置 (grouping) や通貨単位 (currency) などが設定可能です。

参考)

label - How do I make a custom axis formatter for hours & minutes in d3.js? - Stack Overflow

tickFormat() 使うことに気づくまでえらい遠回りしました orz