プロ野球が今年も終盤に入ってきました。交流戦の時期にプロ野球勝率予測計算Perlスクリプトというのを書いて、どのチームの優勝の可能性が強そうかを見るために当日以後の勝率を算出していました。

しかし、そのスクリプトだけでは欲しい出力を得られず、パイプでsortを通して出力を並べ替える必要がありました。これをパイプを通さずにスクリプトのみで並べ替えて出力できないかと思い、手を加えてみました。

使い方は変わらないのですが、一応、簡単に記しておきます。

書式

winrate.pl [オプション] 戦績データ

オプション

  • -d
  • 引き分けを考慮しない(非現実的な引き分け数を多数表示することがあるので)
  • -i
  • 交流戦の試合数で計算。このオプションが無くとも戦績データのチーム数が6を超過していれば交流戦として計算します。
    来年2015年は18試合に減らされるということが取り決められたようなので、その試合数に合わせて変更する必要があるでしょう。
  • -r
  • 全試合数ではなく残り試合数で表示。
  • -s
  • 勝率で並べ替えをせずに表示。(改良する前のままの出力です。)

戦績データ

ファイルは次のような書式のテキストファイルです。それぞれの要素はタブで区切ります。

[チーム名](タブ)[勝ち数](タブ)[負け数](タブ)[引き分け数]
チーム名は簡単のためアルファベット2文字以内で記述することとします。

2014/9/21時点のパ・リーグでは次のようになります。
H	77	55	6
B	74	57	2
F	66	64	3
M	61	74	2
L	58	70	4
E	59	72	0
これを例(ファイル名をp20140921.txt)としてスクリプトで、引き分け無し、残り試合での表示で出力するようにオプションを指定して処理します。
winrate.pl p20140921.txt -d -r | head -20
と入力すると次のように表示されます。
T    W  - L   ave.
H     6 -  0  0.6014
B    11 -  0  0.5986
H     5 -  1  0.5942
B    10 -  1  0.5915
H     4 -  2  0.5870
B     9 -  2  0.5845
H     3 -  3  0.5797
B     8 -  3  0.5775
H     2 -  4  0.5725
B     7 -  4  0.5704
H     1 -  5  0.5652
B     6 -  5  0.5634
H     0 -  6  0.5580
B     5 -  6  0.5563
B     4 -  7  0.5493
F    11 -  0  0.5461
B     3 -  8  0.5423
F    10 -  1  0.5390
B     2 -  9  0.5352
首位のホークスの目線でこれを見てみます。

9/21の時点でホークスとバファローズの直接対決は1試合残っています。ホークスがバファローズ以外のチームに全勝した場合の勝率は.5942でバファローズはホークス戦を含めて全勝すればこれを上回ることができるので自力優勝の可能性があり、ホークスのマジックは点灯しません。負け数に着眼すると今後、ホークスはバファローズの負け数以下の負け数であれば優勝することが分かります。

ソースは次のとおりです。
#!/bin/perl -w
# 残り試合の勝率計算

use Getopt::Long;

GetOptions('i' => \$interleague, # 交流戦
	   'r' => \$rest,	 # 残り試合表示
	   'd' => \$no_draw,	 # 引き分けを含めない
	   's' => \$not_sort,	 # 勝率で並べ替えない
);

open(DATAFILE, "< $ARGV[0]") or die("指定ファイルエラー\n");

$nl = 0;			# 行番号(チーム識別)
while ($line = <DATAFILE>) {	   # 読み込み
    chomp($line);		# 行末の改行を削る
    next if ($line =~ /^\s*$/);	# 空行は無視

    # 要素で分割
    ($team[$nl], $win[$nl], $lose[$nl], $draw[$nl]) = split(/\t/, $line);
    $nl++;
}

if ($interleague || $nl > 6) {
    $games = 24;		# 交流戦試合数
}
else {
    $games = 144;		# リーグ戦試合数
}

@calc_ave = ();			# 勝率計算結果

for ($i = 0; $i < $nl; $i++) {	# 各チームの今後の勝率算出
    $left = $games - $win[$i] - $lose[$i] - $draw[$i]; # 残り試合
    for ($w = $left; $w >= 0; $w--) {		       # 勝ち数
	for ($l = 0; $l <= $left - $w; $l++) {	       # 負け数
	    $l = $left - $w if ($no_draw); # 引き分け数を加味しない
	    $d = $left - $w - $l;	   # 引き分け数
	    $ave = ($win[$i] + $w) / ($win[$i] + $w + $lose[$i] + $l); # 勝率

	    # 戦績が確定したチームには"*"を付ける
	    $team[$i] = $team[$i] . "*" unless ($left);

	    # 計算結果の要素をまとめる
	    if ($rest) {	# 残り試合数で表示
		$elems = {
		    "team" => $team[$i],
		    "win" => $w,
		    "lose" => $l,
		    "draw" => $d,
		    "ave" => $ave
		};
	    }
	    else {		# 全試合数で表示
		$elems = {
		    "team" => $team[$i],
		    "win" => $win[$i] + $w,
		    "lose" => $lose[$i] + $l,
		    "draw" => $draw[$i] + $d,
		    "ave" => $ave
		};
	    }
	    push(@calc_ave, $elems);
	}
    }
}

unless ($not_sort) {		# 勝率で降順並べ替え
    @calc_ave = sort {$b->{"ave"} <=> $a->{"ave"}} @calc_ave;

    # 並べ替えた場合はヘッダ表示
    if ($no_draw && $rest) {
	print "T    W  - L   ave.\n";
    }
    else {
	print "T    W  - L   D   ave.\n";
    }
}

# 結果表示
foreach $ave (@calc_ave) {
    printf("%-3s  %2d - %2d", @$ave{"team"}, @$ave{"win"}, @$ave{"lose"});

    if ($no_draw && $rest) {	# 引き分けを加味しない
	printf("  %.4f\n", @$ave{"ave"});
    }
    else {
	printf("  %2d  %.4f\n", @$ave{"draw"}, @$ave{"ave"});
    }
}
変更点のみを示せればと思ったのですが、半分別物のようになったので別記事に分けました。

【Perl】 ハッシュの配列の要素でソート - The jonkiのサイトを参考にさせて頂きました。「ハッシュ 要素 並べ替え」で調べてみてもなかなかその方法が見つからなかったのですが、ここの方法でPerl初心者でも容易に希望通りの処理を行うことができました。