[回収率113%] 競馬AIの作り方をわかりやすく解説

競馬Python

競馬ファンなら、自作のAIで勝ち馬を予測してみたいと思いますよね。

この記事ではPythonを用いた競馬予想AIの作り方と回収率についてデータとソースコードを載せながら詳しく解説しています。

結論を記載しますと、2020年〜2021年のテストデータに対して

単勝回収率109%
複勝回収率113%

となりました。

早速作り方を解説していくのですが、具体的にやることを先にまとめておきます。

1、競馬データを取得する(前回の記事で解説)

2、競馬データを機械学習用に整形する

3、2005年から2015年のデータを使用して、機械学習する

4、2016年から2019年データを使用して、最適な買い方を探索する

5、2020年から2021年のデータを使用して、回収率を計算する。

それではやって行きましょう。

また、競馬データの解析ではPythonのプログラミングスキルが必須になります。

Pythonの基本が完全には身についていない方は、以下の本で勉強するのがおすすめです。

また、筆者は以下の本を使ってPythonを使った機械学習の理論を学びました。

また、こちらのnoteで有料コードを公開しています。

有料コードをご購入頂くと開発時間を大幅に短縮できますので、ぜひご検討ください。

スポンサーリンク

データの整形

データを整形するためには元になるデータが必要です。

競馬のデータは前回の記事を参考にして取得して下さい。(netkeibaを元にしています。)

データの取得ができたら、機械学習を行うためにデータを整形して行きましょう。

サンプルコードは下記の通りです。めちゃくちゃ長いですね、、、、ですがご自身で設定いただく箇所は少ないのでご安心下さい。

こいつの使い方を解説していきます。

注意:上記コードは前回の記事で生成した競馬データに対してのみ使用可能です。

スポンサーリンク

前回の記事で生成した1年分の競馬データを下図のように1つのフォルダの中に入れます。

またファイル名が「2013.csv」のような形になっていることを確認して下さい。

サンプルコードの4から5行目のyearStartとyearEndに、上図のcsvファイル名の最小値と最大値を入力して下さい。

上図の場合は「yearStart = 2013」、「yearEnd = 2020」になります。

続いてサンプルコード11行目のvar_pathに、上図のcsvファイルが入っているフォルダのパスを記述して下さい。

windowsの場合は、「Shift」を押しながらファイルを右クリックして「パスのコピー」をクリックするとパス名をコピーできます。

macの場合は、「option」を押しながらファイルを右クリックして「◯◯◯◯のパス名をコピー」をクリックするとパス名をコピーできます。

macで説明いたしますと例えば、2013.csvを右クリックすると下図のような画面が出ますが、ここでoptionキーを押すと「コピー」が「”2013.csv”のパス名をコピー」に変わリます。

これでパスをコピーできます。

先程コピーしたパス名をサンプルコード「”/Users/Documents/Python/Data/”」のところにコピペして、2013.csvの部分を消せばOKです。

サンプルコード11行目の「str(for_year) + “csv”」の部分が、forループ1回ごとに2013.csv→2014.csv→2015.csv、、、、といった具合に更新されていくので、2013.csvの部分は消してOKという訳です。

あとは特にご自身で設定してもらう部分はないため、いつも通りRunからRun Module等でコードを走らせればデータをいい感じに整形してくれます。

整形されたデータは「data」の中に格納されています。この「data」の中身を説明します。

スポンサーリンク

整形後のデータの説明

「data」の中にはレース毎の情報が格納されています。

「data」の中身は「最近のレース→昔のレース」の順番で並んでいます。

dataの構造は下図のようになっています。

下図の2つの整数でdataの中身を取り出すことができます。

例えば 「data[0]」と入力すると一番最近(今回の場合、2020年12月末)のレース情報を見ることができます。

続いて「data[0][0]」と入力すると以下のように馬名のリストを取得できます。

続いて「data[0][1]」と入力すると以下のように騎手のリストを取得できます。

続いて「data[0][2]」と入力すると、、、、といった具合で調べていくと、レース情報と整数の対応は以下の通りになっていることがわかります。

スポンサーリンク

前走の情報を追加するコードの説明

競馬の予測を行う上で大切なのが、前走、前々走などの過去の結果です。

そこで今回は前5走までのデータを追加してみます。

