GD::Graphを使ったPerlグラフ生成

PerlのGD::Graphモジュールは、データをすばやく簡単にグラフィカルに表現するためのソフトウェア開発者向けツールだ。もともとはMartien Verbruggenが1995年に作成したこのパッケージは、非常に柔軟で人気の高いツールへと成長した。データベースなどから動的データセットを即座に取得して表示したいという場合に好適である。企業のイントラネットで広く使われており、まさに経営陣が望むような形式でデータを示すグラフを生成できるので、多くのWeb管理者が利用している。

GD::Graphは、下位レベルのユーティリティモジュール集合の上に成り立っている。これらのモジュールの最も基礎的な部分は、Thomas BoutellGDグラフィックスプリミティブライブラリであり、各種の、線、多角形、テキストをメモリ内カンバスに描画する構造を提供する。このライブラリの上に、2つのPerlモジュールがある。1つは、Lincoln SteinGDモジュールで、これによって、Perl開発者は基本となるGD機能にアクセスできる。もう1つは、Martien VerbruggenのGD::Textモジュールで、これはテキストをGDベースのイメージとして描画するためのPerlインタフェースを提供する。これらのモジュール集合の最上位にGD::Graphモジュールがあり、各種下位モジュールの機能を利用してグラフのイメージを描画する。

GDライブラリは現在のすべてのLinuxディストリビューションに含まれており、他のプラットフォーム用のソースも入手可能だ。GD::GraphのPerl特有の部分は、Linuxディストリビューションに含まれているか、CPANからすべて入手できる。最新バージョンは、以下のコマンド1つで全部取得できる(ルートとして実行)。

perl -MCPAN -e 'install GD::Graph'

このインストールコマンドを完全に実行するには、PerlのCPANモジュール、GD開発パッケージCコンパイラが必要だ。現代のほとんどの開発用ワークステーションでは、これらは標準要素である。

GD::Graphの機能

GD::Graphモジュールは、データの集まりを入力として受け取り、出力としてそのデータのグラフィカル表現であるイメージを生成する。このモジュールのよさは、その卓越した柔軟性にある。折れ線グラフ、散布図、棒グラフ、面グラフ、円グラフなど、各種さまざまなグラフを生成でき、またこれらを混合して、複数のデータセットを1つのイメージとして表すこともできる。グラフの軸、目盛、色、キー、アイコン、ロゴも完全に制御できる。基本となっているGDモジュールは、非常に多くの下位レベルルーチンを提供するので、これを使ってイメージに更にテキストを追加したり、枠で囲んだり、アルファブレンディングを追加したりすることもできる。GD::Graphに付属のサンプルスクリプトで生成される各種グラフスタイルを以下に示す。

サンプルスクリプトで生成したグラフ
シンプルな使用方法

GD::Graphの一般的な使用方法には、3つのステップがある。まず、開発者はデータを取得してこれをPerlデータ構造体に変換する。次に、グラフの描画方法を具体的に指定するパラメータセットを追加して、このデータ構造体からGD::Graphオブジェクトを作成する。最後に、作成されてメモリに格納されているこのグラフイメージを、通常はファイルに保存するか、ネットワーク接続に渡すか、そのイメージを使用するアプリケーションに何らかの方法で送るなどの方法で要求元に渡す。

グラフは必ず、データセットを含む多次元配列から始める。Perlの多次元配列は、複数の配列参照を含む配列として格納される。GD::Graphデータでは、X軸のラベルが1番目の配列参照に、実際のデータセットがそれ以降の配列参照に格納される。2つのデータセットを持つGD::Graph配列を非常に単純なPerlコードで設定する場合、以下のようになる。

my @xLabels  = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my @data2002 = qw(  17  19  26  38  56  64  67  53  40  29  21  13 );
my @data2003 = qw(  19  24  27  41  56  69  75  60  44  33  22  15 );
my @data     = ( \@xLabels, \@data2002, \@data2003 );
これらの配列は、すべて同じサイズでなければならない。未知の値がある場合は、その値をundefで設定すると、その値については描画されなくなる。

