お久しぶりです。Perfumeエンジニアの新井です。
先週の9/21(日)にPerfumeがメジャーデビュー10周年&結成15周年の記念日を迎えました。
その記念日に、最終日を迎えたPerfume 5th Tour 2014『ぐるんぐるん』の詳しい様子は、こちらのライブレポートを御覧ください。
また、このPerfume Dayに各CDショップで開かれたイベントの様子はこちらの記事を。
「この記念日を迎えた感動を、自分なりに表現しなければ…」
「メジャーデビュー10周年ということで、これまでの歴史を振り返りたい…」
「よしそれじゃ、これまでリリースしたシングルをざっと振り返ろう!」
という個人的な使命感に駆られて、このブログを書いています。
やることはタイトル通り
『メジャーデビューからの全シングル20曲の売上枚数の遷移を折れ線チャートで表示』
それを実現するためにこの頃興味があったD3.jsを、勉強がてら使ってみました。
※Perfumeのシングル曲のデータに関しては、こちらのサイトを全面的に信用して進めていきます。
D3.jsってなに?
データ視覚化のためのJavaScriptライブラリです。
公式サイト:http://d3js.org/
以下、そこからの引用です。
D3 helps you bring data to life using HTML, SVG and CSS. D3’s emphasis on web standards gives you the full capabilities of modern browsers without tying yourself to a proprietary framework, combining powerful visualization components and a data-driven approach to DOM manipulation.
要は、
「D3.jsを使うことで、HTMLやSVG、CSSにデータを吹き込みことができるし、
特定のフレームワークに縛られず、モダンブラウザの性能をフルに引き出すことができるよ」
ということを言っています。
また、D3.jsを利用しはじめるのは簡単で、
1 |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> |
このスクリプトを貼り付けるだけでOKです。
ここにかっこいいD3.jsのサンプルがたくさんあるので、D3.jsの幅広い表現を体感してみてください。
デモ
今回作ってみたものをHerokuにあげて公開してます。ちょっと触ってみてください。
URL:http://prfm.herokuapp.com/sales.html
※グラフ上に表示している順位は、オリコンチャートの最高順位です。
ちょこっと解説
今回書いたソースはこちら。
また、上記のPerfumeのシングル情報をまためたページから、データを引っ張ってきてごにょごにょっとしたcsvファイルがこちらとなります。
以下、ちょこっと解説していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<body> <script src="http://d3js.org/d3.v3.js"></script> <script> //マージン情報を設定 var margin = {top: 20, right: 40, bottom: 30, left: 50}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var parseDate = d3.time.format("%Y-%m-%d").parse; //目盛りを表示するためにスケールを設定 var x = d3.time.scale() // 時間スケールを設定 .range([0, width]) // 出力サイズを指定 var y = d3.scale.linear() // 連続値を扱うスケールを指定 .range([height, 0]); // 出力サイズを指定 //スケール情報を渡して、X軸Y軸を定義する var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .tickFormat(d3.time.format("%Y/%m")); var yAxis = d3.svg.axis() .scale(y) .orient("left"); //マウスオーバーで表示させるdiv要素を定義 var div = d3.select("body") .append("div") .attr("class", "detail") .style("opacity", 0); //グラフを表示させるsvg要素を定義 var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) //新しいグループ要素gを追加 .append("g") // 目盛り全体の位置を調整する .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
ここで出てきたsvg要素とは、HTML5から導入されたSVGの記述に基づいて、HTMLだけで図形が描画できるものです。
SVGは、Scalable Vector Graphicsの略で、XMLによってベクター形式の画像を表現できます。
また、ここで出てきてた’.append(“g”)’とは、新しいグループ要素gを用意することで、次に出てくるtransformを全体に適用できるようにしています。
このグループ要素gと軸の描写に関しては、以下の説明が詳しくわかりやすかったです。
D3 入門 : 軸 – スコット・マレイ – alignedleft
次に、実際にグラフを描写する関数を見ていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
function draw(interpolate) { //以下を削除して、初期化 //折れ線削除 svg.selectAll("path").remove(); //xy軸削除 svg.selectAll("g").remove(); //散布図削除 svg.selectAll("circle").remove(); //テキスト削除 svg.selectAll("text").remove(); if (!interpolate) interpolate = "cardinal"; //line関数を使って、折れ線を定義 var line = d3.svg.line() .x(function (d) { return x(d.date); }) .y(function (d) { return y(d.sales); }) //interpolateで折れ線の形状を指定 .interpolate(interpolate); d3.csv("data/single.csv", function (error, data) { // データの読み込み data.forEach(function (d) { d.date = parseDate(d.date); d.sales = +d.sales; }); // x軸の値設定 x.domain(d3.extent(data, function (d) { return d.date; })).nice(); // nice()で軸のmax値、min値をいい感じにしてくれる // y軸の値設定 y.domain(d3.extent(data, function (d) { return d.sales; })).nice(); // nice()で軸のmax値、min値をいい感じにしてくれる // x軸の追加 svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); // y軸の追加 svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") // 文字を反時計回りに90度回転 .attr("transform", "rotate(-90)") // 文字の表示位置を指定 .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("累計売上(枚)"); // 折れ線を追加 svg.append("path") .datum(data) .attr("class", "line") .attr("d", line); |
これで、無事折れ線グラフを描写できます。
1 2 3 4 5 |
// 折れ線を追加 svg.append("path") .datum(data) .attr("class", "line") .attr("d", line); |
さてここで、datum()が出てきましたが、これはデータをバインドするために使用する関数です。
D3.jsでは、データバインドのための関数として、このdatum()以外に、もう一つdata()も存在します。
この違いは、一つの要素にバインドする場合はdatum()を、複数の要素にバインドする場合はdata()を利用するという感じらしいです。
このことは、Stack Overflowでも議論されていました。
あとは、各ポイント毎に表示しているオレンジ色の○と、テキスト部分を描画しているソースを見ていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
// 散布図 svg.selectAll("dot") .data(data) .enter() .append("circle") //各位置ごとにX軸、Y軸の値を指定 .attr("cx", function (d) { return x(d.date); }) .attr("cy", function (d) { return y(d.sales); }) //アニメーションのため最初は半径0pxで指定 .attr("r", 0) .attr("stroke", "#black") .attr("stroke-width", "1px") .attr("fill", "#FFA500") //1000msかけて半径4pxに .transition() .duration(1000) .attr("r", 4); //テキスト svg.selectAll("dot") .data(data) .enter() .append("text") .text(function (d) { return d.rank + "位"; }) .attr("font-size", "12px") .attr("fill", "#black") //各位置ごとにX軸、Y軸の値を指定 .attr("x", function (d) { return x(d.date); }) //散布図の○のちょっと上に表示 .attr("y", function (d) { return y(d.sales) - 5; }) //マウスオーバーで詳細を表示 .on("mouseover", function (d) { div.transition() .duration(500) .style("opacity", 0); div.transition() .duration(200) .style("opacity", 9); var D = new Date(d.date); div.html( '<a href= "https://www.youtube.com/results?search_query=' + d.title + '" target="_blank">' + d.title + "</a>" + "<br/>" + d.sales + "枚" + " (" + D.getUTCFullYear() + "/" + (D.getMonth() + 1) + "/" + D.getDate() + ")") .style("left", x(d.date) + "px") .style("top", y(d.sales) + "px"); }); |
これで、グラフに表示する全ての要素の記述が完成したので、ページ読み込みの際にこのdraw関数が走るように設定すればOKです。
参考にしたサイト
D3.jsで折れ線チャートを作るにあたって、以下のサイトを参考にしました。
Line Chart
D3.jsの使い方とグラフを作成するサンプル | Tips Note
D3 入門 | スコット・マレイ | alignedleft
D3.js入門 (全17回) – プログラミングならドットインストール
Heroku Button
今回Herokuにあげたアプリケーションは、READMEに置いたDeploy to Herokuボタンをクリックするだけで、各々のHeroku環境にデプロイされて自由に使うことが出来ます。
(Heroku Buttonで共有するほどのアプリケーションではないですが…)
Heroku Buttonに関して詳しくは伊藤直也さんがブログにて紹介されていますので、そちらをぜひご覧ください。
http://d.hatena.ne.jp/naoya/20140809
Heroku Button で、URI を介した Web アプリケーションの交換が可能になった。
このことに感動したので、おまけ的にこのブログで取り上げてみました。
みなさんもぜひ、どんどん活用してみてくださいm(_ _)m
まとめ
今回はPerfumeのデータを可視化したいという思いから、D3.jsで遊んでみました。
jsをゴリゴリ書いていくことで、幅広いデータの表現が可能となるので、エンジニアにとってはとても幸せなツールだと思いますヽ(*´∀`)ノ
(追伸)
この頃、関根さんに感化されてPyConに参加したり、Pythonを勉強はじめてみたりしてます。
Pythonでのデータ分析とかめちゃくちゃ楽しそうなので、追々このブログでも取り上げられたらな…と思ってます。
こんにちは。アライドには2013年に新卒で入社しました。 Perfume × ラクロス × エンジニア といった感じです。お手柔らかにお願いしますm(_ _)m