サンプルコードは以下の通りです。例えば、data[0]のレースで1着の「キティラ」の前5走を調べるとしましょう。

この場合、data[1]からdata[6000]までのレースの全ての馬名を調べて「キティラ」と一致するものを検索します。

そして、一致したもののレース番号と着順をリストに記録します(サンプルコード16〜17行目)。

前5走を調べるため、もしこのリストの長さが5を超えたら終了します(サンプルコード19〜20行目)。

そしてこの処理はだいぶ重いため、サンプルコード25〜26行目でcsvに保存しています。

筆者は2013年から2020年のデータを使用しているため、全レース数は約27000です。

使い方としましてはまず、2行目「getData」に何レース分を解析するのかを入力して下さい。

「なんで27000レース全部解析しないんや?」って疑問に思う方もいらっしゃるかと思います。

その答えは、例えば2013年1月のデータに対して「前5走までのデータを調べろ」と命令しても、2012年のデータがないので、前5走までのデータ」は調べられないですよね。

言い換えると、最近のデータと昔のデータで解析条件が対等ではなくなってしまいます。これはデータ解析する上で良くないですよね。

そういった理由で、27000レースのうち最近20000レースを解析対象としました。

次に、3行目「pastRaces」で過去レース分遡るかを指定します。

「なんで過去レース全部遡らんのや?」って疑問に思う方もいらっしゃるかと思います。

これに関しては先程と同様に条件を揃えるためです。例えばレース番号0対して過去レース全部遡ると27000レース遡ります。

一方、レース番号20000の場合は7000しか遡りません。

このようにレース番号によって遡るレース数が対等ではなくなるため、過去レース分遡るかを指定しています。

そして、4行目「pastResults」で前何レースを参考にするのか指定して下さい。

最後に25行目のcsvファイルの保存先を指定して下さい。

保存したcsvファイルを読み込むコードは以下の通りです。ご自身で設定するのは2行目のパス名”/Users/Documents/Python/Data/2013_2020_index.csv”の部分だけです。

これを先程生成したcsvファイルのパス名に置き換えて下さい。

読み込んだcsvの情報は「pastIndex」の中に入っています。

試しに「pastIndex[0][0]」を実行してみましょう。

「pastIndex[0][0]」の中には、下図のようにレース番号0で1着だった馬(キティラ)の前5走の情報が入っています。

見方としましては、前走はレース番号74で2着、前前走はレース番号216で9着、前前走はレース番号374で8着といった具合です。

試しに検証してみます。「data[74][0]」を実行してみて下さい。下記のようにレース番号74の馬名が見れます。

確かに2着にキティラが記録されていますね。

以上でデータの整形は終了です。

スポンサーリンク

整形したデータの機械学習

では、いよいよ機械学習をさせて行きましょう。

ここまでで2013年から2020年のデータを整形しました。

以下ではデータ数を増やすために2005年から2021年のデータを使用しています。

(2013年から2020年まででもOKです)

まず最初に、機械学習のアルゴリズムを決める必要があります。

結論としては「lightGBM」一択かと思います。

筆者も「ニューラルネットワーク」や「ランダムフォレスト」など様々なアルゴリズムを試してみたのですが、今回の機械学習はデータの数がかなり多いため、「ニューラルネットワーク」や「ランダムフォレスト」では学習に時間がかかりすぎてしまいました。

一方、「lightGBM」は大きいサイズのデータに対しても素早く学習できるため、今回の環境でも1〜5分程度で学習が完了しました。

それに「lightGBM」はデータを正規化や標準化する必要がないので、データの扱いが少し楽です。

以上の観点から、アルゴリズムは「lightGBM」で行きましょう。

次に説明変数と目的変数を設定する必要があります。

目的変数は機械学習の予測対象のことで、競馬の場合は着順や走破タイム、1着になる確率などになります。

今回は走破タイムを目的変数とします。

説明変数とは、前走の着順や通過、馬体重といった目的変数の予測の元になるパラメータです。

下記のコードでは説明変数に、

「馬番、年齢、性、体重、体重増減、斤量、クラス、出走馬数、距離、芝・ダ、過去5レース着順、過去5レースタイム、過去5レースの上がり、過去5レースの着差、過去5レースの間隔、過去5レースの距離変化、他の馬で平均タイム上位5頭の前走タイム、他の馬で平均タイム上位5頭の前走クラス」