ほとんどの場合、これらの各配列に名前を付ける必要はないので、通常は、以下のように無名配列を使ってデータを作成する。

my @data = ( ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
              "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
             [ 17, 19, 26, 38, 56, 64, 67, 53, 40, 29, 21, 13],
             [ 19, 24, 27, 41, 56, 69, 75, 60, 44, 33, 22, 15] );
データの準備ができたら、各グラフに対応するコンストラクタを呼び出してグラフオブジェクトを作成できる。すべてのグラフコンストラクタには、2つのパラメータがある。目的のグラフイメージの幅と高さである。したがって、たとえば、幅800ピクセル、高さ600ピクセルの新しい棒グラフオブジェクトを作成する場合、以下のように記述する。

my $graph = GD::Graph::bars->new( 800, 600 );

この段階では、新しいオブジェクトには、イメージを格納する領域があるだけだ。データ設定とイメージ作成そのものは、この後だ。次のステップは、必要な外観をグラフオブジェクトに設定することである。このステップでは、グラフオブジェクトのset()メソッドを使用する。ここではさまざまなオプションが使用可能だが、その多くについてはこの記事の後半で説明することにする。ここでは、グラフのY軸のタイトルとラベルを設定する方法を紹介する。

$graph->set( title   => "Rainfall 2002/2003",
             y_label => "Millimetres" );
これで、グラフを実際に描画できるようになった。描画操作は簡単だ。以下のメソッドを呼び出すだけでいい。

my $image = $graph->plot( \@data )

データ配列を参照として渡していることに注意してほしい。この呼び出しの戻り値は、GDライブラリからの下位レベルのイメージオブジェクトだ。グラフ描画が何らかの原因で失敗した場合には、未定義の値が返される。

最後のステップは、GDイメージを使用可能な形式に変換し、何らかの処理を行うことだ。自分のマシンにImageMagickパッケージがある場合は、以下のような方法でPNGフォーマットに変換して表示できる。

my $pngData = $image->png();
open( OUT, "| display -" ) or die( "Can't display image: $!" );
binmode OUT;
print OUT $pngData;
close OUT;

UnixまたはLinuxでは、binmodeを使って出力をバイナリモードにすることは不要だが、他のシステムではこれが必要な場合もある。

これらをまとめると、以下のようなスクリプトになる。このスクリプトは、2つのデータセットに基づく非常に単純な棒グラフを生成して表示する。

#!/usr/bin/perl -w
use strict;

use GD::Graph::bars;

my @xLabels  = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my @data2002 = qw(  17  19  26  38  56  64  67  53  40  29  21  13 );
my @data2003 = qw(  19  24  27  41  56  69  75  60  44  33  22  15 );
my @data     = ( \@xLabels, \@data2002, \@data2003 );

my $graph = GD::Graph::bars->new( 800, 600 );

$graph->set( title   => "Rainfall 2002/2003",
             y_label => "Millimetres" );

my $image = $graph->plot( \@data ) or die( "Cannot create image" );

open( OUT, "| display -") or die( "Cannot display image: $!" );
Two-data-set bar chart
binmode OUT; print OUT $image->png(); close OUT;

このスクリプトで生成されるグラフはこのようになる。

上級操作

GD::Graphモジュールの上級操作では、必要なグラフスタイルを選択し、オプションを設定してグラフの出力を調整することができる。

グラフスタイルの選択は、該当するモジュールを使用し、そのコンストラクタを呼び出すだけだ。前述の例では、棒グラフを生成するためにbarsモジュールを使用した。このモジュールの代わりに、linespointslinespointsareamixedpieを使用できる。

