Perlでグラフを作ろう (GD::Graph)

Perl でいろいろとプログラムを作っていると、あるデータをグラフ化したくなってきます。そこで、グラフを作成するモジュールを探して使ってみることにします。

* モジュールの入手

Perl で、グラフを作成するのに便利なモジュールとして GD::Graph があります。まず、これを利用するのに必要なパッケージを入手しなくてはなりません。

GD Graphics Library Thomas Boutell氏の作成した、線や多角形、円を描画するためのライブラリ http://www.boutell.com/gd/
PNG graphics library PNG グラフィックスフォーマット用のライブラリ http://www.libpng.org/pub/png
zlib compression library zlib 汎用圧縮ライブラリ http://www.gzip.org/zlib/
FreeType TrueTypeフォントをレンダリングするライブラリ http://www.freetype.org/
JPEG library JPEG グラフィックスフォーマット用のライブラリ ftp://ftp.uu.net/graphics/jpeg/
GD Perl Module GD ライブラリを Perl で利用可能にするモジュール CPAN
GD::Text ( GDTextUtil) GD を利用してテキストを扱えるようにするモジュール CPAN
GD::Graph (GDGraph) GD を利用してグラフを扱えるようにするモジュール CPAN
Jcode 日本語文字コードを変換するモジュール http://openlab.ring.gr.jp/Jcode/

これらすべてが必要なわけではありません。OS によっては、すでにインストールされているものもあります。

* インストール

必要なモジュールを入手したら、それらをインストールする必要があります。OSによって、入手しなければならないモジュールが異なります。ここでは、RedHat Linux 9 を利用して説明します。RedHat Linux 9 ではほとんどのライブラリがインストールされています。以下では、必要なモジュールのインストールのみ記述しています。また、Perl のバージョンが 5.6.0 以上でなければなりません。

インストールはスーパーユーザーで作業
GD Perl モジュールのインストール
 
  # gzip -dc GD-1.41.tar.gz | tar xf -
  # cd GD-1.41
  # perl Makefile.PL
  ...
  Build JPEG support? [y] <Enter>
  Build FreeType support? [y] <Enter>
  Build XPM support? [y] <Enter>
  Build GIF support (for patched versions of gd)? [n] <Enter>
  ...
  # make
  # make test
    (このときに X Window に対して描画できる状態で無ければいくつかテストが失敗します。)
  # make html
  # make install
 
GD::Text Perl モジュールのインストール
 
  # gzip -dc GDTextUtil-0.86.tar.gz | tar xf -
  # cd GDTextUtil-0.86
  # perl Makefile.PL
  # make
  # make test
  # make demo
  # make install
 
GD::Graph Perl モジュールのインストール
 
  # gzip -dc GDGraph-1.43.tar.gz | tar xf -
  # cd GDGraph-1.43
  # perl Makefile.PL
  # make
  # make install
  # make sample
 
Jcode Perl モジュールのインストール
 
  # gzip -dc Jcode-0.83.tar.gz | tar xf -
  # cd Jcode-0.83
  # perl Makefile.PL
  # make
  # make test
  # make install

* 利用

GD::Graph の一般的な利用には、次の3つのステップを行う必要があります。

GD::Graph によって作成できるグラフには以下のようなものがあります。(一部)

Graph List

グラフは、データセットを含む多次元配列を作成するところから始まります。Perlの多次元配列は、複数の配列のリファレンスを含む配列として格納します。GD::Graphデータでは、最初にX軸のラベルの配列のリファレンス格納されます。つづいて、実際のデータセットの配列のリファレンスが格納さます。ます、単純にデータセットをひとつだけ持つグラフを作ってみましょう。

my @labels  = qw( under 10s  20s  30s  40s  50s  60s  70s over );
my @dataset = qw(   20   40   60   80   65   15   10   20    5 );
my @data    = ( \@labels, \@dataset);

配列は、すべて同じサイズでなければなりません。そこで、もし未知の値がある場合、その値をundefと設定することでその値については描画されなくなります。

上記のように記述しましたが、各配列に名前をつける必要が無いので無名配列を使ってデータを作成してしまうことも出来ます。

my @data = (["under","10s","20s","30s","40s","50s","60s","70s", "over"],
            [   20,    40,   60,   80,   65,   15,   10,   20,     5]);