を使用しています。

136行目で機械学習に回す割合を設定できます。

筆者は、2005〜2021年のデータを読み込んで、そのうちの0.6をトレーニングに使用したので、およそ2005〜2015年のデータがトレーニング用のデータになったわけです。

続いて、「lightGBM」のハイパーパラメータを最適化する必要があります。

「ハイパーパラメータとはなんぞや?」とお思いの方も多いかと思います。

ハイパーパラメータとは人間が手動で設定しなければならないパラメータです。

そこでこちらの有料版のコードでは、最適なハイパーパラメータを探索する方法として以下の2種類のコードを記載しています。

最適なハイパーパラメータの探索
①交差検証法とグリッドサーチの合わせ技
②Optunaを使った自動最適化

(交差検証法とグリッドサーチの詳細な説明はこの記事が参考になります。)

本サイトに記載している無料版コードでは、手動でハイパーパラメータを設定します。

サンプルコードは以下の通りです。

11行目から22行目までの部分で、ハイパーパラメータを入力します。

そして以下のコードを実行することによって、最適なハイパーパラメータを使用してlightGBMをトレーニングします。

そして、予測値と実際の値をプロットしてうまくいっているのかを確認しています。

さらに予測精度として決定係数を表示します。

決定係数が1に近づくほど予測の精度が高いと言えます。

ここまでのコードを全て走らせると以下のような結果が得られるかと思います。

横軸が標準化した予測走破タイムで、縦軸が標準化した予測走破タイムです。

きちんと右肩上がりになっていれば予測がうまくいっています。

今回の説明変数とハイパーパラメータですと、決定係数は0.268となりました。

以上の手続きを繰り返して、決定係数が最大になるように説明変数とハイパーパラメータを調整してください。

スポンサーリンク

最適な馬券の買い方の探索

以上で紹介したやり方などで最適なlightGBMのモデルを作ることが競馬AI作りのゴールになります。

何か良さげなモデルができたら、次は最適な馬券の買い方を探索して、実際の回収率を算出して見ましょう。

筆者は、機械学習によって予測された勝率からオッズを算出して、実際のオッズを予測オッズで割って回収率の期待値を算出し、回収率の期待値が100%を超える馬券を買うという方法を用いました。

毎レース100円購入する場合、

1年間に稼げる金額 = 回収率 x 購入点数 x 100

です。

そこで、筆者は上記のプロセスで作成したAlを使用して算出した期待値に対する回収資金のプロットを作って、期待値がいくらなら購入すべきなのかを実際のオッズ毎に単勝と複勝で調べました。

まずは単勝の場合です。

実際のオッズの範囲をグラフ上に表示し、縦軸に資金(=収支)、横軸に期待値をプロットしました。

このグラフを作るために使用したレース数は約28000レースであり、統計的にも十分であると思われます。

全体として期待値が1を超えていると収支がプラス、つまり回収率が100%を超えてきます

縦軸は収支ですので、期待値が高くなるにつれて購入点数が減少し、最終的に収支はゼロに収束します。

グラフが極大を迎える期待値を基準にして馬券を購入すれば良いのですが、実際のオッズが高くなるとこの期待値の基準も高くなることがわかりました。

そこで筆者は、実際のオッズに対して必要な期待値を表に記録して、その表を元にして馬券を購入するプログラムを組みました。

スポンサーリンク

複勝の場合は以下の通りです。

単勝の場合と同様に、実際のオッズが高くなるとこの期待値の基準も高くなっています。

よって単勝の場合と同様に、実際のオッズに対して必要な期待値を表に記録しました。

このオッズと必要な期待値の表を見ながら、馬券を購入してやれば良いというわけです。

スポンサーリンク

最適化した買い方での回収率の算出

先程作った表を元にして、残りの未使用のテストデータ(2020〜2021年分)を使用して、回収率を算出しました。

なおちゃんと購入点数も書いてあるので、収支を計算できるようにしています。

使用したコードは下記です。

結果、最初に述べた通り以下のようになりました。

単勝は1347点購入して回収率109%
複勝は710点購入して回収率113%

