エラーも警告も出さずに、レースの6.17%が消えていた
このツールは公開データと機械学習で競艇の舟券収支を検証している。結論はすでに出ていて、回収率は100%に届かない。今回の記事はその話ではない。もっと地味で、もっと基本的な話をする。
2026年8月31日まで、このDBは全期間のレースのうち6.17%を、こっそり欠いたまま学習・集計していた。 エラーは出ない。警告も出ない。ただ、そのレースが「出走表の無いレース」として静かに存在しなくなっていた。
何が起きていたか
原因は出走表パーサだった。公式の番組表(B)ファイルは固定幅のテキストで、モーターの2連率(小数)の次にボート番号が続く。通常は間に空白がある。
37.25 159
ところがボート番号が3桁になったとき、この空白が消えることがある。
37.25159
パーサ(src/collector/parse_b.py)は率と番号の境界を\s+(1文字以上の空白)で区切る前提で作っていた。空白が消えた行はこの正規表現にマッチせず、その行だけ、もっと言えばそのレース1件がまるごと取り込みから漏れた。
なぜ気づかれなかったか
ここが今回の一番の要点である。エラーも警告も出ない。
レース自体の情報(raceテーブル)は別ファイル・別のパース経路で先に作られるので、そちらは普通に存在する。欠けるのは出走表(race_entry)だけ。つまり「レースはあるのに、出走する6艇の情報が無い」という状態になる。
処理は止まらない。例外も飛ばない。次の日のバッチも、その次の日のバッチも、何ごともなく走り続ける。「静かに消える」というのは比喩ではなく、実装として本当に何のログも残さず消えていたということだ。
どこに、どれだけ影響したか(2026-08-31修正前の実測)
修正前の実測値は次のとおり。出典は docs/03_findings.md(検証の正典)。
| 指標 | 修正前 | 修正後 |
|---|---|---|
race_entry 行数 |
844,467 | 899,724 |
| 検証レース数(検証期間) | 27,171 | 29,791 |
| 1号艇の的中率 | 54.28% | 54.77% |
| 3連単 上位1点 回収率 | 76.64% | 76.99% |

出典: docs/03_findings.md §5-6(2026-08-31修正前後の実測)
影響は3つに分けられる。
- 予想からその場が丸ごと抜ける。 出走表が無いレースは、その日のその会場の予想が出せない。
- モデルの学習データが欠ける。 6.17%分のレースを見ないまま学習していた。
- 実績の母集団が偏る。 「1号艇の1着率」のような集計値が、欠けたレースの分だけズレた数字になっていた。
とくに2026年8月は31日中23日で、どこかの会場が欠落していた月だった。ほぼ毎日、どこかのレースが静かに消えていたことになる。
数字への影響は、思ったより小さかった
ここは正直に書く。回収率への影響は+0.35ptだけだった。76.64%が76.99%になっただけで、赤字が黒字に変わったわけではない。

出典: docs/03_findings.md §5-6(2026-08-31修正前後の実測) / いずれも100%に届かず赤字
このバグの深刻さは「儲かる手法が隠れていた」ことではない。「母集団が偏っていた」ことにある。6.17%という数字は、次の疑問を消せなかったという意味で重い。「もし欠けていたレースに何か違う傾向があったら、今まで見ていた数字は歪んでいたのではないか」。今回は実測してみたら影響は小さかった、というだけで、確認せずに済ませられる話ではなかった。
今のDBで確認する(2026-09-11集計・終端2026-09-10)
ここからは、修正済みのバグが今も直ったままかを、このDB(2024-01-01〜2026-09-10、151,524レース)で今回あらためて数え直した結果である。過去記事の数字を使い回さず、SQLを今実行して確認した。

出典: 自前DB 151,524レース(2024-01-01〜2026-09-10・2026-09-10集計)
確認したのは「raceテーブルにレースがあるのにrace_entryに出走表が無い」件数を、今のDBで数えるだけの単純なチェックだ。結果は次のとおり。
- 全期間(151,524レース)で、欠けているレースは 0件
- 修正日を含む直近11日間(2026-08-31〜2026-09-10・1,632レース)でも 0件
修正から10日以上・1,632レース分のデータが新しく積み上がっても、同じバグは再発していない。一度直したパッチが、その後の日次取り込みでも欠損ゼロを保っていることを、日付を進めた上で確認できたことになる。
参考までに、今のDBでの1号艇の1着率も載せておく(母集団の取り方で1.2ptほど動くのはdocs/03_findings.mdの指摘どおりで、この記事の主題である欠損とは別の話である)。
- 1号艇1着率(6艇完走のみ・139,338レース): 55.31%
- 1号艇1着率(失格・欠場も母数に含む・149,614レース): 54.59%
- 出走表がある行の母集団(149,614件)は、結果がある行の母集団(149,614件)と完全に一致した
最後の1行が今回の要点である。修正前は「結果はあるのに出走表が無い」レースが6.17%分あり、この2つの母集団が食い違っていた。今は一致している。定義の違いによる数字のブレが、データの欠損由来ではなくなったということだ。
検出方法は単純だった
この種の欠損を見つける方法自体は難しくない。docs/03_findings.mdにある通り、
検出は「
raceに在るのにrace_entryに無いレース」を数えるだけでできる。
SQLにすると次の1文で足りる。
SELECT COUNT(*) FROM race r
WHERE NOT EXISTS (SELECT 1 FROM race_entry e WHERE e.race_id = r.race_id);
難しかったのは「気づくこと」ではなく「気づく仕組みを持っていなかったこと」だった。 このチェックは日次の取り込みジョブの最後に一言足しておけば、次に同種の欠損が起きても即座に検出できる。今回の教訓はここに尽きる。固定幅パーサの区切り文字を厳格にしすぎない、が根本対策としてはもちろん正しい。しかしそれ以上に、「取り込めなかった」ことを取り込みパイプライン自身が検知できる形にしておくほうが、次の未知のフォーマット崩れにも気づける。
この記事で確かめたこと・確かめなかったこと
- 確かめた: 修正が今のDBでも欠損ゼロを保っていること(全期間・直近11日間とも欠損0件)。
- 確かめた: 今のDBでの1号艇1着率・出走表カバレッジの現在値。
- 確かめなかった: 欠けていた6.17%のレースそのものの特徴(会場・時期の偏りなど)。当時のデータは取り込み直しで上書きされており、今のDBから欠損時点の状態を再現することはできない。「2026年8月は31日中23日で欠落」という記述はdocsの実測を引用したもので、本記事で作り直した数字ではない。
免責
- 本記事に記載した数値はすべて過去データに対する実測であり、将来の的中や利益を保証するものではありません。
- 本記事および言及したツールは分析の補助を目的としており、舟券の購入を勧誘するものではありません。
- 舟券は20歳未満の方は購入できません。
- 舟券の購入は自己責任で、生活に必要な資金とは別の、余裕資金の範囲で行ってください。のめり込みには十分ご注意ください。
この記事は note でも同じ内容を公開しています。note で読む