データの準備ができたら、各グラフに対応するコンストラクタを呼び出してグラフ・オブジェクトを作成します。すべてのグラフ・コンストラクタには、作成したいグラフイメージの幅と高さの2つのパラメータがあります。よって、600ピクセル X 450ピクセルの棒グラフ・オブジェクトを作成する場合は以下のように記述します。

my $graph = GD::Graph::bars->new( 400, 300 );

これで、イメージを格納する領域が確保されました。実際に表現できるグラフは以下の通りです。

GD::Graph::lines 折線グラフ
GD::Graph::bars 棒グラフ
GD::Graph::points 点グラフ
GD::Graph::linespoints 点付折れ線グラフ
GD::Graph::area 面グラフ
GD::Graph::mixed 上記のグラフを組み合わせたグラフ
GD::Graph::pie 円グラフ

次に、必要な外観をグラフ・オブジェクトに設定します。ここでは、グラフ・オブジェクトのset()メソッドを使用してタイトルとグラフのY軸のラベルを設定しましょう。日本語の文字列は UTF-8 にします。

$graph->set( title   => jcode("にこにこ村の人口")->utf8,
             y_label => jcode("人数")->utf8 );

次に、日本語を表示するために使用する TrueType フォントを選ばなければなりません。

GD::Text->font_path( "./" );
$graph->set_title_font( "GOTHIC_FONT", 14 );
$graph->set_legend_font( "GOTHIC_FONT", 8 );
$graph->set_x_axis_font( "GOTHIC_FONT", 8 );
$graph->set_x_label_font( "GOTHIC_FONT", 10 );
$graph->set_y_axis_font( "GOTHIC_FONT", 8 );
$graph->set_y_label_font( "GOTHIC_FONT", 8 );

これは、カレントディレクトリにある GOTHIC_FONT.ttf という TrueType フォントをそれぞれのラベルでそれぞれのフォントサイズで利用することを意味します。

これで、グラフを実際に描画できるようになりました。オブジェクトの作成は、plot()メソッドを使用します。

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

データの配列のリファレンスを渡しています。何らかの原因でグラフの描画が失敗した場合、未定義の値が返されます。

最後に、作成したオブジェクトを利用可能な形式に変換します。JPEG形式のファイルを作ってみましょう。

open( OUT, "> graph.jpg") or die( "Cannot open file: graph.jpg" );
binmode OUT;
print OUT $image->jpeg();
close OUT;

これで、graph.jpg という名前で JPEG イメージが出来上がりました。出力先や出力できる画像フォーマットはいくつかありますが、実際に Web などで利用することを考えると以下の通りです。

open( OUT, "> filename") ファイル "filename" へ出力
open( OUT, "| display -") ImageMagickによる X Window への表示
print "Content-type: image/jpeg;\n\n";
binmode STDOUT;
ブラウザに返す
jpeg() JPEG フォーマット
png() PNG フォーマット

今までの内容を、実際に実行できるようにコードをまとめてみましょう。

#!/usr/bin/perl -w
use strict;
use Jcode;
 
use GD::Graph::bars;
 
my @labels  = qw( under 10s  20s  30s  40s  50s  60s  70s over );
my @dataset = qw(   20   40   60   80   65   15   10   20    5 );
my @data    = ( \@labels, \@dataset);
 
my $graph = GD::Graph::bars->new( 400, 300 );
 
$graph->set( title   => jcode("にこにこ村の人口")->utf8,
             y_label => jcode("人数")->utf8 );
 
GD::Text->font_path( "./" );
$graph->set_title_font( "GOTHIC_FONT", 14 );
$graph->set_legend_font( "GOTHIC_FONT", 8 );
$graph->set_x_axis_font( "GOTHIC_FONT", 8 );
$graph->set_x_label_font( "GOTHIC_FONT", 10 );
$graph->set_y_axis_font( "GOTHIC_FONT", 8 );
$graph->set_y_label_font( "GOTHIC_FONT", 8 );
 
my $image = $graph->plot( \@data );
 
open( OUT, "> graph.jpg") or die( "Cannot open file: graph.jpg" );
binmode OUT;
print OUT $image->jpeg();
close OUT;

出来上がった JPEG ファイル(graph.jpg )は、以下のようになります。

Graph (Bar)

ここまでで、GD::Graph の機能が簡単に理解できたと思います。非常に簡単にグラフが作れるので大変便利なモジュールです。つづいて、もう少し複雑なグラフを作ってみましょう。

