続・競馬であそぶ

誤解、曲解、ウソ、冗談は人生に欠かせない気分直しの万能薬。




9. テキストデータの集計・検索

kbsTk や sskj で出馬表や成績ファイルを出力しているうちに、データがたまってくる。
これらのデータを、自分の求める条件で集計・検索する方法を知らなければ、 ただのゴミ
でしかない。

これらは、整形されたテキストファイルで固定長なので位置を決めてから順番に処理する。
固定長ではない部分もそれなりに処理すればOKで、殆ど問題にはならないはず。
一方、カンマなどで区切られた csv ファイルでは位置は関係なく順番だけで処理できる。

固定長の場合は、そのままでも自然に見ることが出来る(まぁ、JV ファイルは見づらい?)
が、csv では EXCEL など他のツールが必要になることもある。
固定長のまま、schema.ini をつかう方法もあるが、アッサリ、csv にコンバート してやれば
いいだけの話でここで問題が生じるとは思えない。
好きなほうを使えばいいだけ。但し、ここでは固定長中心。

** sskj.pl は削除したままなので近いうち再UP予定(内容の変更はなし)。
-----------------------------------------------------------------
ここでは、Perl で kbsTk データの処理について少し、、
New に対応させていますが、旧 kbsTk のでも少しの書き換えで大丈夫。

集計・検索の処理の流れとしては

1. 対象ファイルの指定。( どのファイルか分からなければ処理のしようがなひ )
2. ファイル内の単位、例えば1行一単位とか、その行の中のドコにナニがあるのか知る。
3. 出力したいのは、ナンなのか自問。( 要するに、自分の知りたいことよ )
4. スクリプトを書く。( 最初はヒトの作ったものを参考にして改良するのが正しい )
5. 結果を出力して思索。( 利益の出そうなデータなら独占し、そうでなければ公開? )
6. 儲かったら自慢し、見損じだったら次の集計・検索に向かう。

-----------------------------------------------------------------
基本的には他のスクリプトでも、なんども繰り返し使っている方法ですが動くものなら
何度でも使う。
上記の 3. 以降は、人それぞれなので 1. 2. について説明します。

対象ファイルがタクサンあるフォルダで、下のスクリプトを(スクリプト名は自由に)実行
すると、同じ場所に syuk_DD.dat(ダート) と syuk_TT.dat(芝)に分類されたファイルが
出来ます。
ただそれだけのことです。
もっと細かい分類が必要なら、書き換えるべし。

#!/usr/bin/perl
#===========================================================================#
# 対象ファイル(例)
# ファイル名のアタマ2桁が 20 から始まる3桁以上の数字( ^20\d+ )など 
# これで2005xxxxout.dat などが全部引っ掛かる。同じフォルダには、紛らわしいのは
# 置かないこと。あるいは、もっと厳密に縛ることも可能。
use strict;
my $flg = 0 ;
my $outD = 'syuk_DD.dat' ;       open OUD, ">$outD" ;       # 出力用ファイルを開く
my $outT = 'syuk_TT.dat' ;       open OUT, ">$outT" ;       # 出力用ファイルを開く
print STDERR "  ファイルリスト作成中 \n";
my @t_files = grep { -T } glob "*" ;    # 100個あれば100個

for ( @t_files ) {
    my ( $list ) = (split '/', $_)[-1];
    unless ( $list =~ /^20\d+/ ) {      # ファイル名を抽出、要らないのは外す
        print STDERR "  対象ファイルではないので Skip!  $list \n" ;
        next;
    }
    open INF, $list ;
    while ( <INF> ) {
        if( /^.{6,7}R/ ) {
            $flg = /0m ダ/ ? 1 : 2 ;         # タイトル行から芝・ダートを分類
            next ;
        }
        next unless /^\s+\d\|/ ;                    # 行頭が数値と | ならOK
                                                    # 旧Ver. なら /^\d\(/  ★
        next unless substr( $_, 62, 2 ) > 0 ;       # 人気順位が入っている位置
            # ↑ 違っていたら修正。行頭は 0 から数え 、62文字目から2文字の意
        print OUD if $flg == 1;             # ダート
        print OUT if $flg == 2;             # 芝のとき
    }
    close INF ;
}
close OUT ;     close OUD ;

これで、第一段階はクリアとします。
-----------------------------------------------------------------
次は少し長いです。長いだけで仕掛けは簡単ですが、、
スクリプト名を適当に付けて同じフォルダで実行すると、out_DD.dat 又は out_TT.dat
が出現します。