長くなりましたが以上がPythonを使用した競馬AIの作り方と回収率です。

有料コード

問い合わせが多くて対応できないため、完全版のコードを有料公開しています。

完全版では上記に加えて、特徴量の可視化、Optunaによるハイパーパラメータの最適化、予測値からオッズの変換も実装しています。

また、2012〜2022年の中央競馬データを使用した時に、単勝回収率118%&複勝回収率108%の具体的なコードを提供しています。

かなりの開発時間の短縮になると思うので、ぜひご検討ください。

ただし本有料記事は、あくまで過去のデータに対する回収率を保証するものであり、未来のデータに対して回収率を保証するものではないので、ご自身の機械学習モデル開発の時短目的での購入をお願いいたします。

<説明>
2012〜2022年の11年分競馬データの中から、約7年分の競馬結果を機械学習モデルのトレーニングに使用し、残り約4年分をテストデータ(10028レース)に使用した際に、単勝回収率が118%(1723点購入)で、複勝回収率が108%(944点購入)になるような機械学習モデルを提供します。具体的には以下の内容を含みます。
 ・機械学習用のcsvデータ(2012〜2022年分)
 ・機械学習用のPythonコード(ipynb形式)
 ・使い方(テキスト形式)

<特徴>
 ・データの整形実装済み
 ・製作者による特徴量最適化済み
 ・特徴量の重要度を可視化済み
 →自身で特徴量を生成&選択も可能
 ・製作者によるパイパーパラメータ最適化済み
 ・グリッドサーチと交差検証による手動最適化実装済み
 →自身でハイパーパラメータの最適化も可能
 ・Optunaによるハイパーパラメータの自動最適化実装済み
 ・決定係数出力による予測精度評価
 ・予測値をオッズに変換するコードも実装
 ・期待値(実際のオッズ/予想オッズ)を元に回収率を計算
 ・予測オッズに対して必要な期待値を可視化

有料記事はこちら

スポンサーリンク

おわりに

以上で競馬AIに関する解説を終了します。

また、競馬AIと自動購入コードを使って実際に馬券購入している様子をツイートしていますので、フォローしていただけると嬉しいです。

