サッカー　逆転可能な勝ち点差＝残り試合数なのか？

# はじめに
　サッカーのリーグ戦において「逆転可能な勝ち点差は残り試合数と同じ」や「追いつける勝ち点は試合数と同じ」なんてことをよく言われます。この言葉の出典はよくわかりませんが、長年の経験や統計に基づくものと考えられます。
　しかし勝ち点が3ついたとしても、次節で自チームが勝って相手チームが負ければ追いつける場合もあるわけです。その場合は3試合も消化せず1試合で追いついてるわけですが、先ほども述べたように「追いつける勝ち点は試合数と同じ」という言葉は長年の経験を背景としたものと考えられますし、自分の応援しているチームの状況を見ても、なんとなく当てはまっているようにも感じます。
　そんなことをふと思い、プログラムを用いて「勝ち点差が開いているところから何試合後に追いついたか」ということを調査したことを本記事では書きたいと思います。

# 方法
- 調査対象：2003年のJ1リーグ(この当時は1stステージと2ndステージに分かれていましたが、ここでは簡単のために通年でリーグが行われたとします)
- 使用データ：リンクのサイト[http://mikami3345.html.xdomain.jp/SoccerData/MatchData.html](http://mikami3345.html.xdomain.jp/SoccerData/MatchData.html)より、J1_2003-2022.csvをダウンロードして用います。
- 調査環境：OS Window11にAnacondaをインストールした環境
- プログラミング言語とバージョン：python 3.11.5
- 調査方法
　- 手順1：全チームを対象に、他チームに勝ち点差があるところから、差をつけられたチームに勝ち点で追いつくまたは追い抜くまでにかかった試合数をカウントする。
　
　- 手順2：手順１でカウントした結果をcsvに出力し、エクセルを用いて目視確認する。
　
　- 手順3：各勝ち点差毎に追いつくもしくは追い抜くまでにかかった試合数をまとめ、平均値と標準偏差を計算し評価する。

手順2については、スマートな方法ではありませんが、どんなデータになるのか興味があるため一旦csvに出力して自分の目で見るという方法をとっています。

# 作成したコード

```
import pandas as pd
from collections import defaultdict

# 出力するCSVファイル用のリスト
catch_up_data = []
points_by_round_data = []

# ファイルのパス
file_path = 'J1_2003-2022.csv'

df = pd.read_csv(file_path)

# 2003年のデータだけをフィルタ
df_2003 = df[df['Season_End_Year'] == 2003]

# 各チームの勝ち点を保持するための辞書
points = defaultdict(int)

# 節ごとの勝ち点（各チーム）を保持するためのリスト
points_by_round = defaultdict(list)

# 節カウンター
current_round = 1

# データ処理
for index, row in df_2003.iterrows():
    home_team = row['HomeTeam']
    away_team = row['AwayTeam']
    result = row['Result']

    # 勝ち点を計算
    if result == 0:
        points[home_team] += 3
        points[away_team] += 0
    elif result == 1:
        points[home_team] += 1
        points[away_team] += 1
    else:
        points[away_team] += 3
        points[home_team] += 0

    if (index + 1) % 8 == 0:
        current_round += 1

        for team, point in points.items():
            points_by_round[team].append(point)

        for target_team in list(points.keys()):
            for other_team in list(points.keys()):
                if other_team == target_team:
                    continue

                if points[target_team] < points[other_team]:
                    first_diff_round = current_round
                    first_diff_points = points[other_team] - points[target_team]

                    temp_points = points.copy()

                    catch_up_games = 0

                    for sub_index, sub_row in df_2003.iloc[index + 1:].iterrows():
                        sub_home_team = sub_row['HomeTeam']
                        sub_away_team = sub_row['AwayTeam']
                        sub_result = sub_row['Result']

                        if sub_result == 0:
                            points[sub_home_team] += 3
                        elif sub_result == 1:
                            points[sub_home_team] += 1
                            points[sub_away_team] += 1
                        else:
                            points[sub_away_team] += 3

                        if (sub_index + 1) % 8 == 0:
                            catch_up_games += 1
                            if points[target_team] >= points[other_team]:
                                catch_up_data.append([target_team, other_team, catch_up_games, first_diff_round, first_diff_points])
                                break

                    points = temp_points

for team, points_list in points_by_round.items():
    points_by_round_data.append([team] + points_list)

# CSVに保存
catch_up_df = pd.DataFrame(catch_up_data, columns=['Target_Team', 'Other_Team', 'Catch_Up_Games', 'First_Diff_Round', 'First_Diff_Points'])
catch_up_df.to_csv('catch_up_data.csv', index=False)

points_by_round_df = pd.DataFrame(points_by_round_data, columns=['Team'] + [f'Round_{i+1}' for i in range(len(points_list))])
points_by_round_df.to_csv('points_by_round.csv', index=False)
```

# 結果
結果を以下の表に示します。

勝ち点差：差をつけられたチームとの勝ち点差
平均値：差をつけられたチームに勝ち点で追いつくまたは追い抜くまでにかかった試合数の平均値
標準偏差：　差をつけられたチームに勝ち点で追いつくまたは追い抜くまでにかかった試合数の標準偏差

|勝ち点差  |試合数　平均値  |試合数　標準偏差|
|---|---|---|
|1  |  3.13|  3.53|
|2  |  3.93|  4.58|
|3  |  5.19|  5.20|
|4  |  5.93|  4.27|
|5  |  7.69|  5.55|
|6  |  10.42|  6.21|
|7  |  10.23|  4.74|
|8  |  9.00|  4.61|
|9  |  9.80|  4.10|
|10  |  8.00|  4.36|
|11  |  16.50|  0.71|

# 考察
　得られた結果より「追いつける勝ち点＝試合数」とは厳密には言えないと考えられます。
　勝ち点差5以下について平均値で評価するとだいたい「追いつける勝ち点＝試合数+2」の傾向があることが示唆されます。次に勝ち点差6以上場合の平均値で評価すると、勝ち点差6以上7以下ですと「追いつける勝ち点＝試合数+4」の傾向があることが示唆されますが、勝ち点差8以上9以下ですと「追いつける勝ち点＝試合数+1」の傾向が示唆されます。
 　ただ、いずれの場合もばらつきが大きくあり、「追いつける勝ち点＝試合数+〇」とは言い切れず、今回の結果からは「その傾向が示唆される」ぐらいと考えられます。
　なお勝ち点差10や11についてはデータが少ないので、ここでは参考程度にしかならないと考えています。

# 課題
　本記事では、2003年のJ1リーグのデータを用いて検証を行いましたが、データが非常に限定的なものであるため、今後はより多くのデータを用いての検証が必要です。
　そのため2004年以降のデータについても追加で分析していきたいと思いますが、現状コードではチーム数の変動やACLやカップ戦などの影響で試合日程が変更になった場合に対応できていません。コードの修正ができ次第また分析を進めていきたいと思います。

👆の課題について[サッカー　逆転可能な勝ち点差＝残り試合数なのか？その2](https://qiita.com/bebebefu/items/20dbf6081e0c403d488c)に対応してみました。