はじめに
データを加工してグラフ画像を作成する必要がありました。
棒グラフを積み重ねる必要があったので、当初はMATLABを使う予定でした。
しかし、色々調べるとPerlの「GD::Graph」モジュールも綺麗そうだと分かったので使ってみました。
フォントさえ用意すればLinux、Windows関係なく日本語も表示出来ます。
環境1
「Perl ver.5.12」では「Statistics::Lite」がインストール出来なかったので「ver.5.10」を使用した。
- WindowsXP Professional SP3 32bit
- Strawberry Perl ver.5.10.1.5
- GDGraph ver.1.44_01
環境2
- Debian squeeze 32bit
- Perl ver.5.10.1
- libgd-graph-perl(GDGraph ver.1.44-3)
データ
テキストデータだけでPerlのスクリプトは含まれません。
wp.zip
日本語フォント Migu 1P
「MigMix-1P-20110610」に含まれる「MigMix-1P-bold.ttf」を使いました。
http://mix-mplus-ipa.sourceforge.jp/migmix/
自分用のメモ
X軸は「control.txt」の目盛りを基準とするため、「control.txt」の平均値を使う。
このときにX軸データのMin、Maxが必要なので、予め、全てのデータを連結したファイルを作成して読み込む。
GDモジュールで画像サイズを小さくすると目盛り値が重なってしまうので、ある程度大きいサイズを設定する。
Y軸の値は0~Nの整数値なので、それぞれの値毎に配列を準備(二次元配列で対応)して、各々が別の棒グラフになるようにする。
それを「GD::Graph」を使って棒グラフを積み重ねることで色分けする。
「control.txt」の平均値を計算してX軸の中心とする。
X軸のデータの最小値、最大値を求めて、それらが含まれるようにX軸の目盛りの数を設定する。
一目盛り幅は、平均値の20%とする。
その目盛りの幅がBIN幅となり、その範囲に含まれるデータを加算して棒グラフで表示する。
BIN幅の左の目盛りは~以上、右の目盛りは~未満とする。
グラフ図
ソース
UTF8で保存。
#!/usr/bin/perl use strict; use warnings; use utf8; use Encode; #use GD::Graph::mixed; use GD::Graph::bars; #use GD::Graph::hbars; use Statistics::Lite qw( :all ); #use POSIX; # ceil #use Math::Round; # 四捨五入 { my $infile = "control.txt"; # my $infile = "data1.txt"; # my $infile = "data2.txt"; # control.txt、data1-2を合体させたもの。 my $allfile = "data_all.txt"; # GDで使用するフォント my $font_name = "MigMix-1P-bold.ttf"; # 平均値の20%を一目盛の幅とする my $ratio = 0.2; # $allfileの読み込み open my $h_inall, "<", $allfile or die "$!: $allfile"; my @data_all = <$h_inall>; close( $h_inall ); my $ct_all = 0; my @data_x_all = (); my $tmp; foreach my $i ( @data_all ) { ( $data_x_all[ $ct_all ], $tmp ) = split(/\t/, $i); chomp( $data_x_all[ $ct_all ] ); $ct_all++; } # 個別のデータ読み込み open my $h_in, "<", $infile or die "$!: $infile"; my @data = <$h_in>; close( $h_in ); my $ct = 0; my @data_x = (); my @data_y = (); foreach my $i ( @data ) { ( $data_x[ $ct ], $data_y[ $ct ] ) = split(/\t/, $i); chomp( $data_x[ $ct ] ); chomp( $data_y[ $ct ] ); $ct++; } my $mean_x = mean @data_x; # my $stddev_x = stddev @data_x; # print "mean x org: $mean_x \n"; # print "width org: " . ($mean_x * $ratio) . "\n"; # print "min x: " . (min @data_x) . "\n"; # X軸の目盛りをcontrol.txtのデータに合わせる $mean_x = 0.894063333333333 if ( $infile !~ /control/ ); # データの最小値・最大値 # my $min_x = min @data_x; # my $max_x = max @data_x; my $min_x = min @data_x_all; my $max_x = max @data_x_all; # print "min x: $min_x \n"; # print "max x: $max_x \n"; # X軸の目盛りの分割数(プラス側・マイナス側) # 小数点以下を切り捨てる。 my $div_x_nega = abs( $mean_x - $min_x ) / ( $ratio * $mean_x ); my $div_x_plus = abs( $mean_x - $max_x ) / ( $ratio * $mean_x ); $div_x_nega = sprintf( "%d", $div_x_nega ); $div_x_plus = sprintf( "%d", $div_x_plus ); $div_x_nega += 1; $div_x_plus += 1; # X軸のマイナス側のBIN幅(二度手間だが、出力用に使う) my $r1; my $r2; my @bin_width_nega = (); my @bin_width_plus = (); my @bin_width = (); for ( my $i = 0; $i < $div_x_nega; $i++ ) { $r1 = $mean_x * ( 1 - ( 1 + $i ) * $ratio ) ; $r2 = $mean_x * ( 1 - $i * $ratio ) ; $bin_width_nega[ $i ] = $r1 . "\t" . $r2; # print "- $i $i: $data_x[ $i ] $range1 $r2 \n"; } @bin_width_nega = reverse @bin_width_nega; # X軸のプラス側のBIN幅(二度手間だが、出力用に使う) for ( my $i = 0; $i < $div_x_plus; $i++ ) { $r1 = $mean_x * ( 1 + $i * $ratio ); $r2 = $mean_x * ( 1 + ( 1 + $i ) * $ratio ); $bin_width_plus[ $i ] = $r1 . "\t" . $r2; # print "+ $i $i: $data_x[ $i ] $range1 $r2 \n"; } @bin_width = ( @bin_width_nega, @bin_width_plus ); # 実際の目盛(X軸のマイナス側) my @scale_x_nega = (); for ( my $i = 0; $i < $div_x_nega; $i++ ) { $scale_x_nega[ $i ] = $mean_x - ( $ratio * $mean_x ) / 2 - $i * $ratio * $mean_x } @scale_x_nega = reverse @scale_x_nega; # 実際の目盛(X軸のプラス側) my @scale_x_plus = (); for ( my $i = 0; $i < $div_x_plus; $i++ ) { $scale_x_plus[ $i ] = $mean_x + ( $ratio * $mean_x ) / 2 + $i * $ratio * $mean_x } my @scale_x = ( @scale_x_nega, @scale_x_plus ); # X軸の目盛りの桁を統一する foreach my $i ( @scale_x ) { $i = sprintf( "%0.3f", $i ); } # print "@scale_x \n\n"; # 結果を格納する配列の初期化 my @result = (); for ( my $i = 0; $i <= @data_y; $i++ ) { for ( my $j = 0; $j < $div_x_nega + $div_x_plus; $j++ ) { $result[ $i ][ $j ] = 0; } } # BIN幅に合わせたデータの仕分け my $range1; my $range2; my $range_found; for ( my $i = 0; $i < @data_x; $i++ ) { # print "$i: $data_x[ $i ] $data_y[ $i ] \n"; $range_found = 0; # X軸のマイナス側 for ( my $k = 0; $k < $div_x_nega; $k++ ) { $range1 = $mean_x * ( 1 - ( 1 + $k ) * $ratio ) ; $range2 = $mean_x * ( 1 - $k * $ratio ) ; if ( $range1 <= $data_x[ $i ] &amp;&amp; $data_x[ $i ] < $range2 ) { # print "- found $i $k: $data_x[ $i ] $data_y[ $i ] $range1 $range2 \n"; $result[ $data_y[ $i ] ][ $div_x_nega - 1 - $k ] += $data_y[ $i ]; # 加算する $range_found = 1; last; } } next if ( $range_found == 1 ); # X軸のプラス側 for ( my $j = 0; $j < $div_x_plus; $j++ ) { $range1 = $mean_x * ( 1 + $j * $ratio ); $range2 = $mean_x * ( 1 + ( 1 + $j ) * $ratio ); if ( $range1 <= $data_x[ $i ] &amp;&amp; $data_x[ $i ] < $range2 ) { # print "+ found $i $j: $data_x[ $i ] $data_y[ $i ] $range1 $range2 \n"; $result[ $data_y[ $i ] ][ $j + $div_x_nega ] += $data_y[ $i ]; # 加算する last; } } } # Y値の重複データを削除、数値の昇順でソートする my %count = (); my @data_y_uniq = grep( !$count{$_}++, @data_y ); @data_y_uniq = sort {$a <=> $b} @data_y_uniq; # GDに渡す配列に、X軸の目盛りを格納した配列を代入する my @plot_data = (); push @plot_data, \@scale_x; # データ整理 foreach my $i ( @data_y_uniq ) { my @data = (); for ( my $j = 0; $j < $div_x_nega + $div_x_plus; $j++ ) { print "Error: $i $j \n" if ! defined $result[ $i ][ $j ]; push @data, $result[ $i ][ $j ]; } push @plot_data, \@data; } # データ範囲毎に標準出力に出力する(確認用) for ( my $j = 0; $j < $div_x_nega + $div_x_plus; $j++ ) { my @data = (); foreach my $i ( @data_y_uniq ) { print "Error: $i $j \n" if ! defined $result[ $i ][ $j ]; push @data, $result[ $i ][ $j ]; } my $sum = sum @data; # print $scale_x[ $j ] . "\t" . $sum . "\n"; print $bin_width[ $j ] . "\t" . $sum . "\n"; } # グラフ描画 # my $gd = GD::Graph::mixed->new( 1280, 1024 ); my $gd = GD::Graph::bars->new( 1280, 1024 ); # 全てのグラフを棒グラフにする my @gd_types = (); for ( my $i = 0; $i < @data_y_uniq; $i++ ) { $gd_types[ $i ] = 'bars'; } # 「GD::Graph::mixed」のオプション $gd->set( title => encode('utf-8', $infile), t_margin => 10, b_margin => 10, l_margin => 10, r_margin => 10, x_label => encode('utf-8', "横軸"), x_label_position => 0.5, y_label => encode('utf-8', "縦軸"), types => [ @gd_types ], y_tick_number => 10, y_label_skip => 2, bar_width => 20, bar_spacing => 3, # overwrite => 1, # 「$gd = GD::Graph::mixed」の場合は不要。 cumulate => 1 # 棒グラフを縦に積み重ねる場合は「1」 ); # フォントの設定 GD::Text->font_path("./"); $gd->set_title_font( $font_name, 14 ); $gd->set_legend_font( $font_name, 8 ); $gd->set_x_axis_font( $font_name, 8 ); $gd->set_x_label_font( $font_name, 10 ); $gd->set_y_axis_font( $font_name, 8 ); $gd->set_y_label_font( $font_name, 8 ); # $gd->set_legend( encode(utf-8, "xxx"), # encode(utf-8, "yyy"), # encode(utf-8, "zzz"); my $img = $gd->plot( \@plot_data ) or die( "Cannot create image" ); # ファイルに保存する my $imagefile = "gd-" . $infile . ".jpg"; open my $h_gd, ">", "$imagefile" or die "$!: $imagefile"; binmode $h_gd; print $h_gd $img->jpeg(); close $h_gd; exit; }
Comments