Comments

  1. TOKUMEI より:

    こんばんは。データ取得の記事のリンクが切れているのですが、削除されてしまいましたでしょうか。

  2. ナナシー より:

    はじめまして、個人で競馬AIを開発している者です。非常に有益な情報を公開してくださったことに感謝致します。そこで一つ質問があるのですが、標準化された予測走破タイムをどのようにして勝率に変換しているのでしょうか。ご教授いただければ幸いです。

    • 管理人 より:

      コメントありがとうございます。
      非常に鋭い質問です、、、、
      機械学習の予測値からオッズへの変換方法については、私のAIの根幹部分になりますので詳細はお答えできないのですが、せっかくコメント頂いたので概略だけでもお答えいたします。
      まず、走破タイムの確率密度関数(典型的には正規分布)を算出し、次に数学的な計算で1着になる確率(=オッズ)を計算しています。
      ヒントが少なくて申し訳ないのですが、ご参考下さい。

      • ナナシー より:

        ご返信頂きありがとうございます。ヒントは大変参考になりました。感謝感激です。

  3. TAKUWO より:

    初歩的な質問で申し訳ないのですが、データ整形が正しくされないのですが原因分かりますでしょうか。
    エラーは出ずに実行できるのですが、成型後data[0][0]を読み込むと0:馬名~12:人気までまとめて出力されます。

    • 管理人 より:

      data[“この数字が何に対応しているか”][“この数字が何に対応しているか”]を調べると良いと思います。
      こ例えば、data[“西暦”][“レース番号”]と対応している可能性とかですかね、、、、

  4. おじさん より:

    ご教示ください。環境は、windowsです。
    見よう見まねで、打ち込んでおりますが。
    以下のところで、エラーが出て止まっております。

    #最適なハイパーパラメータでトレーニングとテストを行う
    bestEst.fit(xTrainDf,np.ravel(yTrainDf))

    Backend TkAgg is interactive backend. Turning interactive mode on.
    Traceback (most recent call last):
    File “C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.1264.0_x64__qbz5n2kfra8p0\lib\runpy.py”, line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
    File “C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.1264.0_x64__qbz5n2kfra8p0\lib\runpy.py”, line 86, in _run_code
    exec(code, run_globals)

    上記含め数行。どの様に解決すればよろしいのでしょうか?

    • 管理人 より:

      このエラーは私は見たことがないですね、、、
      こういう場合はググってデバグする必要があります。
      とりあえず、筆者がググって考えた案をいかに記載します。
      下記のサイトによると、「plt.use(‘Agg’)」と追加するとうまくいくかもしれません。
      https://jpdebug.com/p/1696971
      また筆者のコードはVSCODEとJupyterで機能することは確認できているので、これらをインストールしてみるのも良いかもしれません。

      • おじいさん より:

        ご検討有り難うございました。環境はWindows11+VSCODEとなります。
        plt.use(‘Agg’)は効果は無いようでした。当てる場所の問題かもしれませんが。
        そこで、対象レース数を極端に減らし(10Race)、処理を進めると別のエラーとなりました。
        dataGet = 10#何レース分のデータを取得するか、pastRaces = 1000#過去何レース調べるか

        raise ValueError(“DataFrame.dtypes for data must be int, float or bool.\n”
        ValueError: DataFrame.dtypes for data must be int, float or bool.
        Did not expect the data types in the following fields: 7, 14, 22, 30, 38, 46

        xTrainDf,np.ravel(yTrainDf)の値の形を変えろ的なものだと思うのですが、
        OSによる起因ならWindows10に戻せば治るものなのでしょうか?

        全く的外れな考えとは思いますが、ご意見ください。
        それぞれの引数には、生成したパラメータは入っておりました。

        • 管理人 より:

          このエラーはよく見るやつですね。
          ひとまずxTrainDfとyTrainDfの中身を表示して確認してみてください。
          整数または小数以外の要素が含まれているはずです。
          筆者の予想では「nan」が含まれているのかと、、、、
          原因がわかったら、この”不純物”を取り除くコードを追記してください。

        • 管理人 より:

          また、表示では整数に見えてもtype()で確認すると文字列型になっている可能性もあります。
          lightGBMは文字列型は学習できないので、上記エラーになると思います。

          • おじいさん より:

            ご検討有り難うございました。非常に勉強になりました。

            結論としては、騎手名が入っており、これの取り込みを外しました。
            グラフ表示の部分では、説明変数と目的変数(走破タイム)の相関のため大きな値でしたが斜め上に伸びており一応成功したと思っています。

            精度を上げるには、説明変数(配列要因の決められたパラメータ)と目的変数(実際?の結果)により機械学習が行われるため、説明変数の多さや正確さ、騎手名=能力(期待値)などが重要ということでしょうか?

          • 管理人 より:

            おめでとうございます!
            精度を上げる2大要素は、「何を説明変数にするのか」と「ハイパーパラメータの最適化」だと考えています。
            色々試して、決定係数が最大になるものを見つけると良いかと思います。

  5. L より:

    #最適なハイパーパラメータでトレーニングとテストを行う
    bestEst.fit(xTrainDf,np.ravel(yTrainDf))
    predict = bestEst.predict(xTestDf)
    print(bestEst.score(xTestDf,yTestDf))

    のところで
    ValueError: Input data must be 2 dimensional and non empty.
    と出てしまいます。対処法などありますでしょうか?

    • 管理人 より:

      一般的な知識として、デバグの作業をする際は、変数の中身を表示しまくると良いです。
      まずどの行でエラーが発生しているのかを確認するために、1行ずつ実行してみてください。
      今回のケースはおそらくxTrainDfとyTrainDfがの中身がバグっていそうなので、これらの中身を表示して確認してみてください。
      筆者の予想では、どちらかが空になっているのかと思います。

  6. L より:

    初歩的なアドバイスからありがとうございます。
    おっしゃられた通りに1行目、
    bestEst.fit(xTrainDf,np.ravel(yTrainDf))
    を実行したところ
    ValueError: DataFrame.dtypes for data must be int, float or bool.
    Did not expect the data types in the following fields: 7, 14, 22, 30, 38, 46
    とのエラーがでました。
    xTrainDfとyTrainDfは中身はちゃんとはいっているようです。

    • 管理人 より:

      前に質問いただいた方も同じエラーが出ていました。
      その時は、騎手の名前が説明変数(xTrainDf)の中に入っていたためでした。
      lightGBMでは文字列型を学習できないので、騎手の名前を削除するかなんらかの数値に変換する処理が必要になります。

      先程掲載しているコードを修正(騎手の名前を削除)したので、コピペで使えるかもしれません。

  7. わちき より:

    有益な情報公開ありがとうございます!
    PythonとLightGBMの勉強に使用させて頂いております!

    >ValueError: DataFrame.dtypes for data must be int, float or bool.
    Did not expect the data types in the following fields: 7, 14, 22, 30, 38, 46
    こちらのエラーは騎手名(文字列)がそのまま配列に組み込まれている為に起こるようです。
    本文にある様に平均着順に手動で変更する必要があるみたいです。

  8. なるせ より:

    とても参考になります。ありがとうございます。本題への影響はないのですが、Python の書き方で少し気になることがありましたのでフィードバックさせてください。スルーしていただいてももちろん問題ないです!

    for 文の書き方で、python は一般的に iterator をとっているのでいわゆる配列の添え字をそんなに意識することなく中身に集中できます。具体的には、

    data = [array1, array2, … array100]

    という data の中身に対して順次処理をしたい場合、参考にさせていただいたコードは全体的に添え字を意識し、配列長を取ってその range を取って以下のように書かれていました。

    for for_arrays in range(len(data)):
    item = data[for_arrays]
    # 以下略
    pass

    これは↓のようにシンプルに書けます。全く同じ動作です。

    for item in data:
    pass

    もし仮に添え字(= loop の回数)を使いたいケースは

    for i, item in enumerate(data):
    pass

    とすれば、OK です。

    実際のコードだと例えば↓こういうところです。

    for for_horses in range(len(data[for_races][0])):#馬の数、0はnameListを示す
    var_name = data[for_races][0][for_horses]
    # 以下略
    pass

    は、以下のようにかけます。var_name に代入している冗長なところがすっきりするかと思います。

    for var_name in data[for_races][0]:#馬の数、0はnameListを示す
    # 以下略
    pass

    以上、ご参考まで。(もしうるさく感じてしまったらごめんなさい)

    • 管理人 より:

      コメントありがとうございます!!助かります!!
      前は添字を省略した「for n in data」の形も使い分けしていたのですが、forの中身を書いている途中で「やっぱり添字使必要じゃん!!」ってなることが多かったので、結局思考停止で「for n in range(data)」と記述するスタイルに至りました笑
      確かに確実に添字を使わない場面では使え分した方がいいのかもしれないですね、、、、
      ありがとうございます!

      • なるせ より:

        その気持ちめっちゃわかります!w僕も enumerate に出会ってからようやく呪縛から逃れられました。多分実行時間も変わると思います。(前処理部分は 1 回しか実行しないので実行速度もそんなに気にならないとは思いますが)

        余談ですが、データは、netkeiba のスクレイピングのほうではなく、JRA-VAN の csv ダウンロードのほうを使っています。フォーマットが微妙に、というか結構違っていて苦労していますが、最終的に同じ data[n][m] や pastIndex[n][m] を生成すればいいのだと思いいたり、そこまでは自作しました。

        オッズに転換するところは自分で独自にやろうと思いますが、とりあえず走破タイム上位2馬をワイドのオッズが x 以上の時に買う、みたいなシンプルな方法でやってみようと思っています。ありがとうございます。

  9. じゃっくん より:

    有益な情報公開いただきありがとうございます。
    ご教示いただきたいのですが、どうしても以下のエラーが出てしまいます。

    —> 34 for n in range(lpredictOddsTestListTest):
    NameError: name ‘predictOddsTestListTest’ is not defined

    ご回答頂けますと幸いです。

    • じゃっくん より:

      エラーの内容に誤りがありました申し訳ございません。
      正しくは以下になります。

      —> 34 for n in range(len(predictOddsTestListTest)):
      NameError: name ‘predictOddsTestListTest’ is not defined

    • 管理人 より:

      そこのコードはコピペだと使えないので、参考程度にお願いします。
      predictOddsTestListTestに機械学習で予測したオッズを入れて、actualOddListTestTestに実際のオッズを入力して下さい。

  10. たすけ より:

    地方競馬に対応させるにはどこを調整すればいいでしょうか
    競馬場とクラスを一旦変えようとしていますが

    中央と違うデータのようで手こずっています。
    ご教授いただけますと幸いです。

    • 管理人 より:

      おっしゃる通り競馬場とクラスを変えればいけると思います。
      スクレイピングしたデータはほとんど同じなので、エラーは整形の問題ではないかと予想されます。
      一般論として、「print」などで中身を表示しまくるとバグの原因が見つけやすいので、ご参考までに。

  11. キセキ より:

    質問があります。
    同じようにプログラムを組んでみたのですが、決定係数が0.8563となりました。0.268にほど遠い値となりました。
    また、
    plt.xlim(-3,3)
    plt.ylim(-3,3)
    ではプロットされず、
    plt.xlim(0,300)
    plt.ylim(0,300)
    で右肩上がりに細々とプロットされるようになりました。
    原因はわかりますか?

    • 管理人 より:

      目的変数が間違っている可能性が高いですね、、、、
      ひとまず、目的変数が何になっているのかを確認することをお勧めします。
      個人的には標準化していないタイムがプロットされているのではないかと予想します。
      標準化していないタイムは距離と相関が強いので、決定係数が0.8くらいになるかなあと思いました。

      • マックス より:

        初歩的な質問になってしまいますが、タイムの標準化はどのように行えばよいでしょうか?
        (コピペさせていただいたのですが、自分も前の質問者様と同様の結果になってしまいます。)

        • 管理人 より:

          一般論として、デバクの際は「中身を出力する」が基本になります。
          標準化前のタイムのリストの中身を確認することをおすすめします。

          • ノベル より:

            私も前の質問者様達と同様の結果になってしまい、困っています。
            タイムの標準化はコードの何行目で行っているか教えていただきたいです。
            また、タイムの標準化のやり方等も教えていただけるとありがたいです。
            また、predictOddsTestListModelとactualOddListTestModelの部分でエラーが出てしまうのですが、他の回答者様のコメントでそれぞれ機械学習で予想したオッズと、実際のオッズを入力とありましたがそちらも具体的な数値を教えていただけるとありがたいです。
            無知でご迷惑をお掛けし大変申し訳ないのですがよろしくお願いいたします。

          • 管理人 より:

            タイムの標準化コードを追加しました。

  12. てる より:

    コメント失礼いたします。

    成型後のデータで人気順のデータのみ上手く表示されません。
    date[0][12]と入力したところ理想的には[6, 2, 1, 7, 3, 5, 8, 9, 4]と表示されてほしいのですが、[2, 7, 4, 1, 3, 8, 8, 5, 5]と表示されてしまいます。
    (小倉競馬場のデータのみをスクレイピングしたので,data[0][]によって2022年9月4日 4回小倉8日目 第一R 障害3歳以上未勝利の結果が表示されました。)
    馬番などの他のデータは正確に表示されています。
    原因としては何が考えられるでしょうか。

    回答いただけると幸いです。
    よろしくお願いいたします。

  13. ゆり より:

    コメント失礼いたします。

    makeXParamList(n)の
    if len(pastIndex[n][nn]) <= 0:#過去データが2以下の馬が1頭でもいるか
    break

    if len(pastIndex[n][nn]) <= 2:#過去データが2以下の馬が1頭でもいるか
    break
    の間違いでしょうか。
    「過去データが2以下の馬が1頭でもいるか」とコメントが入っているので、
    pastIndex[n][nn]の要素数が2以下かどうかで判定する必要があるように思います。

    お手数をおかけしますが、よろしくお願いします。

  14. こんちゃん より:

    pythonの勉強をなかなか始められなかったのですが、趣味の競馬が絡んでいるこの記事のおかげで重い腰が上がりました。大感謝です。

    ささいな点に気づいたのですが、以下の計算では、int(var2[0])から1を引く必要があると思います。

    dateList.append((int(var1[0].replace(“,”,””))-yearStart)*365+int(var2[0])*30+int(var2[1].split(“日”)[0]))

    1点お伺いしたいのですが、馬券の買い方の箇所のグラフは、管理者様による、特徴量の選択やハイパーパラメータのチューニングを終えたあとのモデルを使用した結果という理解で正しいでしょうか?