一方、グラフオプションの選択は複雑だ。多種多様なオプションがあり、すべての種類のグラフに適用できるものと、特定の種類のグラフだけに適用できるものがある。GD::Graphには非常に多くのオプションがあって一度にすべてを紹介することはできないので、ここではいくつかを紹介して、このモジュールの表現能力の一部を示したい。

ほとんどの種類のグラフに適用できる汎用オプションは、グラフオブジェクトのset()メソッドを使って設定する。後で説明するように、特殊なオプションを設定するための他のメソッドもある。

まず色から始めよう。グラフの色を制御す最も簡単な方法は、GD::Graph::colourモジュールを使う方法で、これはシステムカラーにアクセスする。まず、スクリプトの先頭に以下のコードを追加する。

use GD::Graph::colour qw( :files );
GD::Graph::colour::read_rgb( "/usr/X11/lib/X11/rgb.txt" );

これで、(少なくともLinuxシステムの)システムテーブルのカラー名を使用できる。モジュール名がイギリス英語スペルの”colour”なので注意しよう。このモジュールを使用すると、以下のようにset()メソッド呼び出しのオプションを追加して、元の”Rainfall”(降雨量)棒グラフのけばけばしい色を変更できる。

boxclr       => "LightGrey"
dclrs        => [ "DeepSkyBlue", "SteelBlue" ]
shadowclr    => "DarkSlateGrey"
shadow_depth => 3
最初のコード例では、Y軸の目盛の値を指定していなかったことにお気づきかもしれない。これは、GD::Graphモジュールが使用するデータに合う範囲を自動的に割り出すからだ。この機能は、y_max_valueオプションとy_min_valueオプションを設定して無効にすることができる。このグラフの場合、Y軸の最大値を80にすると、デフォルトの100より見た目がよくなる。

y_max_value => 80

set()メソッドには、このような棒グラフの外観を内容と性質に応じて調整するオプションがほかにも多数ある。たとえば、cumulate => 1overwrite => 1などを試してみるとよい。この2番目のオプションは、テストデータを使って実際に効果を確認したほうがわかりやすい。

次に、別のグラフについていくつかオプションを紹介する。以下のPerlスクリプトは、混合(mixed)タイプのグラフを生成する。このスクリプトの出力イメージをその下に示す。

#!/usr/bin/perl -w
use strict;

use GD::Graph::mixed;
use GD::Graph::colour qw( :files );
use GD::Text;

my @xLabels  = qw( 00-03 03-06 06-09 09-12 12-15 15-18 18-21 21-00 );
my @avDown   = qw(     3     3     6    24    16    16    16    12 );
my @avUp     = qw(     1     1     2     3     2     2     1     1 );
my @down     = qw(   4.2   3.5  9.25    22  17.5  14.8  12.25  8.3 );
my @up       = qw(   0.25 1.75  1.65   3.25  2.1  1.85   0.95  0.3 );
my @data     = ( \@xLabels, \@avDown, \@avUp, \@down, \@up );

my $graph = GD::Graph::mixed->new( 800, 600 );

GD::Graph::colour::read_rgb( "/usr/X11/lib/X11/rgb.txt" ) or
  die( "Can't read colours" );

$graph->set( title            => "Data flow 6th April 2004",
             t_margin         => 10,
             b_margin         => 10,
             l_margin         => 10,
             r_margin         => 10,
             x_label          => "Time (3 hour blocks)",
             x_label_position => 0.5,
             y_label          => "MB/sec",
             types            => [ qw(bars bars
                                      linespoints linespoints) ],
             dclrs            => [ qw(LightYellow1 LightYellow4
                                      orange1 orange2) ],
             y_max_value      => 28,
             y_tick_number    => 14,
             y_label_skip     => 2,
             line_width       => 2,
             long_ticks       => 1,
             bar_width        => 10,
             bar_spacing      => 3,
             markers          => [ 5, 5 ],
             legend_placement => "RT" );

