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

# はじめに
　先日、[サッカー　逆転可能な勝ち点差＝残り試合数なのか？](https://qiita.com/bebebefu/items/1b8f363670770682d321)の記事でサッカーのリーグ戦において「逆転可能な勝ち点差は残り試合数と同じ」や「追いつける勝ち点は試合数と同じ」なのかということを2003年のJ1リーグの結果で調査してみました。
 その結果として勝ち点差よりも数試合多くかかりそうという結果が得られましたが、2003年のデータだけなので非常に限定的な結果しか得られていないという課題も記載しました。本記事はそれに対応するため2004年～2022年のJ1リーグの結果を用いて調査したものになります。

# 方法
- 調査対象：2003年～2022年の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に出力し、エクセルを用いて目視確認します。（2003年は全チームを対象に目視確認しましたが、さすがに大変なので2004年以降は毎年度1チームを対象にサンプル的に目視確認しました。）
　
　- 手順3：各勝ち点差毎に追いつくもしくは追い抜くまでにかかった試合数をまとめ、平均値と標準偏差を計算し評価します。

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

# 作成したコード
前回の記事ではチーム数の変動やACLやカップ戦などの影響で試合日程が変更になった場合に対応していませんでしたが、今回は対応させました。
```
import pandas as pd
from collections import defaultdict

# J1リーグの試合結果のCSVファイルのパス
file_path = 'J1_2003-2022.csv'

# CSVデータの読み込み
df = pd.read_csv(file_path)

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

# 2003年から2022年までループ
for year in range(2003, 2023):
    df_year = df[df['Season_End_Year'] == year]
    teams = df_year['HomeTeam'].unique()

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

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

    # データ処理
    for index, row in df_year.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
        elif result == 2:  # アウェイチームが勝利
            points[away_team] += 3
            points[home_team] += 0

        # 各節終了後に全チームの勝ち点を記録
        for team in teams:
            if team == home_team:
                points_by_round[team].append(points[home_team])
            if team == away_team:
                points_by_round[team].append(points[away_team])

    # 勝ち点推移のデータを保存
    points_by_round_data = []
    for team, points_list in points_by_round.items():
        points_by_round_data.append([year, team] + points_list)

    for target_team in teams:
        for other_team in teams:
            if target_team == other_team:
                continue
            
            for round_number, (target_points, other_points) in enumerate(zip(points_by_round[target_team], points_by_round[other_team])):
                if target_points < other_points:
                    catch_up_round = round_number + 1
                    catch_up_points_diff = other_points - target_points

                    # この節以降で追いつくまでの節を調査
                    for subround_number, (sub_target_points, sub_other_points) in enumerate(zip(points_by_round[target_team][round_number:], points_by_round[other_team][round_number:]), start=round_number):
                        if sub_target_points >= sub_other_points:
                            catch_up_data.append([year, target_team, other_team, subround_number + 1 - catch_up_round, catch_up_round, catch_up_points_diff])
                            break


    # 勝ち点推移のデータを保存
    points_by_round_data = []
    for team, points_list in points_by_round.items():
        points_by_round_data.append([year, team] + points_list)

    # CSVに保存
    columns = ['Year', 'Team'] + [f'Round_{i+1}' for i in range(len(points_list))]
    points_by_round_df = pd.DataFrame(points_by_round_data, columns=columns)
    points_by_round_df.to_csv(f'points_by_round_{year}.csv', index=False)

# 追いつきデータをCSVに保存
catch_up_df = pd.DataFrame(catch_up_data, columns=['Year', 'Target_Team', 'Other_Team', 'Catch_Up_Games', 'First_Diff_Round', 'First_Diff_Points'])
catch_up_df.to_csv('catch_up_data.csv', index=False)
```

# 結果

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

|勝ち点差  |試合数　平均値  |試合数　標準偏差|データ数|
|---|---|---|---|
|1  | 3.53| 4.49| 6663|
|2  | 4.43| 4.94| 5965|
|3  | 5.39| 5.72| 4937|
|4  | 7.06| 5.70| 2894|
|5  | 7.94| 5.84| 2347|
|6  | 8.96| 6.04| 1557|
|7  | 10.00| 6.04| 1073|
|8  | 10.81| 6.00| 693|
|9  | 11.65 | 5.36| 447|
|10  | 13.31| 6.00| 316|
|11  | 12.25| 5.22| 180|
|12  | 13.66| 4.61| 126|
|13  | 13.95| 5.16| 76|
|14  | 13.69| 4.98| 49|
|15  | 13.15| 4.64| 26|
|16  | 13.94| 4.44| 17|
|17  | 14.13| 3.40| 8|
|18  | 14.75| 0.96| 4|
|19  | 13.33| 2.88| 6|
|20  | 17.50| 0.71| 2|
|21  | 15.00| -| 1|
|22  | 16.00| 1.58| 5|

# 考察とまとめ
　得られた結果より、先日の[サッカー　逆転可能な勝ち点差＝残り試合数なのか？](https://qiita.com/bebebefu/items/1b8f363670770682d321)の記事でも記載しましたが、「追いつける勝ち点＝試合数」とは厳密には言えないと今回の結果からも考えられます。
　勝ち点差10以下について平均値で評価するとだいたい「追いつける勝ち点＝試合数+2～+3」の傾向があることが示唆されます。次に勝ち点差11以上13以下の場合の平均値で評価すると「追いつける勝ち点＝試合数+1～+2」の傾向があることが示唆されます。一方で勝ち点差14以上になりますと「追いつける勝ち点>試合数」となることがわかりました。ただ、勝ち点差14以上の大きな勝ち点差がつくのはシーズン中盤ぐらいからと考えられます。このことが追いつけた場合の試合数が少なくなることにつながっていると考えられます。
 　ただ、いずれの場合もばらつきが大きくあり、「追いつける勝ち点＝試合数+〇」とは言い切れず、今回の結果からも、どのケースも「その傾向が示唆される」ぐらいと考えられます。
　今回J1リーグ20年の結果を調査して、私が一番思ったのは「J1リーグ20年の結果を調査してみたら、応援してるチームがあのチームに追いつくには、勝ち点+2～+3の試合数を消費する必要が最低でもありそうだな。シーズン中に追いつけるのか、ちょっとキツイな。」ということでした。皆さんも応援してるチームがあるかもしれませんが、残りのシーズンはあと2節!!(2023年11月中旬現在)、頑張ってほしいですね！