あなたの天然記念物
ホーム更新雑談Perl鉄ゲタランドナーコースガイド自転車Linuxリンク経歴連絡先

GD::Graphを使ってグラフ作成 (2016.04.16)

きっかけ

ボーカロイド楽曲の投稿数と再生数の推移の集計結果をグラフ化したかった。

注意点(感想)

  1. set関数のハッシュで指定する設定とset_xxx関数にパラメタで指定する設定の区別(なぜ片方に統一しないのか?)
  2. 漢字対応フォントを設定して文字化けを防ぐ。(指定したフォントは別途インストール済)
  3. フォント指定は階層構造の継承がないので1つずつ全部指定する。(トップレベルの指定で、一斉に設定とか無かった)
  4. x軸のラベル(数字)のフォーマットは説明にあるけど動作しないのでデータで予め加工しておく。(元々x軸は文字列の列挙OK)

コード

#!/usr/local/bin/perl -w

use 5.10.1;
use utf8;
use strict;
use warnings;
use open IO => ":utf8";

use DateTime;
use DateTime::Format::Strptime;
use Encode;
use Encode::Locale;
use GD::Graph::lines;
use XML::Simple qw/:strict/;

use MyPrint;
use MyEnvironment;

$| = 1;

binmode STDOUT, ":encoding(console_out)";

my_status("開始");
my_print "開始";
my $strp = DateTime::Format::Strptime->new(pattern => '%Y%m');

my %month_data;
my $xml = XML::Simple->new();
my $xml_ref = $xml->XMLin("month_data.xml", ForceArray => 0, KeyAttr => "");
my @upload;
my @movie;
my @play;
for (sort {$a->{upload} cmp $b->{upload}} @{$xml_ref->{playdata}}) {
	my $dt = $strp->parse_datetime($_->{upload});
	my_print $dt->strftime("投稿月:%F");
	my $days = DateTime->from_object(object => $dt)->add(months => 1)->delta_days($dt)->in_units("days");

	push @upload, $dt->strftime("%Y");
	push @movie, $_->{movie} / $days;
	push @play, $_->{play} / $days;
}

my $gd_graph = GD::Graph::lines->new(1000, 500);
$gd_graph->set(
	t_margin => 10,
	b_margin => 10,
	l_margin => 10,
	r_margin => 10,
	transparent => 0,
	interlaced => 0,
	title => "ボーカロイド楽曲投稿と再生",
	long_ticks => 1,
	line_width => 2,

	fgclr => "black",
	legendclr => "black",

	x_ticks_length => 4,
	x_ticks => 1,
	x_label => "投稿年(1ヶ月平均)",
	x_label_position => 0.5,
	x_label_skip => 12,
	x_last_label_skip => 2,

	two_axes => 1,

	dclrs => ["black", "black"],
	line_types => [1, 3],

	y_tick_number => 15,
	y_label_skip => 5,
	y1_min_value => 0,
	y1_max_value => 150,
	y2_min_value => 0,
	y2_max_value => 1500000,
	y2_number_format => sub {return $_[0] / 10000 . "万"},

	y1_label => "投稿/日",
	y2_label => "再生/日",

	legend_placement => "BR",
	legend_marker_width => 30,
);

GD::Text->font_path("c:/windows/fonts/");
my @font_spec = qw/ ipaexg.ttf 18 /;
$gd_graph->set_title_font(@font_spec);
$gd_graph->set_x_axis_font(@font_spec);
$gd_graph->set_y_axis_font(@font_spec);
$gd_graph->set_x_label_font(@font_spec);
$gd_graph->set_y_label_font(@font_spec);
$gd_graph->set_legend_font(@font_spec);

$gd_graph->set_legend(qw/ 投稿 再生 /);
$gd_graph->set_text_clr("black");

if (open my $result_fh, ">:raw", "result.png") {
	print $result_fh $gd_graph->plot([\@upload, \@movie, \@play])->png;
	close $result_fh;
}

my_print "終了";

my_exit();


sub my_status {
	my ($name, $step, $base) = @_;
	my $st;
	if (!@_) {
		$st = "コマンド プロンプト";
	} elsif (@_ < 3) {
		$st = "$name 処理中";
	} else {
		$st = sprintf "%s %4.1f%%", $name, $step * 100 / $base;
	}

	if (IsWin()) {
		system Encode::encode SystemEncode(), "title $st";
	}
}



sub my_exit {
	my_status();
	exit;
}