GD::Graphモジュールは、グラフ・スタイルの選択とオプションの設定によりグラフの出力を調整することができます。グラフのスタイルは、上記の表の一覧を確認してください。オプションは、すべてのグラフに共通のものと固有のものがあります。ここですべては紹介できないので、よく使いそうなものをピックアップして紹介します。

まず、フォントの設定ですが、これも既に上記で説明しているので省略します。日本語の TrueType フォントを指定し、漢字コードを UTF-8 にすればほぼ問題なく表示されます。

続いて、色の指定ですが、通常 16 進数を用いた色の指定方法(#FF0000)で行えるが、色の名前を用いて色指定を行えると便利なのではないかと考えます。それを実現する機能が GD::Graph::colourモジュールを使う方法です。ううむ、スペルがイギリス英語だなあ〜

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

これで、X Windows System のカーラーテーブルのカラー名を使用できるようになります。実際の色は、xcolors コマンドで確認できます。

boxclr       => "WhiteSmoke"
dclrs        => [ "SlateGray" ]
shadowclr    => "chocolate"
shadow_depth => 3

これらは、グラフ・オブジェクトのset()メソッドで設定します。では、「にこにこ村の人口」のグラフの色を変更してみましょう。画像フォーマットも PNG に変更してみます。

Graph (Bar)

オプション設定の多くは、グラフ・オブジェクトのset()メソッドを使って設定します。よく使いそうな設定をリストしておきます。

title グラフのタイトル
t_margin Top マージン
b_margin Bottom マージン
l_margin Left マージン
r_margin Right マージン
x_label X軸ラベル
y_label Y軸ラベル
y_max_value Y軸の最大値
y_tick_number Y軸を刻む数
y_label_skip Y軸ラベルのスキップ
types mixed タイプのグラフの場合、各データをどの種類のグラフにするか無名配列でリストする
cumulate データセットが積算される(棒グラフ、面グラフ)
line_types 線グラフの線の種類を無名配列でリストする( 1:実線 2:ダッシ 3:点線 4:点線 )
line_width 線グラフの太さ
markers 点、点付折れ線グラフで使われる点の種類を無名配列でリストする
( 1:塗り四角 2:四角 3:十字 4:クロス十字 5:塗り菱形 6:菱形 7:塗り丸 8:丸 )
marker_size 点、点付折れ線グラフで使われる点のサイズ(デフォルト 4)
bar_width 棒グラフの幅
bar_spacing 棒グラフ間の幅
bgclr グラフの背景色
fgclr グラフの前景色
boxclr グラフ内の背景色
dclrs グラフの色を無名配列でリストする
accentclr グラフの外枠の色
shadowclr グラフの影の色
shadow_depth グラフの影の幅

また、よく使いそうなメソッドもリストしておきます。

set_legend() 凡例となる文字列をリストする
set_text_clr() 文字の色

最後に、ちょっと複雑なグラフと円グラフを作ってみましょう。まず、棒グラフと折れ線グラフの混在したグラフ。

#!/usr/bin/perl -w
use strict;
  
use GD::Graph::mixed;
use GD::Graph::colour qw( :files );
use GD::Text;
use Jcode;
  
GD::Graph::colour::read_rgb( "/usr/lib/X11/rgb.txt" ) or
  die( "Can't read colours" );
  
my @labels = qw( under 10s  20s  30s  40s  50s  60s  70s over );
my @male   = qw(    8   20   25   33   29    8    7    8    0 );
my @female = qw(   12   20   35   47   36    7    3   12    5 );
my @differ = qw(    1    3    9   10    2    1    5    2    0 );
my @data   = ( \@labels, \@male, \@female, \@differ);
  
my $graph = GD::Graph::mixed->new( 400, 300 );
  
$graph->set( title            => jcode("にこにこ村の人口")->utf8,
             t_margin         => 10,
             b_margin         => 10,
             l_margin         => 10,
             r_margin         => 10,
             x_label          => jcode("年齢")->utf8,
             x_label_position => 0.5,
             y_label          => jcode("人数")->utf8,
             types            => [ qw(bars bars linespoints) ],
             dclrs            => [ qw(AliceBlue LavenderBlush DarkSalmon) ],
             boxclr           => "snow",
             y_tick_number    => 10,
             y_label_skip     => 2,
             line_width       => 1,
             markers          => [ 7 ],
             marker_size      => 2,
             bar_width        => 10,
             bar_spacing      => 3,
             cumulate         => 1
           );
  
$graph->set_legend( jcode("男性")->utf8,
                    jcode("女性")->utf8,
                    jcode("前年差")->utf8);
  
GD::Text->font_path( "./" );
$graph->set_title_font( "GOTHIC_FONT", 12 );
$graph->set_legend_font( "GOTHIC_FONT", 7 );
$graph->set_x_axis_font( "GOTHIC_FONT", 7 );
$graph->set_x_label_font( "GOTHIC_FONT", 9 );
$graph->set_y_axis_font( "GOTHIC_FONT", 7 );
$graph->set_y_label_font( "GOTHIC_FONT", 7 );
  
my $image = $graph->plot( \@data ) or die( "Cannot create image" );
  
open( OUT, "> graph2.png") or die( "Cannot open file: graph2.png" );
binmode OUT;
print OUT $image->png();
close OUT;

Graph (Bar & Line)

 

円グラフ

#!/usr/bin/perl -w
use strict;
  
use GD::Graph::pie;
use GD::Graph::colour qw( :files );
use GD::Text;
use Jcode;
  
GD::Graph::colour::read_rgb( "/usr/lib/X11/rgb.txt" ) or
  die( "Can't read colours" );
  
my @data = ( [ jcode("好き")->utf8, jcode("嫌い")->utf8,
               jcode("どちらともいえない")->utf8, jcode("無回答")->utf8 ],
             [ 43, 15, 32, 10 ] );
 
my $graph = GD::Graph::pie->new( 400, 300 );
 
$graph->set( title          => jcode("にこにこ村について")->utf8,
             t_margin       => 10,
             b_margin       => 10,
             l_margin       => 10,
             r_margin       => 10,
             axislabelclr   => 'black',
             dclrs          => [ qw(MediumSlateBlue MediumSeaGreen DarkSalmon FloralWhite) ],
             pie_height     => 36,
             start_angle    => 230,
             transparent    => 0,
           );
 
GD::Text->font_path( "./" );
$graph->set_title_font( "GOTHIC_FONT", 12 );
$graph->set_value_font( "GOTHIC_FONT", 8 );
 
my $image = $graph->plot( \@data ) or die( "Cannot create image" );
 
open( OUT, "> graph3.png") or die( "Cannot open file: graph3.png" );
binmode OUT;
print OUT $image->png();
close OUT;

Graph (Circle)

* Solaris でperl モジュールコンパイル時のエラーに対処

Solarisのパッケージに含まれている perl と gcc を利用して perl モジュールを作成しようした際にエラーが出てコンパイルできない場合があります。そのような場合 perlgcc (/usr/perl5/bin/perlgcc) を利用します。

    # perl Makefile.PL
    # make
    /usr/ucb/cc:  language optional software package not installed
    ...
    # perl Makefile.PL CC=gcc
    # make
    gcc: unrecognized option `-KPIC'
    gcc: language ildoff not recognized
    ...
    # perlgcc Makefile.PL
    # make

perlgcc が存在しない場合、perl Makefile.PL によって生成された Makefile を以下のように修正するとよい...

修正前 修正後
CCCDLFLAGS = -KPIC CCCDLFLAGS =
OPTIMIZE = -xO3 -xspace -xildoff OPTIMIZE =
    # perl Makefile.PL CC=gcc
    # edit Makefile
    # make
    # make install

これを毎回行うのは面倒で gcc しか利用しないなら Config.pm を編集してしまうという荒業があります。たとえば、 Intel 版 Solaris の Perl 5.8.4 の場合。

/usr/perl5/5.8.4/lib/i86pc-solaris-64int/Config.pm の編集
修正前 修正後
cc='cc' cc='gcc'
ccname='cc' ccname='gcc'
cccdlflags='-KPIC' cccdlflags=''
ld='cc' ld='gcc'
optimize='-xO3 -xspace -xildoff' optimize=''
'cc' => 'cc' 'cc' => 'gcc'

これで、デフォルトで gcc を利用するようになります。 この作業は、perlgcc が存在しない場合の応急措置です。Solarisのパッケージに含まれる Perl ではなく、gcc を利用してソースから Perl をビルドしたほうがよいかもしれません。