2008年8月30日

何かを見つけた、その後

おっとっと、忘れてた。

仮に、問題のポイントと相関が高いカウンターが見つかったとしよう。それで終わってくれるならば特にどうと言うことは無いだろうが、普通は
「で、それは何をどうしているって??」
と言うことを確認しなくてはいけない。もし障害分析を仕事でやっている場合ならばお客様には
「r2が 0.43 なのでこいつが問題点です」
という1行よりはもう少し親切な…そう例えばグラフ化するとか…してあげなくてはいけない。

そこで、自分が「これ」と決めたカウンターに関して、Excelシートを作る事を考えよう。

もちろん、対象となるカウンターがもう決まっているのだから、relog.exe に対象カウンター一覧を食わせて TSV ファイルを作り、それをそのまま Excel に飲ませるのだってOKだ。
ただ、このやり方だと、例えば対象となる Performance Monitor のログが30個あると、30回「新しくシートを作って、そこにTSVファイルを読み込ませて」を繰り返さなくてはいけない。はっきり言おう。私は嫌だ。

と言うわけで。Excelシートを作ってくれるスクリプトだ。

toxls.pl

#!/usr/bin/perl
# Output xls file name is described as ARGV[0];
# ARGV[1] and on should contian
# ( ARGV[odd], ARGV[odd+1] ) = ( TSV filename, sheet name ).
# And at least ARGV[1] and ARGV[2] should be given in order to have something meaningful.
# Or, you'll get empty Excel file.


use Spreadsheet::WriteExcel;
use Spreadsheet::WriteExcel::Utility;

$workbookname = shift @ARGV;
if ( !defined( $workbookname ) ) {
die "Excel WorkBook name not given\n";
}

my $workbook = Spreadsheet::WriteExcel->new($workbookname);


@sheetnames = @ARGV;

while( $sheetfilename = shift @ARGV ) {
$sheetname = shift@ARGV;
if ( !defined( $sheetname )) {
die "Excel sheetname not given for TSV file name: $sheetfilename\n";
}

readsheet( $sheetname, $sheetfilename );
}

$workbook->close();
exit 0;


sub readsheet {
my ( $sheetname, $sheetfilename ) = @_;

my $worksheet = $workbook->add_worksheet( $sheetname );
open SHEETFILE, $sheetfilename or die "unable to open filename: $sheetfilename\n";


my $linenumber = 0;
my $offset = 5;
my $maxcells = 0;

while () {
chomp;

my @cells = split /\t/;

if ( $maxcells < $#cells ) {
$maxcells = $#cells;
}

if ( $linenumber == 0 ) {
$column = $linenumber;
} else {
$column = $linenumber + $offset;
}

for ( $i = 0; $i <= $#cells; $i++ ) {
$cells[$i] =~ /^\"(.+)\"/;
my $tmp = $1;
$worksheet->write( $column, $i, $tmp );
}
$linenumber++;
}

$maxlinenum = $linenumber + $offset;

for ( $row = 0; $row <= $maxcells; $row++ ) {

# Since we need to skip first element, headofdata requires to have +1.
my $headofdata = $offset+1 +1;
my $tailofdata = $maxlinenum - 1;

my $ydatatop = xl_rowcol_to_cell( $headofdata, $row );
my $ydatatail = xl_rowcol_to_cell( $tailofdata, $row );

my $xdatatop = xl_rowcol_to_cell( $headofdata, 1 );
my $xdatatail = xl_rowcol_to_cell( $tailofdata, 1 );


my $formulacol = 1;
my $formularow = $row;
if ( $row == 0 ) {
$worksheet->write_string( $formulacol, $formularow, 'R2' );
} elsif ( $row == 1 ) {
$worksheet->write_formula( $formulacol, $formularow, "=RSQ( $ydatatop:$ydatatail, $xdatatop:$xdatatail)" );
} else {
$worksheet->write_formula( $formulacol, $formularow, "=RSQ( $ydatatop:$ydatatail, $xdatatop:$xdatatail)" );
}


$formulacol = 2;
$formularow = $row;
if ( $row == 0 ) {
$worksheet->write_string( $formulacol, $formularow, 'a' );
} elsif ( $row == 1 ) {
# do nothing
} else {
$worksheet->write_formula( $formulacol, $formularow, "=SLOPE( $ydatatop:$ydatatail, $xdatatop:$xdatatail)" );
}


$formulacol = 3;
$formularow = $row;
if ( $row == 0 ) {
$worksheet->write_string( $formulacol, $formularow, 'b' );
} elsif ( $row == 1 ) {
# do nothing
} else {
$worksheet->write_formula( $formulacol, $formularow, "=INTERCEPT( $ydatatop:$ydatatail, $xdatatop:$xdatatail)" );
}


$formulacol = 4;
$formularow = $row;
my $r2 = xl_rowcol_to_cell( 1, $formularow );
if ( $row == 0 ) {
$worksheet->write_string( $formulacol, $formularow, 'High Relation' );
} elsif ( $row == 1 ) {
# do nothing
} else {
$worksheet->write_formula( $formulacol, $formularow, "=IF( $r2>=0.25,\"*\",\"\")" );
}
}

close SHEETFILE;
}


ヘルプも何もありゃしない :p 使い方としてはこんな感じ:


% toxls.pl AFO.xls A.TSV A-dayo B.TSV B-desu


するってーと AFO.xls が作られる。開くと見えるのはこんな感じだ。



タブを見ると「A-dayo」「B-desu」というのがあるのが判るだろう。それぞれ A.TSV, B.TSVの中身がシートになった状態になっている。

こいつにはもう一つおまけの機能がある。2行目から4行目がそれぞれ「r2」「y=ax+bのa」「y=ax+bのb」になっている。これらは Perlで算出したものではなく、Excelの「式」が書いてあってあなたがこのExcelシートを開いたときに値を再計算してくれた状態なのだ。

5行目には「もしr2の値が0.25より大きかったら * を、そうでなければ何も表示しない(エラーの場合を除く)」という式が埋めてある。単なる目印のようなものだが、たまに便利だったりする。