芝orダートを指定します。ダートなら 1、芝なら 2 です。
抽出条件は、自分で指定します。いくつか例があるので、試してください。
考えればワカルッチャ。簡単すぎて、小学生でも出来ます。
全部 # でコメントアウトした場合は、全データが出ます。

出力ファイルの最後尾に集計データが表示されます。
出力フォーマットは、いつもここで使っているもので詳細はスクリプトを 参照してください。
改変は勝手にやってください。お好きなように、、
下記の例と、変数などの位置が異なる場合はそれぞれ調整してください。未確認です。

#!/usr/bin/perl
#===========================================================================#
# 全面修正
use strict ;
######### ダートなら 1 ##### 芝なら 2  ###############
my $fflg = 1 ;          # ★
my $inF ;
my $outF ;
if ( $fflg == 1 )    {  $inF = 'syuk_DD.dat' ;   $outF = 'out_DD.dat' ; }
elsif ( $fflg == 2 ) {  $inF = 'syuk_TT.dat' ;   $outF = 'out_TT.dat' ; }
else                 {              exit() ; }
open INF, $inF ;
open OUT, ">$outF" ;

my ( $a1, $a2, $a3, $a0, $maxb, $maxd ) = () ;
my ( $c1, $c2, $c3, $c0, $b1, $d1 ) = () ;
my $fuka = 0 ;
my $fukb = 0 ;
my @chk_a ;
my @chk_k ;
my @chk_n ;
my $head = $fflg == 1 ? 'ダ' : '芝' ;

