今日はDC.jsという多次元解析のためのチャートライブラリをご紹介します。ちなみにこれはD3の情報ソースという5日目の記事で紹介したDashing D3.jsで最近紹介されていて知りました。
DC.jsによるダッシュボードサンプル |
DC.jsはサイトを見ていただければどういうものか分かると思いますが、 複数のチャートにまたがって、データのフィルタが可能なチャートライブラリです。デモサイトのそれぞれのチャートを操作すると他のチャートもそれに従ってダイナミックに表示が更新されます。
DC.jsはCrossfilterという配列のフィルタ処理を行うライブラリとD3.jsに依存しています。CrossfilterはSquareというiPhoneをカードリーダーにするハードウェア、サービスを提供している会社が提供しているオープンソースのライブラリで自社のSquare Registerのために開発されました。
上記のCrossfilterのサイトでもDC.jsのサイトのようなデモが見れます。DC.jsの作者はこのCrossfilterのデモに感銘を受けて、これを手軽に扱えるライブラリとしてDC.jsを開発したとのことです。
Dashing D3.jsで紹介されていたDC.jsのチュートリアルがとてもわかり易かったのでそれを元にして紹介する形をとりたいと思います。分かりやすくするため、若干コードを変更しているところもありますが基本的には一緒です。
Making Dashboards with Dc.js - Part 1: Using Crossfilter.js
Making Dashboards with Dc.js - Part 2: Graphing
1.Crossfilter
まずCrossfilterがどのように動作するかCrossfilterのAPIドキュメントにあるにある データを使って見てみましょう。var data = [
{date: "2011-11-14T16:17:54Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T16:20:19Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T16:28:54Z", quantity: 1, total: 300, tip: 200, type: "visa"},
{date: "2011-11-14T16:30:43Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:48:46Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:53:41Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:54:06Z", quantity: 1, total: 100, tip: 0, type: "cash"},
{date: "2011-11-14T16:58:03Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:07:21Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:22:59Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:25:45Z", quantity: 2, total: 200, tip: 0, type: "cash"},
{date: "2011-11-14T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "visa"}
];
var ndx = crossfilter(data);
var totalDim = ndx.dimension(function(d) { return d.total; });
var total_90 = totalDim.filter(90);
では次にstringでやってみましょう。
var typeDim = ndx.dimension(function(d) {return d.type;});
var visa_filter = typeDim.filter("visa");
var cash_filter = typeDim.filter("cash");
ちなみにコメントアウトしてるvar total_90 = totalDim.filter(90);を有効にするとfilterの結果は0になります。totalが90のものにvisa、 cashのものがないからです。このようにちゃんとcrossfilterされていることが確認できます。では次に実際にDC.jsを使ってチャートを描画してみましょう。
2.DC.jsでチャートの描画
2.1:ラインチャート
まずデータを用意します。以下ははデイリーのwebサーバのhit数を示した配列データです。var data = [
{date: "12/27/2012", http_404: 2, http_200: 190, http_302: 100},
{date: "12/28/2012", http_404: 2, http_200: 10, http_302: 100},
{date: "12/29/2012", http_404: 1, http_200: 300, http_302: 200},
{date: "12/30/2012", http_404: 2, http_200: 90, http_302: 0},
{date: "12/31/2012", http_404: 20, http_200: 90, http_302: 0},
{date: "01/01/2013", http_404: 2, http_200: 90, http_302: 0},
{date: "01/02/2013", http_404: 1, http_200: 10, http_302: 1},
{date: "01/03/2013", http_404: 2, http_200: 90, http_302: 0},
{date: "01/04/2013", http_404: 2, http_200: 90, http_302: 0},
{date: "01/05/2013", http_404: 2, http_200: 90, http_302: 0},
{date: "01/06/2013", http_404: 2, http_200: 200, http_302: 1},
{date: "01/07/2013", http_404: 1, http_200: 200, http_302: 100}
];
var parseDate = d3.time.format("%m/%d/%Y").parse;
data.forEach(function(d) {
d.date = parseDate(d.date);
d.total = d.http_404 + d.http_200 + d.http_302;
});
//dataからcrossfilterのインスタンスを作成
var ndx = crossfilter(data);
//X軸をtimelineにするためdateのdimensionを作成
var dateDim = ndx.dimension(function(d) {
return d.date;
});
//Y軸にtotalを表示するためのkey-valueデータをdateDimから作成
var hits = dateDim.group().reduceSum(function(d) {
return d.total;
});
var minDate = dateDim.bottom(1)[0].date;
var maxDate = dateDim.top(1)[0].date;
dc.lineChartでインスタンスを作成し、parameterを設定して最後にdc.renderAll()で描画します。
//dcのlineChartインスタンスを作成
var hitslineChart = dc.lineChart("#chart-line-hitsperday");
//parameter設定
hitslineChart
.width(450).height(200)
.dimension(dateDim)
.group(hits)
.x(d3.time.scale().domain([minDate, maxDate]));
//チャートを描画
dc.renderAll();
2.2:パイチャートの追加
yearの属性を追加する処理を追加します。var parseDate = d3.time.format("%m/%d/%Y").parse;
data.forEach(function(d) {
d.date = parseDate(d.date);
d.total = d.http_404 + d.http_200 + d.http_302;
d.Year=d.date.getFullYear(); //yearの属性を追加
});
//パイチャートのdimensionを作成
var yearDim = ndx.dimension(function(d) {return +d.Year;});
//パイチャートののkey-valueデータをyearDimから作成
var year_total = yearDim.group().reduceSum(function(d) {return d.total;});
基本的にラインチャートと同じ手続きです。
//dcのパイチャートインスタンスを作成
var yearRingChart = dc.pieChart("#chart-ring-year");
yearRingChart
.width(200).height(200)
.dimension(yearDim)
.group(year_total)
.innerRadius(30);
2.3:ラインチャートにhttpステータス毎のデータを表示
httpステータス毎にgroupデータを作成します。//Y軸にtotalを表示するためのkey-valueデータをhttpステータス毎に作成
var status_200=dateDim.group().reduceSum(function(d) {return d.http_200;});
var status_302=dateDim.group().reduceSum(function(d) {return d.http_302;});
var status_404=dateDim.group().reduceSum(function(d) {return d.http_404;});
//lineChartの各種parameter設定
hitslineChart
.width(450).height(200)
.dimension(dateDim)
.group(status_200,"200")
.stack(status_302,"302")
.stack(status_404,"404")
.renderArea(true)
.x(d3.time.scale().domain([minDate,maxDate]))
.legend(dc.legend().x(50).y(10).itemHeight(13).gap(5))
.yAxisLabel("Hits per day");
2.4:データテーブルの追加
最後にデータテーブルを追加してみましょう。以下のデータテーブル用のhtmlを追加します。
<div style='font-size:11px; width:270px; margin-left:180px;'>
<table id="dc-data-table">
<thead>
<tr class="header">
<th>Day</th>
<th>TPS 200</th>
<th>TPS 302</th>
<th>TPS Total</th>
</tr>
</thead>
</table>
</div>
//dcのデータテーブルインスタンスを作成 var datatable = dc.dataTable("#dc-data-table"); datatable .dimension(dateDim) .group(function(d) {return d.Year;}) .columns([ function(d) { return d.date.getDate() + "/" + (d.date.getMonth() + 1) + "/" + d.date.getFullYear(); }, function(d) {return d.http_200;}, function(d) {return d.http_302;}, function(d) {return d.http_404;}, function(d) {return d.total;} ]);
いかがでしょうか?かなり少量のコードでこのような複数のチャートにまたがったフィルタ処理ができるのはすごいですね。DashBoard等のUI開発にかなり役に立ちそうです。
ではd3.js Advent Calendar 2013はこれでおしまいです。投稿していただいた方、読んでいただいた方、本当にありがとうございました!
Happy D3 life and Happy Holiday, そして良いお年を!