FlotとCSV

Linux

はじめに

あるデータをグラフに描画する必要がありました。
単なるグラフでなく、グラフをクリックすると、該当する場所の値をポップアップで表示したいとの要望です。

自分でWordPressを使っていることもあるせいか、すぐ思いついたのはWebのアクセス統計解析などで使われているグラフでした。
で、探してみたところ、要件に合うのは以外に少なく、
Timeplotは、サーバーサイドだし、データ数が多いと遅いので×。
(TimePlotもローカルで動作するのを確認した。)

HighChartsは、データ数が5000個くらいまでならO.K.ですが、数万個だと遅すぎて×。
個人的にHighChartsが好きだったのですが、遅いのが致命的なので、データ数が少ない場合に使うことにします。
結果、Flotを使うことにしました。
今回は付属サンプルにある「interacting.html」を改良して、CSVファイルを読み込んでグラフを表示するようにしました。
CSV(カンマ、又はタブ区切り)のデータは、「jquery.csv.js」を利用して読み込んでいます。
読み込むファイル名をJavaScriptファイルに記述する必要があるのが面倒ですが、暇を見つけてユーザが指定出来るように改造しようと思います。

今回は散布図なのですが、1個の点が何を表しているのかラベルを付けて、値とともにポップアップで表示させています。
これは、「mydata.js」の「$(“#placeholder”).bind(“plothover”, function (event, pos, item)」で行っています。
「item」オブジェクトが持っている情報は、「API.txt」の633行目あたりに記述されています。

ただ、「IE ver.8」はFlotをtrunk版や、「ExplorerCanvas」を最新のモノに入れ替えても動作しません。(他のバージョンは未確認。)
HighchartsとCSVを使ったグラフの記事もあります。

【追記2010.09.13】
revulo様に紹介頂いた「FlashCanvas」を使うようにソースを修正しました。

環境

  • Flot ver.0.6
  • JQuery ver.1.3.2
  • JQuery ver.1.4.2
  • jquery.csv.js ver.1.0
  • flashcanvas.js Free edition ver.1.3

今回、必要な機能

  • 数万個のデータを(ある程度)ストレス無く扱える。
  • グラフをクリックすると、該当する場所の値をポップアップで表示できる。
  • CSVなどの外部ファイルからデータを読み込める。
  • ローカルで使用可能。
  • 散布図(scatter)が描画可能。
  • カスタマイズが容易。
  • 見た目が綺麗。

サンプルデータ

適当に作ったものです。
js_graph_data.zip

グラフのサンプル

上記データをグラフ表示したもの。

必要なファイル

  • Flot(Googleで検索。JQueryも含まれています。)
  • サンプルデータ(カンマ、又はタブ区切り)(上記)
  • jquery.csv.js(Googleで検索)
  • mydata.js(下記)
  • XHTMLファイル(下記)
  • layout.css(不要かも。Flotの「examples」にあります。)

動作確認

JQueryの「ver.1.3.2」、「ver.1.4.2」の両方で動作したもの。

  • WindowsXP pro SP3 32bit
  • Mozila Firefox ver.3.6.9
  • Opera ver.10.61
  • Safari v.5.0.2
  • Google Chrome v.6.0.572.55
  • Internet Explorer ver.8.0.6001.18702

IEは、「ExplorerCanvas」の代わりに「FlashCanvas」を使って表示出来ることを確認した。
Chromeはローカル×、リモート○。
以下、「ツール」→「JavaScriptコンソール」のエラーメッセージ。

XMLHttpRequest cannot load file:///D:hoge.txt. Origin null is not allowed by Access-Control-Allow-Origin.

動作確認

JQueryの「ver.1.3.2」、「ver.1.4.2」の両方で動作したもの。

  • MacOS X ver.10.4.11
  • Mozila Firefox ver.3.6.3
  • Safari v.4.1.1

itemオブジェクトの情報

「API.txt」の633行目あたりに記述されている。

item: {
datapoint: the point, e.g. [0, 2]
dataIndex: the index of the point in the data array
series: the series object
seriesIndex: the index of the series
pageX, pageY: the global screen coordinates of the point
}

Flot

右上メニューにある「Github」も利用し易い。
http://www.flotcharts.org/

JQuery

jQuery
jQuery: The Write Less, Do More, JavaScript Library

FlashCanvas

Google Code Archive - Long-term storage for Google Code Project Hosting.

ExplorerCanvas

ExplorerCanvas

html

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
<title>Graph</title>
<link href="layout.css" rel="stylesheet" type="text/css"></link>
<!--[if IE]>
<script type="text/javascript" src="./flashcanvas.js"></script>
<![endif]-->
<!-- <script type="text/javascript" src="./jquery-1.4.2.min.js"></script> -->
<script type="text/javascript" src="./flot/jquery.min.js"></script>
<script type="text/javascript" src="./flot/jquery.flot.js"></script>
<script type="text/javascript" src="./jquery.csv.js"></script>
<script type="text/javascript" src="./mydata.js"></script>
</head>
<body>
<h1>Graph</h1>

<div id="placeholder" style="width:800px; height:500px"></div>

<!--
<p>Try pointing and clicking on the points.</p>
<p id="hoverdata">Mouse hovers at (<span id="x">0</span>, <span id="y">0</span>).
<span id="clickdata"></span></p>
-->

</body>
</html>

mydata.js

このファイルでデータの読み込みと、グラフの外観、挙動を制御します。
「var csv = $.csv(“\t”)( data );」で、タブ区切りのテキストファイルを読み込んでいます。
「var csv = $.csv()( data );」にすれば、カンマ区切りのテキストファイルを読み込めます。
詳細は、「jquery.csv.js」に記述されています。
以下は、4列のデータであると仮定しています。
サンプルデータは3列しかありませんが、動作には問題ありません。
「jquery.csv.js」は改行コードを指定出来るように改造したものを使っています。

$.get('hoge.txt', function( data ) {
    var mydata       = [];
    var mydata_label = [];
    var mydata_des   = [];
    var csv          = $.csv("\t")( data );
    var digit        = 3;
    // var axix_y_min   = 0;
    // var axix_y_max   = 20;

    // read csv file
    $( csv ).each( function() {
        mydata.push( [ parseInt( this[0] ), parseFloat( this[1] ) ] );
        mydata_label.push( [ this[2] ] );
        mydata_des.push( [ this[3] ] );
    });

    var plot = $.plot($("#placeholder"),
    [ { data: mydata, label: "All"} ], {
        series: {
            points: { show: true }
        },
        grid: { hoverable: true, clickable: true },
        // yaxis: { min: axix_y_min, max: axix_y_max }
    });

    function showTooltip(x, y, c1, c2, c3) {
        $('<div id="tooltip">;' + c1 + '<br />;' + c2  + '<br />;' + c3 + '</div>;').css( {
            position: 'absolute',
            display: 'none',
            top: y + 5,
            left: x + 5,
            border: '1px solid #fdd',
            padding: '2px',
            'background-color': '#fee',
            opacity: 0.80
        }).appendTo("body").fadeIn(200);
    }

    var previousPoint = null;
    $("#placeholder").bind("plothover", function (event, pos, item) {
        $("#x").text(pos.x.toFixed( digit ));
        $("#y").text(pos.y.toFixed( digit ));

        if (item) {
            if (previousPoint != item.datapoint) {
                previousPoint = item.datapoint;

                $("#tooltip").remove();
                var x = item.datapoint[0].toFixed( digit );
                var y = item.datapoint[1].toFixed( digit );

                x = parseInt( x );
                // showTooltip(item.pageX, item.pageY,
                //  item.series.label + "->;" +
                //  mydata_label[ item.dataIndex ] +
                //  " " + "(" + x + "," + y + ")");
                showTooltip(item.pageX, item.pageY,
                mydata_label[ item.dataIndex ],
                "(" + x + " , " + y + ")",
                mydata_des[ item.dataIndex ]);
            }
            else {
                $("#tooltip").remove();
                previousPoint = null;
            }
        }
    });

    $("#placeholder").bind("plotclick", function (event, pos, item) {
        if (item) {
            $("#clickdata").text("You clicked point array index: " + item.dataIndex + " in " + item.series.label + ".");
            // $("#clickdata").text("You clicked point " + item.dataIndex + " in " + item.series.label + ".");
            plot.highlight(item.series, item.datapoint);
        }
    });
});

Comments