while ( <INF> ) {
    next unless( /^\s+\d[\|\(]/ ) ;
# 6|11|ゴールデンロ  4牡8| 4赤木高太57o -1    & C・西  0z215.7(16)  -94-  +-- -( 77) -[16]
# 6|12aアドマイヤマ 43牡6|13武豊  57o 241- ・&*4・西-16v 10.7( 3)c -4c:  VBc q( 86)3 [ 3] 250
# 7|13aサイレントウ8 6セ6| 7コーツィ57o      ・&*4・外 +8:  2.0! 1)b +53:  -A8 *(   )1-[ 1] 120
# $u_ban               $ksp               $kwr$mae      $toz $tj                   $chaku$fukh
#                $posi                      $ysm             $pa$pi$pu$pe     $idx
#                                            $yuw
#                                                 $tjyu                 $mkz$mkall$mkzen
#
    my %mrk_a = ( 'a' => 10, 'b' => 11, 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15,
                  'g' => 16, 'h' => 17, 'i' => 18, ' ' => 20 , '-' => 20 ) ;
    my ( $u_ban, $kaku, $dash, $sasi, $posi, $sinba, $ksp, $kwr, $mae, $ysm, $yuw,
         $tjyu, $bnmk, $toz, $tj, $pa, $pi, $pu, $pe, $po, $mkz, $mkall,
         $mkzen, $idx, $chaku, $fukh  ) =
        unpack '@4 A2 @6 A1 @19 a1 @20 a1 @21 a1 @25 A1 @26 A2 @44 a2 @46 a1 @47 a2 @49 A1
            @52 A3 @55 A1 @56 A5 @62 A2 @67 a1 @68 a1 @69 a1 @70 a1 @71 a1 @73 a1 @74 a2
            @76 a2 @79 A3 @86 A2 @89 a4', $_ ;

    my $pinum = $pi =~ /[1-9]/ ? $pi : $mrk_a{$pi} ;
    my $punum = $pu =~ /[1-9]/ ? $pu : $mrk_a{$pu} ;
    my $omk = substr( $_, 63, 1 ) ;
    
############★ 抽出 条件 は 以下で指定 ★###################
#   next unless $pe eq '-' ;
#   next unless $dash >= 7 ;
#   next unless $tj > 2 ;
#   next unless $mkzen =~ /q$/ ;
#   next unless /武豊/ ;
###  
###------------------------------------------------------------
        ##### カウント ##### 異常のところでは中止のみ加算
    if( $kwr =~ /替|変|代|換/ ) {           # 乗り代わり
        if( $chaku == 1 ) {                 # 一着のとき
            $a1++ ;         $b1 += $toz ;       $maxb = $toz if $maxb < $toz ;
            push @chk_n, $toz ;         push @chk_a, $toz ;
            $fuka += $fukh ;
        }
        if( $chaku == 2 ) { $a2++ ;     $fuka += $fukh ; }
        if( $chaku == 3 ) { $a3++ ;     $fuka += $fukh if $fukh ; }
        if( $chaku > 3 ) { $a0++ ; }            # 着外
        if( $chaku == 0 ) { $a0++ if /中止$/ ; }        # 異常
    } else {
        if( $chaku == 1 ) {
            $c1++ ;         $d1 += $toz ;       $maxd = $toz if $maxd < $toz ;
            push @chk_k, $toz ; push @chk_a, $toz ;
            $fukb += $fukh ;
        }
        if( $chaku == 2 ) { $c2++ ;     $fukb += $fukh ; }
        if( $chaku == 3 ) { $c3++ ;     $fukb += $fukh if $fukh ; }
        if( $chaku > 3 ) { $c0++ ; }        # 着外
        if( $chaku == 0 ) { $c0++ if /中止$/ ; }        # 異常
    }
    print OUT ;                 # 1行づつ出力。必要なければコメントアウト
}
my $k_a = $a1 + $a2 + $a3 + $a0 ;   # レース数・乗替
my $l_a = $c1 + $c2 + $c3 + $c0 ;   # レース数・継続
my $e1 = $c1 + $a1 ;                # 1着数合計
my $e2 = $c2 + $a2 ;                # 2着数合計
my $e3 = $c3 + $a3 ;                # 3着数合計
my $m_a = $l_a + $k_a ;             # 対象の全レース数
my $f1 = $d1 + $b1 ;                # 単オッズ合計
my $k_r = $a1 + $a2 ;               # 連対数合計
my $l_r = $c1 + $c2 ;
my $k_f = $k_r + $a3 ;              # 3着入着数合計
my $l_f = $l_r + $c3 ;
my $k_1 = 100 * $a1 / $k_a if $k_a > 0 ;        # 単率
my $l_1 = 100 * $c1 / $l_a if $l_a > 0 ;        # 単率
my $m_1 = 100 * $e1 / $m_a  if $m_a > 0 ;
my $k_2 = 100 * $k_r / $k_a if $k_a > 0 ;       # 連率
my $l_2 = 100 * $l_r / $l_a if $l_a > 0 ;       # 連率
my $m_2 = 100 * ($k_r + $l_r) / $m_a if $m_a > 0 ;
my $k_3 = 100 * $k_f / $k_a if $k_a > 0 ;       # 複率
my $l_3 = 100 * $l_f / $l_a if $l_a > 0 ;       # 複率
my $m_3 = 100 * ($k_f + $l_f) / $m_a if $m_a > 0 ;

my $b2_ = $b1 / $a1  if $a1 > 0 ;              # 単配平均
my $d2_ = $d1 / $c1  if $c1 > 0 ;
my $f2_ = $f1 / $e1  if $e1 > 0 ;
my $f2 = &chuo_t( @chk_a ) ;                    # 単配中央値
my $d2 = &chuo_t( @chk_k ) ;
my $b2 = &chuo_t( @chk_n ) ;

my $f3d = 100 * $f1 / $m_a if $m_a > 0 ;        # 単回収
my $d3d = 100 * $d1 / $l_a if $l_a > 0 ;        # 単回収
my $b3d = 100 * $b1 / $k_a if $k_a > 0 ;        # 単回収

my $fukc = $fuka + $fukb ;
my $fk1 = $fukc / $m_a if $m_a > 0 ;
my $fk2 = $fukb / $l_a if $l_a > 0 ;
my $fk3 = $fuka / $k_a if $k_a > 0 ;
my $mkA = &admk( $f3d, $fk1 ) ;
my $mkB = &admk( $d3d, $fk2 ) ;
my $mkC = &admk( $b3d, $fk3 ) ;

if( $m_a and $l_a and $k_a ) {
    printf OUT 
    "%s@%4dA%4dB%4d全%5d:単%5.1f 連%5.1f 複%5.1f|単%5.1f 複%5.1f%s%7.1f\(%4.1f\@%5.1f\)\n", 
        $head, $e1, $e2, $e3, $m_a, $m_1, $m_2, $m_3, $f3d, $fk1, $mkA, $f1, $f2, $f2_ ;
}
if( $l_a ) {
    printf OUT 
    "続@%4dA%4dB%4d全%5d:単%5.1f 連%5.1f 複%5.1f|単%5.1f 複%5.1f%s%7.1f\(%4.1f\@%5.1f\)%5.1f\n", 
        $c1, $c2, $c3, $l_a, $l_1, $l_2, $l_3, $d3d, $fk2, $mkB, $d1, $d2, $d2_, $maxd ;
}
if( $k_a ) {
    printf OUT 
    "※@%4dA%4dB%4d全%5d:単%5.1f 連%5.1f 複%5.1f|単%5.1f 複%5.1f%s%7.1f\(%4.1f\@%5.1f\)%5.1f\n", 
        $a1, $a2, $a3, $k_a, $k_1, $k_2, $k_3, $b3d, $fk3, $mkC, $b1, $b2, $b2_, $maxb ;
}
close OUT ;
close INF ;

sub chuo_t {        # 中央値を抽出
    my @sorted = sort { $b <=> $a; } @_ ;
    my $aa = @sorted / 2 ;
    my $ans = $sorted[$aa] ;
    return $ans ;
}

sub admk {
    my ( $tt, $ff ) = @_ ;
    my $ans = ':' ;
    if ( $ff > 120 ) {
        if ( $tt > 100 ) {
            $ans = '$' ;
        } else {
            $ans = 'F' ;
        }
    } elsif ( $ff > 100 ) {
        if ( $tt > 120 ) {
            $ans = '$' ;
        } elsif ( $tt > 100 ) {
            $ans = 'S' ;
        } else {
            $ans = 'f' ;
        }
    } elsif ( $ff > 85 ) {
        if ( $tt > 100 ) {
            $ans = 's' ;
        } elsif ( $tt + $ff > 180 ) {
            $ans = 'n' ;
        }
    } elsif ( $ff < 50 ) {
        $ans = 'X' ;
    } elsif ( $ff < 65 ) {
        $ans = 'x' ;
    } elsif ( $tt + $ff > 180 ) {
        $ans = 'n' ;
    }
    return $ans ;
}

これを書き換えることで、成績ファイルにも応用できる筈なので、挑戦してみてください。
使いこなせれば、Perl が好きになるかも、



10. 競馬データ処理のすすめ

この項は、スクリプトとは関係ないので読み飛ばしても支障なし

馬券で勝つためだけなら、こんなに面倒なデータ処理が必要とは思えない。
重要なポイントは数えるほどしかないし、情熱と決意と自制心と理解力さえあれば簡単な
ことだと思う。

馬券を買わなければ負けることは有り得ないので誰でもマイナスからスタートということは
無い。勝つと見極めた時点でスタートをすればいいだけの話。
これからの若者達の人生のように、バカな先人達が作った莫大な借財を背負いながらの
マイナススタートではないということだ。

控除率が下がっても「イマ損をしてるヤツ」が勝ち組に変るなんてことは、考えようもない
し、上がれば「競馬で儲けているような人間」が手を引いて、競馬界は衰退するかもしれな
いけど、「イマ損をしてるヤツ」が少し楽になる可能性もある。
税制の運用で状況が変ることだってある。残念ながら、そのときになってみないと分からな
い。

言えるのは、競馬で損するのが当たり前と思うのなら、ソレに見合った楽しさを競馬で味わ
うべきだし、ホドホド以上には馬券を買うべきではない
楽しみ方は、馬社会への傾倒でもいいし、好きな馬の追っかけでもいい。

競馬データを弄ってあそぶという楽しみ方を、ここでは進めているのだけれどこれ なんかは、
誰も知らない競馬の真理が見えたりという本来の目的の他に、いつのまにかプログラムが
書けるようになった
とか、コンピュータに詳しくなったという実社会でも役 に立つ(かもしれな
い)スキルが身につくというご利益がある。

------------------------------------------------------------------------
自分の場合は、もともとACCESS やEXCELL のデータを仕事でも使っていて、それらをもっ
と簡単に素早く好きなように処理するという目的でPerl も使っていたんです。
まぁ、完全に使いこなすというレベルでは当然ありません。
占いのプログラムをPerl で作ったりもしたけれど、後が続かず自然消滅〜挫折。

そんなとき、競馬データ処理のスクリプトを見つけて、それで再挑戦というステップです。
動かないスクリプトを動くように修正して&修正して、が第一関門でした。
だから、Perl へのこだわりのようなものはないけど、やってチョット得をしたかなと、そんな
気分です。

Microsoft の陰謀?Office は皆同じ Ver.にしなければならぬという、 愚かな脅迫観念から
脱け出して、潔く TEXT にこだわるっていうのも一つの主張。

というわけで、続の最初のシリーズはこれで完結ということにします。
次は何をテーマにするか or 今回のように思いつくままにいくか、思案中です。。

では〜


INDEXへ トップページへ