$graph->set_legend( "Budgeted download",
                    "Budgeted upload",
                    "Actual download",
                    "Actual upload" );

GD::Text->font_path( "/usr/lib/X11/fonts/truetype/" );
$graph->set_title_font( "luximr", 16 );
$graph->set_legend_font( "luximr", 10 );
$graph->set_x_axis_font( "luximr", 9 );
$graph->set_x_label_font( "luximr", 11 );
$graph->set_y_axis_font( "luximr", 9 );
$graph->set_y_label_font( "luximr", 11 );

my $image = $graph->plot( \@data ) or die( "Cannot create image" );

open( OUT, "| display -") or die( "Cannot display image: $!" );
binmode OUT;
print OUT $image->png();
close OUT;
混合グラフ
混合グラフでは、1つのイメージの中で複数のデータセットを異なる方法で表示できる。typesオプションは、各データセットをどのように表示するかを指定する。この例では、最初の2つのデータセット、指定した日のネットワーク回線容量予定値を棒グラフで示し、次の2つのデータセット、実際に使用された回線容量を折れ線グラフで示す。

このグラフでは、折れ線グラフのマーカーを、塗りつぶされたひし形に設定し(markersオプション)、棒グラフの棒の間に少し間隔をあけた(bar_widthオプションとbar_spacingオプション)。また、グラフ全体を横切る目盛線を引き(long_ticksオプション)、Y軸の目盛の数と単位を設定して(y_tick_numberオプションとy_label_skipオプション)データを見やすくした。このグラフの右上には凡例も付けた。これは、グラフのset_legend()メソッドを呼び出すことで設定できる。

自分の好みのフォント処理も指定した。GD::GraphはGD::Textモジュールを使ってテキスト処理を行う。GD::Textは、ネイティブのウィンドウシステムのフォント検索を使用しないので、使用しているSUSEシステムのTrueTypeフォントディレクトリをポイントして、テキストラベルそれぞれに、実際のフォント名とサイズを指定した。特定のフォントを使用するスクリプトを配布する場合は、スクリプトと一緒にフォントファイルも配布するか、対象システムのすべてに存在するフォントだけを使用するのが賢明だ。

CGIの使用

GD::Graphの一般的な使用法の1つは、Webブラウザに表示するグラフを動的に生成することだ。PerlとCGIという昔からの信頼できる組み合わせは、依然として、多くのイントラネットWeb管理者にとって、オンザフライのグラフ生成を実装する最も単純な手段の1つである。以下の単純なCGIスクリプトは、JPEGフォーマットでグラフを生成する。

#!/usr/bin/perl -w
use strict;

use CGI;
use GD::Graph::pie;

my $cgi = CGI->new();
print $cgi->header( -type => "image/jpeg" );

my @data = ( ["Apache","IIS","Other"], [ 67.2, 21.0, 11.8] );

my $graph = new GD::Graph::pie( 250, 200 );

$graph->set( title       => "Web server Usage, March 2004",
             dclrs       => [ qw( #D6D6FF #CECECE #FFFFFF ) ],
             pie_height  => 32,
             start_angle => 90 );

print $graph->plot(\@data)->jpeg();
このスクリプトは、標準CGIモジュールを使って、要求元のブラウザに円グラフを渡す。イメージフォーマットタイプ(ここではPNGの代わりにJPEG)は、描画するデータイメージオブジェクトのそれぞれ異なるメソッドを呼び出すことで、指定する。

結論

GD::Graphモジュールは、あらゆる種類のグラフを生成できるツールとして注目に値する。これは高速で柔軟性があり、すべてフリーソフトウェアで構築されている。Web管理者の間で人気があり、データのグラフィカル表現を生成することが必要な管理者や開発者等にとっても、非常に便利である。

Derek Fountain――Linuxとオープンソーススクリプティング言語専門のフリーランスライター兼ソフトウェア開発者。ウエスタンオーストラリア州パース在住。