機械学習を使用して 2022 年ワールド カップを予測する
記事ディレクトリ
プロジェクト指導
- このプロジェクトは、Kaggle のオープン ソース Baseline に基づいて調整および最適化されており、ベースラインは行ごとに説明されています。
- このプロジェクトで使用されるデータ セットは、1992 年から 2022 年までの FIFA 世界ランキングと、1872 年から 2022 年までの国際サッカーの結果です。
- このプロジェクトでは、問題を分類問題に変換します。つまり、最終的なモデルの目標は、ホーム チームの勝率とアウェイ ゲームの引き分け/勝率を予測することです。
- アウェイチームのアドバンテージを取り除くために、プロジェクトではアウェイチームとホームチームの間の変化の結果が予測され(ワールドカップではホームアドバンテージがないため)、2 つの予測の平均が使用されます。確率として。
- 必要な学生は、ハイパーリンクをクリックしてダウンロードしてください。
データセットの説明
国際サッカーの結果 1872-2022
- このデータセットには、1872 年から 2022 年までの最初の公式試合から 44,152 の国際サッカー試合の結果が含まれています。
- 大会は、FIFA ワールド カップから FIFA ワイルド カップ、通常の親善試合までさまざまです。
- これらの試合は厳密に男子の公式国際大会であり、オリンピックの試合や、少なくとも 1 チームが B チーム、U-23、リーグ選抜チームである試合は含まれていません。
results.csv には、次の列が含まれています。
- date - 試合日
- home_team - ホームチーム名
- away_team - アウェイ チーム名
- home_score - PK戦を除く、延長戦を含むフルタイムのホームチームのスコア
- away_score - PK戦を除く、延長戦を含むフルタイムのアウェイ スコア
- トーナメント - トーナメント名
- 市 - レースが開催される市/町/行政単位の名前
- country - 試合が行われる国の名前
- ニュートラル - 試合がニュートラル グラウンドでプレーされるかどうかを示す TRUE/FALSE 列
shotouts.csvには、次の列が含まれています。
- date - 試合日
- home_team - ホームチーム名
- away_team - アウェイ チーム名
- 勝者 - PK戦の勝者
FIFA 世界ランキング 1992-2022
- country_full — 完全な国名
- country_abrv — 国の略語
- rank — 現在の国のランク
- total_points — 現在の合計ポイント
- previous_points — 以前の評価の合計ポイント
- rank_change — ランク変更
- コンフェデレーション — FIFA コンフェデレーション
- rank_date — ランクが計算された日付
データ分析と前処理
データ準備
#解压数据集
!unzip -d datasets/international-football-results-from-1872-to-2017 1872年至2022年国际足球成绩.zip
!unzip -d datasets/fifaworldranking 国际足联世界排名1992-2022.zip
1872 年から 2022 年までの国際サッカー スコアの分析と前処理
1872年から2022年の国際サッカー結果からresult.csvをインポート
import pandas as pd
import numpy as np
import re
df = pd.read_csv("datasets/international-football-results-from-1872-to-2017/results.csv")
df.head()
まずはデータをプレビューして、その大まかな構造を知り、
データの基本情報を確認しましょう
df.info()
- データは日付を表していることがわかりますが、日付の形式ではないため、前処理時に日付の形式に変更する必要があります
- この表の 2 つの列のみが連続特徴であり、残りの特徴は離散的です
欠損値のチェック
#缺失值查看、
df.isna().sum()
- 同時に 40 個の欠損値を含む特徴の列が 2 つあることがわかります。
- そして、これら 2 列の機能は、まさに最も重要なスコア機能を表しています。
- スコア機能がないと、その後のモデリングを実行できないため、スコアが欠落しているサンプルを削除する必要があります。
スコアが欠落しているサンプルを削除し、日付形式を変更します
#删除缺失值所在的行
df.dropna(inplace=True)
#将日期列的格式转换为日期格式
df["date"] = pd.to_datetime(df["date"])
ただし、このように直接実行するとエラーが報告されます。実行後、日付列に文字列 '2022-19-22' (通常の日付ロジックに準拠していない) が含まれていることが判明したため、次を含む行を削除する必要があるためです。この文字列を最初に処理してから、データを処理します。
df = df.drop(df[df['date']=='2022-19-22'].index,axis=0)
使用するデータセットは、2018 FIFA オリンピック (2018 ワールド カップ後、2022 ワールド カップ前の最後の試合) です。アイデアは、ワールド カップの準備と分類段階で試合の状況を分析することです。
したがって、データセットをフィルタリングしたい
- 2022年の最後の数試合を見てみましょう
df.sort_values("date").tail()
- 2018 年 8 月 1 日以降のゲームを除外し、インデックスをリセットします
df = df[(df["date"] >= "2018-8-1")].reset_index(drop=True)
df.sort_values('data').tail()
FIFA World Rankings 1992-2022 データセットの分析と前処理
前と同じように、最初に日付形式を変換し、2018 年 8 月 1 日以降のデータを抽出する必要があります。
rank = pd.read_csv("datasets/fifaworldranking/fifa_ranking-2022-10-06.csv")
rank["rank_date"] = pd.to_datetime(rank["rank_date"]) #转换日期格式
rank = rank[(rank["rank_date"] >= "2018-8-1")].reset_index(drop=True) #筛选数据集
ワールド カップの一部のチームは、ランキング データセットで異なる名前を持っています。ですから、調整が必要です。
rank["country_full"] = rank["country_full"].str.replace("IR Iran", "Iran").str.replace("Korea Republic", "South Korea").str.replace("USA", "United States")
2 つのテーブルをマージする
次に、データ セットをマージする必要があります. マージは、ワールド カップのデータ セットとそのランキングを取得することです.
- 日付をインデックスとして設定し、次に国別にグループ化し、毎日の最初のデータをデータとして再サンプリングし、最後にインデックスをリセットします
- 空の場合は、フォワード フィル方式を使用します。
rank = rank.set_index(['rank_date']).groupby(['country_full'], group_keys=False).resample('D').first().fillna(method='ffill').reset_index()
- ランク テーブルで機能「country_full」、「total_points」、「previous_points」、「rank」、「rank_change」、「rank_date」を選択して、df テーブルとマージします
- そして、左側のテーブルの日付、home_team、右側のテーブルのrank_dateとcountry_fullに従って、左右のアライメントを実行します
- 左と右のテーブルには重複する機能列があるため、そのうちの 1 つだけを取得する必要があるため、ここでは rank_date と country_full を削除することを選択します
df_wc_ranked = df.merge(rank[["country_full", "total_points", "previous_points", "rank", "rank_change", "rank_date"]],
left_on=["date", "home_team"], right_on=["rank_date", "country_full"]).drop(["rank_date", "country_full"], axis=1)
- result.csv の home_team (ホーム チーム名) と away_team (ビジター チーム名) のほかに、
- 上記のマージは、ホーム チームのデータをマージするだけですが、ビジター チームのデータはまだマージされていないため、再度マージする必要があります。
- ここでは、ランクの機能列を上記と同じにしますが、左揃えのホームチームがアウェイチームになります
- 一度マージしただけなので、再度マージすると列名の重複が多くなります。また、ホームチームとアウェイチームの特徴を区別するために、ホームチームの順位特徴列の接尾辞を_homeに、アウェイチームの順位特徴列の接尾辞を_awayに変更します。
- 最後に、今と同じで、残りの繰り返し機能の 1 つ (時間や国名など) を取得できます。
df_wc_ranked = df_wc_ranked.merge(rank[["country_full", "total_points", "previous_points", "rank", "rank_change", "rank_date"]],
left_on=["date", "away_team"], right_on=["rank_date", "country_full"],
suffixes=("_home", "_away")).drop(["rank_date", "country_full"], axis=1)
統合後、ホーム チームとアウェイ チームの両方がブラジルであるデータの一部を見てみましょう。
df_wc_ranked[(df_wc_ranked.home_team == "Brazil") | (df_wc_ranked.away_team == "Brazil")].tail()
データの準備ができたので、データセットに対して特徴エンジニアリングを実行できます
特徴エンジニアリング
- ここでのアイデアは、サッカーの試合の結果に影響を与える機能をさらに作成することです
- 影響の特徴としては、
1. チームの過去の得点
2. チームの過去の得点と失点
3. チームのランキング
4. チームのランキングの上昇
5. ランキングに直面している進行状況 ボールと負け
6. ゲームの重要性 (友好的かどうか) - そのため、関数を作成したいと思います: どちらのチームが勝ったか、ゲームで何点を獲得したかを決定します。
勝敗を判定する関数をカプセル化する
df = df_wc_ranked
def result_finder(home, away):
if home > away:
return pd.Series([0, 3, 0])
if home < away:
return pd.Series([1, 0, 3])
else:
return pd.Series([2, 1, 1])
results = df.apply(lambda x: result_finder(x["home_score"], x["away_score"]), axis=1)
df[["result", "home_team_points", "away_team_points"]] = results
仮説検定
- ゲームポイントは、データベース内の既存のランキングポイントとは異なり、勝利で 3 ポイント、引き分けで 1 ポイント、敗北で 0 ポイントです。
- さらに、データセット内のランキング ポイントと同じチームのランキングには負の相関関係があると考えており、新しい機能を作成するには、そのうちの 1 つだけを使用する必要があります。
- 以下は、この仮説のテストです。
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(15, 10))
sns.heatmap(df[["total_points_home", "rank_home", "total_points_away", "rank_away"]].corr())
plt.show()
機能の導出
- 次に、モデリングに適した機能を作成する必要があります
- 例:
1. ランキングの差
2. 対戦したチームとの試合で獲得したポイントのランキング
3. 試合でのゴールの差。
違いの一部ではないすべての機能は、両方のチーム (アウェイとホーム) 用に作成する必要があります。
df["rank_dif"] = df["rank_home"] - df["rank_away"] #排名差异
df["sg"] = df["home_score"] - df["away_score"] #分数差异
df["points_home_by_rank"] = df["home_team_points"]/df["rank_away"] #主场队伍进球与排名的关系
df["points_away_by_rank"] = df["away_team_points"]/df["rank_home"] #客场队伍进球与排名的关系
特徴をより適切に導出するために、データセットをホーム チームとアウェイ チームのデータセットに分割し、それらをマージして過去の試合のさまざまな特徴を計算します。
それらを分離・結合してオリジナルのデータセットを構築します。
このプロセスは特徴の導出を最適化します
- まず、データセットをホーム データセットとアウェイ データセットに分割します
home_team = df[["date", "home_team", "home_score", "away_score", "rank_home", "rank_away","rank_change_home", "total_points_home", "result", "rank_dif", "points_home_by_rank", "home_team_points"]]
away_team = df[["date", "away_team", "away_score", "home_score", "rank_away", "rank_home","rank_change_away", "total_points_away", "result", "rank_dif", "points_away_by_rank", "away_team_points"]]
- データセットがマージされたときに特徴列の名前が変更されたため、以降の処理のために特徴列の名前を元の名前に変更する必要があります
home_team.columns = [h.replace("home_", "").replace("_home", "").replace("away_", "suf_").replace("_away", "_suf") for h in home_team.columns]
away_team.columns = [a.replace("away_", "").replace("_away", "").replace("home_", "suf_").replace("_home", "_suf") for a in away_team.columns]
- 特徴計算のためにそれらを一緒に追加します
team_stats = home_team.append(away_team)
- これらの列は特徴の計算に使用されます
team_stats_raw = team_stats.copy()
これで、さらなる機能導出の準備ができたデータセットができました。導出される列は次のとおりです。
- ワールド カップ サイクルにおけるチームの平均ゴール -- ワールド カップ チームの平均ゴール数
- 過去 5 試合でのチームの平均ゴール -- 過去 5 試合でのチームの平均ゴール
- ワールド カップ サイクルでチームが被った平均ゴール -- ワールド カップ チームが犯したファウルの平均数
- 過去 5 試合でチームが被った平均ゴール -- 過去 5 試合でチームが犯したファウルの平均数
- ワールド カップ サイクルでチームが対戦した平均 FIFA ランク -- ワールド カップでのチームの平均 FIFA ランク
- 過去 5 試合でチームが対戦した平均 FIFA ランク -- 過去 5 試合でのチームの平均 FIFA ランク
- サイクルで獲得した FIFA ポイント --FIFA ポイント
- 過去 5 試合で獲得した FIFA ポイント -- 過去 5 FIFA ポイント
- サイクルでの平均ゲーム ポイント -- ゲーム ポイント
- 過去 5 試合の平均得点 -- 直近 5 試合の得点
- サイクルで直面したランクごとの平均ゲーム ポイント。
- 過去 5 試合で対戦したランクごとの平均ゲーム ポイント。
stats_val = []
for index, row in team_stats.iterrows():
team = row["team"]
date = row["date"]
past_games = team_stats.loc[(team_stats["team"] == team) & (team_stats["date"] < date)].sort_values(by=['date'], ascending=False)
last5 = past_games.head(5) #取出过去五场比赛
goals = past_games["score"].mean()
goals_l5 = last5["score"].mean()
goals_suf = past_games["suf_score"].mean()
goals_suf_l5 = last5["suf_score"].mean()
rank = past_games["rank_suf"].mean()
rank_l5 = last5["rank_suf"].mean()
if len(last5) > 0:
points = past_games["total_points"].values[0] - past_games["total_points"].values[-1]#qtd de pontos ganhos
points_l5 = last5["total_points"].values[0] - last5["total_points"].values[-1]
else:
points = 0
points_l5 = 0
gp = past_games["team_points"].mean()
gp_l5 = last5["team_points"].mean()
gp_rank = past_games["points_by_rank"].mean()
gp_rank_l5 = last5["points_by_rank"].mean()
stats_val.append([goals, goals_l5, goals_suf, goals_suf_l5, rank, rank_l5, points, points_l5, gp, gp_l5, gp_rank, gp_rank_l5])
- 新しく派生した機能を元のテーブルとマージします
- full_df を再利用して受け取る
stats_cols = ["goals_mean", "goals_mean_l5", "goals_suf_mean", "goals_suf_mean_l5", "rank_mean", "rank_mean_l5", "points_mean", "points_mean_l5", "game_points_mean", "game_points_mean_l5", "game_points_rank_mean", "game_points_rank_mean_l5"]
stats_df = pd.DataFrame(stats_val, columns=stats_cols)
full_df = pd.concat([team_stats.reset_index(drop=True), stats_df], axis=1, ignore_index=False)
- マージされたデータセットをホームゲームとアウェイゲームに再度分割します
home_team_stats = full_df.iloc[:int(full_df.shape[0]/2),:]
away_team_stats = full_df.iloc[int(full_df.shape[0]/2):,:]
- 先ほどの特徴から派生した列を取り出します
home_team_stats = home_team_stats[home_team_stats.columns[-12:]]
away_team_stats = away_team_stats[away_team_stats.columns[-12:]]
- 名前を変更します (home_ はホームの略です) (away_ はアウェイの略です)
データセットを統一するには、各列にホームとアウェイの接尾辞を追加する必要があります。その後、データを結合して使用できます
home_team_stats.columns = ['home_'+str(col) for col in home_team_stats.columns]
away_team_stats.columns = ['away_'+str(col) for col in away_team_stats.columns]
- データマージ
match_stats = pd.concat([home_team_stats, away_team_stats.reset_index(drop=True)], axis=1, ignore_index=False)
full_df = pd.concat([df, match_stats.reset_index(drop=True)], axis=1, ignore_index=False)
full_df.columns
既存の機能列を見てみましょう
- ゲームが友好的かどうかを判断するために、それを判断する関数をカプセル化します
def find_friendly(x):
if x == "Friendly":
return 1
else: return 0
full_df["is_friendly"] = full_df["tournament"].apply(lambda x: find_friendly(x))
- ワンホットエンコードする
full_df = pd.get_dummies(full_df, columns=["is_friendly"])
特徴エンジニアリング後にデータセットに対してデータ分析を実行する
- ここでは、分析のために機能分析に寄与する列のみを選択します
base_df = full_df[["date", "home_team", "away_team", "rank_home", "rank_away","home_score", "away_score","result", "rank_dif", "rank_change_home", "rank_change_away", 'home_goals_mean',
'home_goals_mean_l5', 'home_goals_suf_mean', 'home_goals_suf_mean_l5',
'home_rank_mean', 'home_rank_mean_l5', 'home_points_mean',
'home_points_mean_l5', 'away_goals_mean', 'away_goals_mean_l5',
'away_goals_suf_mean', 'away_goals_suf_mean_l5', 'away_rank_mean',
'away_rank_mean_l5', 'away_points_mean', 'away_points_mean_l5','home_game_points_mean', 'home_game_points_mean_l5',
'home_game_points_rank_mean', 'home_game_points_rank_mean_l5','away_game_points_mean',
'away_game_points_mean_l5', 'away_game_points_rank_mean',
'away_game_points_rank_mean_l5',
'is_friendly_0', 'is_friendly_1']]
base_df.head()
- 欠損値のチェック
base_df.isna().sum()
- null 値を持つ行の平均値は計算できないことがわかっているため、null 値を持つサンプルを削除する必要があります
base_df_no_fg = base_df.dropna()
ここで、作成されたすべての機能を分析し、予測力があるかどうかを確認する必要があります。また、そうでない場合は、ホーム チームとアウェイ チームの違いなど、いくつかの予測機能を作成する必要があります。予測力を分析するために、引き分けをホーム チームの負けとして指定し、問題をバイナリとして分類します。
df = base_df_no_fg
def no_draw(x):
if x == 2:
return 1
else:
return x
df["target"] = df["result"].apply(lambda x: no_draw(x))
バイオリン プロットと箱ひげ図を使用して特徴をフィルター処理する
- 次に、バイオリン プロットとボックス プロットを使用して、対象によって特徴量の分布が異なるかどうかを分析します。
- 散布図を使用して相関関係を分析する
- 画像をより直感的にするために、いくつかの機能を抽出して同じキャンバスに描画し、次のキャンバスに機能の別の部分を描画します
data1 = df[list(df.columns[8:20].values) + ["target"]]
data2 = df[df.columns[20:]]
- フィーチャを正規化する
scaled = (data1[:-1] - data1[:-1].mean()) / data1[:-1].std()
scaled["target"] = data1["target"]
violin1 = pd.melt(scaled,id_vars="target", var_name="features", value_name="value")
scaled = (data2[:-1] - data2[:-1].mean()) / data2[:-1].std()
scaled["target"] = data2["target"]
violin2 = pd.melt(scaled,id_vars="target", var_name="features", value_name="value")
- data1 に特徴バイオリン プロットを描画する
plt.figure(figsize=(15,10))
sns.violinplot(x="features", y="value", hue="target", data=violin1,split=True, inner="quart")
plt.xticks(rotation=90)
plt.show()
- data2 の特徴的なバイオリン図を描く
plt.figure(figsize=(15,10))
sns.violinplot(x="features", y="value", hue="target", data=violin2,split=True, inner="quart")
plt.xticks(rotation=90)
plt.show()
これらのプロットを見ると、順位差がデータの唯一の適切な区切りであることがわかります。ただし、ホーム チームとアウェイ チームの違いを取得し、データが適切に分離されているかどうかを分析するためのいくつかの機能を作成できます。
- ホームゲームとアウェイゲームの違いをよりよく調べるために、ホームゲームとアウェイゲームの特徴的な平均の違いを見つけて標準化し、バイオリンプロットを描きます
dif = df.copy()
dif.loc[:, "goals_dif"] = dif["home_goals_mean"] - dif["away_goals_mean"]
dif.loc[:, "goals_dif_l5"] = dif["home_goals_mean_l5"] - dif["away_goals_mean_l5"]
dif.loc[:, "goals_suf_dif"] = dif["home_goals_suf_mean"] - dif["away_goals_suf_mean"]
dif.loc[:, "goals_suf_dif_l5"] = dif["home_goals_suf_mean_l5"] - dif["away_goals_suf_mean_l5"]
dif.loc[:, "goals_made_suf_dif"] = dif["home_goals_mean"] - dif["away_goals_suf_mean"]
dif.loc[:, "goals_made_suf_dif_l5"] = dif["home_goals_mean_l5"] - dif["away_goals_suf_mean_l5"]
dif.loc[:, "goals_suf_made_dif"] = dif["home_goals_suf_mean"] - dif["away_goals_mean"]
dif.loc[:, "goals_suf_made_dif_l5"] = dif["home_goals_suf_mean_l5"] - dif["away_goals_mean_l5"]
data_difs = dif.iloc[:, -8:]
scaled = (data_difs - data_difs.mean()) / data_difs.std()
scaled["target"] = data2["target"]
violin = pd.melt(scaled,id_vars="target", var_name="features", value_name="value")
plt.figure(figsize=(10,10))
sns.violinplot(x="features", y="value", hue="target", data=violin,split=True, inner="quart")
plt.xticks(rotation=90)
plt.show()
このプロットからわかるように、得点されたゴール数の差は、ファウルの数と同様に、良い区切りです。しかし、各チームの得点数と失点数の差は、適切な識別基準にはなりません。
- 次に、次の 5 つの機能を除外します。
- ran_dif
- ゴールディフ
- goal_dif_l5
- goal_suf_dif
- goal_suf_dif_l5
- 次に、取得スコアの違い、ランキングの違いなど、他の特徴を作成することもできます。
dif.loc[:, "dif_points"] = dif["home_game_points_mean"] - dif["away_game_points_mean"]
dif.loc[:, "dif_points_l5"] = dif["home_game_points_mean_l5"] - dif["away_game_points_mean_l5"]
dif.loc[:, "dif_points_rank"] = dif["home_game_points_rank_mean"] - dif["away_game_points_rank_mean"]
dif.loc[:, "dif_points_rank_l5"] = dif["home_game_points_rank_mean_l5"] - dif["away_game_points_rank_mean_l5"]
dif.loc[:, "dif_rank_agst"] = dif["home_rank_mean"] - dif["away_rank_mean"]
dif.loc[:, "dif_rank_agst_l5"] = dif["home_rank_mean_l5"] - dif["away_rank_mean_l5"]
- また、得点とファウルの影響をグレードごとに計算し、この違いを調べることができます
dif.loc[:, "goals_per_ranking_dif"] = (dif["home_goals_mean"] / dif["home_rank_mean"]) - (dif["away_goals_mean"] / dif["away_rank_mean"])
dif.loc[:, "goals_per_ranking_suf_dif"] = (dif["home_goals_suf_mean"] / dif["home_rank_mean"]) - (dif["away_goals_suf_mean"] / dif["away_rank_mean"])
dif.loc[:, "goals_per_ranking_dif_l5"] = (dif["home_goals_mean_l5"] / dif["home_rank_mean"]) - (dif["away_goals_mean_l5"] / dif["away_rank_mean"])
dif.loc[:, "goals_per_ranking_suf_dif_l5"] = (dif["home_goals_suf_mean_l5"] / dif["home_rank_mean"]) - (dif["away_goals_suf_mean_l5"] / dif["away_rank_mean"])
- いつものように、新しく構築された特徴を正規化し、バイオリン プロットで視覚化します。
data_difs = dif.iloc[:, -10:]
scaled = (data_difs - data_difs.mean()) / data_difs.std()
scaled["target"] = data2["target"]
violin = pd.melt(scaled,id_vars="target", var_name="features", value_name="value")
plt.figure(figsize=(15,10))
sns.violinplot(x="features", y="value", hue="target", data=violin,split=True, inner="quart")
plt.xticks(rotation=90)
plt.show()
値が低いため、ヴァイオリン プロットからは良いフィードバックが得られないため、これらの機能についてはボックス プロットを使用します。
plt.figure(figsize=(15,10))
sns.boxplot(x="features", y="value", hue="target", data=violin)
plt.xticks(rotation=90)
plt.show()
- このことから、勝点差(全試合と直近5試合)、対戦順位別の勝点差(全試合と直近5試合)、対戦順位差(全試合と直近5試合)が良い特徴であることがわかります。
- また、一部の派生フィーチャの分布は非常に似ているため、分析に散布図を使用します。
sns.jointplot(data = data_difs, x = 'goals_per_ranking_dif', y = 'goals_per_ranking_dif_l5', kind="reg")
plt.show()
- dif_rank_agst と dif_rank_agst_l5 の機能分布は非常に似ているため、プロットにはフル バージョン (dif_rank_agst) のみを使用します。
sns.jointplot(data = data_difs, x = 'dif_rank_agst', y = 'dif_rank_agst_l5', kind="reg")
plt.show()
- スコア機能の場合
sns.jointplot(data = data_difs, x = 'dif_points', y = 'dif_points_l5', kind="reg")
plt.show()
- スコアに対する機能のランキング
sns.jointplot(data = data_difs, x = 'dif_points_rank', y = 'dif_points_rank_l5', kind="reg")
plt.show()
2 つのバージョン (すべての統計、過去 5 試合) はそれほど似ていないため、両方を使用することにしました。したがって、機能スクリーニングの最終結果は次のとおりです。
- ランク差
- ゴールディフ
- goal_dif_l5
- goal_suf_dif
- goal_suf_dif_l5
- dif_rank_agst
- dif_rank_agst_l5
- goal_per_ranking_dif
- dif_points_rank
- dif_points_rank_l5
- フレンドリーです
def create_db(df):
columns = ["home_team", "away_team", "target", "rank_dif", "home_goals_mean", "home_rank_mean", "away_goals_mean", "away_rank_mean", "home_rank_mean_l5", "away_rank_mean_l5", "home_goals_suf_mean", "away_goals_suf_mean", "home_goals_mean_l5", "away_goals_mean_l5", "home_goals_suf_mean_l5", "away_goals_suf_mean_l5", "home_game_points_rank_mean", "home_game_points_rank_mean_l5", "away_game_points_rank_mean", "away_game_points_rank_mean_l5","is_friendly_0", "is_friendly_1"]
base = df.loc[:, columns]
base.loc[:, "goals_dif"] = base["home_goals_mean"] - base["away_goals_mean"]
base.loc[:, "goals_dif_l5"] = base["home_goals_mean_l5"] - base["away_goals_mean_l5"]
base.loc[:, "goals_suf_dif"] = base["home_goals_suf_mean"] - base["away_goals_suf_mean"]
base.loc[:, "goals_suf_dif_l5"] = base["home_goals_suf_mean_l5"] - base["away_goals_suf_mean_l5"]
base.loc[:, "goals_per_ranking_dif"] = (base["home_goals_mean"] / base["home_rank_mean"]) - (base["away_goals_mean"] / base["away_rank_mean"])
base.loc[:, "dif_rank_agst"] = base["home_rank_mean"] - base["away_rank_mean"]
base.loc[:, "dif_rank_agst_l5"] = base["home_rank_mean_l5"] - base["away_rank_mean_l5"]
base.loc[:, "dif_points_rank"] = base["home_game_points_rank_mean"] - base["away_game_points_rank_mean"]
base.loc[:, "dif_points_rank_l5"] = base["home_game_points_rank_mean_l5"] - base["away_game_points_rank_mean_l5"]
model_df = base[["home_team", "away_team", "target", "rank_dif", "goals_dif", "goals_dif_l5", "goals_suf_dif", "goals_suf_dif_l5", "goals_per_ranking_dif", "dif_rank_agst", "dif_rank_agst_l5", "dif_points_rank", "dif_points_rank_l5", "is_friendly_0", "is_friendly_1"]]
return model_df
model_db = create_db(df)
model_db
予測モデルを構築する
- 上記の手順により、予測能力のあるデータセットを取得したので、モデリングを開始できます
- このタスクでは、2 つのモデル (RFC、GBDT) を構築します。最後に、再現率が最も高いモデルが予測モデルとして選択されます。
- まず、機能列とラベル列を除外します
X = model_db.iloc[:, 3:]
y = model_db[["target"]]
- sklearn の統合学習で RFC と GBDT をインポートする
- 分割データセットとグリッド検索パッケージを sklearn にインポートする
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.model_selection import train_test_split, GridSearchCV
- ここでは、8:2 の比率を選択してデータ セットを分割し、ランダム シード番号を 1 に設定します。
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size= 0.2, random_state=1)
GBDT モデルの構築
- 最初に GBDT モデルを構築し、グリッド検索を使用してモデルを微調整します
gb = GradientBoostingClassifier(random_state=5)
params = {
"learning_rate": [0.01, 0.1, 0.5],
"min_samples_split": [5, 10],
"min_samples_leaf": [3, 5],
"max_depth":[3,5,10],
"max_features":["sqrt"],
"n_estimators":[100, 200]
}
gb_cv = GridSearchCV(gb, params, cv = 3, n_jobs = -1, verbose = False)
gb_cv.fit(X_train.values, np.ravel(y_train))
- GBDTのパラメータ構成を見てください
gb = gb_cv.best_estimator_
gb
RFC モデルを作成する
- 次に、RFC モデルを構築し、グリッド検索でモデルを微調整します
params_rf = {
"max_depth": [20],
"min_samples_split": [10],
"max_leaf_nodes": [175],
"min_samples_leaf": [5],
"n_estimators": [250],
"max_features": ["sqrt"],
}
rf = RandomForestClassifier(random_state=1)
rf_cv = GridSearchCV(rf, params_rf, cv = 3, n_jobs = -1, verbose = False)
rf_cv.fit(X_train.values, np.ravel(y_train))
rf = rf_cv.best_estimator_
モデル比較
ここでは、モデルの比較に混同行列と ROC 曲線を使用します。
def analyze(model):
fpr, tpr, _ = roc_curve(y_test, model.predict_proba(X_test.values)[:,1]) #test AUC
plt.figure(figsize=(15,10))
plt.plot([0, 1], [0, 1], 'k--')
plt.plot(fpr, tpr, label="test")
fpr_train, tpr_train, _ = roc_curve(y_train, model.predict_proba(X_train.values)[:,1]) #train AUC
plt.plot(fpr_train, tpr_train, label="train")
auc_test = roc_auc_score(y_test, model.predict_proba(X_test.values)[:,1])
auc_train = roc_auc_score(y_train, model.predict_proba(X_train.values)[:,1])
plt.legend()
plt.title('AUC score is %.2f on test and %.2f on training'%(auc_test, auc_train))
plt.show()
plt.figure(figsize=(15, 10))
cm = confusion_matrix(y_test, model.predict(X_test.values))
sns.heatmap(cm, annot=True, fmt="d")
- GBDT
analyze(gb)
analyze(rf)
分析の結果、ランダム フォレスト モデルの方がわずかに優れている可能性があることがわかりましたが、一般化能力は良くないようです。したがって、GBDT モデルを使用します。
ワールドカップシミュレーション
データの準備と前処理
- 最初に行ったのは、FIFAワールドカップのゲームを作成することでした
- これを行うには、ウィキペディアでチームとグループステージの試合を取得する必要があります
- 最初に pd.read_html を使用してデータをすばやくクロールします
from operator import itemgetter
dfs = pd.read_html(r"https://en.wikipedia.org/wiki/2022_FIFA_World_Cup#Teams")
- クロールされたフォームを前処理する
from collections.abc import Iterable
for i in range(len(dfs)):
df = dfs[i]
cols = list(df.columns.values)
if isinstance(cols[0], Iterable):
if any("Tie-breaking criteria" in c for c in cols):
start_pos = i+1
if any("Match 46" in c for c in cols):
end_pos = i+1
matches = []
groups = ["A", "B", "C", "D", "E", "F", "G", "H"]
group_count = 0
table = {
}
table[groups[group_count]] = [[a.split(" ")[0], 0, []] for a in list(dfs[start_pos].iloc[:, 1].values)]
for i in range(start_pos+1, end_pos, 1):
if len(dfs[i].columns) == 3:
team_1 = dfs[i].columns.values[0]
team_2 = dfs[i].columns.values[-1]
matches.append((groups[group_count], team_1, team_2))
else:
group_count+=1
table[groups[group_count]] = [[a, 0, []] for a in list(dfs[i].iloc[:, 1].values)]
table
上記では、グループステージでの各チームのポイントと、各ゲームに勝つ確率を保存しました。特に、2 つのチームが同じポイントを持っている場合、チームの勝率の平均が引き分けとして使用されます。
次に、各チームの参加チームのデータとして、前のゲームのデータを使用します。例えば、ブラジル対セルビアの場合、ブラジルのデータは前のゲームのデータであり、セルビアのデータも同じです。
def find_stats(team_1):
#team_1 = "Qatar"
past_games = team_stats_raw[(team_stats_raw["team"] == team_1)].sort_values("date")
last5 = team_stats_raw[(team_stats_raw["team"] == team_1)].sort_values("date").tail(5)
team_1_rank = past_games["rank"].values[-1]
team_1_goals = past_games.score.mean()
team_1_goals_l5 = last5.score.mean()
team_1_goals_suf = past_games.suf_score.mean()
team_1_goals_suf_l5 = last5.suf_score.mean()
team_1_rank_suf = past_games.rank_suf.mean()
team_1_rank_suf_l5 = last5.rank_suf.mean()
team_1_gp_rank = past_games.points_by_rank.mean()
team_1_gp_rank_l5 = last5.points_by_rank.mean()
return [team_1_rank, team_1_goals, team_1_goals_l5, team_1_goals_suf, team_1_goals_suf_l5, team_1_rank_suf, team_1_rank_suf_l5, team_1_gp_rank, team_1_gp_rank_l5]
def find_features(team_1, team_2):
rank_dif = team_1[0] - team_2[0]
goals_dif = team_1[1] - team_2[1]
goals_dif_l5 = team_1[2] - team_2[2]
goals_suf_dif = team_1[3] - team_2[3]
goals_suf_dif_l5 = team_1[4] - team_2[4]
goals_per_ranking_dif = (team_1[1]/team_1[5]) - (team_2[1]/team_2[5])
dif_rank_agst = team_1[5] - team_2[5]
dif_rank_agst_l5 = team_1[6] - team_2[6]
dif_gp_rank = team_1[7] - team_2[7]
dif_gp_rank_l5 = team_1[8] - team_2[8]
return [rank_dif, goals_dif, goals_dif_l5, goals_suf_dif, goals_suf_dif_l5, goals_per_ranking_dif, dif_rank_agst, dif_rank_agst_l5, dif_gp_rank, dif_gp_rank_l5, 1, 0]
正式にシミュレーションを開始します
これで、ワールド カップのシミュレーションを開始できます。
このモデルはバイナリ分類モデルであるため、チーム 1 が勝つかどうかのみを予測します。したがって、平均を判断するためのいくつかの基準を定義する必要があります。また、ワールドカップではホームフィールドアドバンテージがないため、チーム1とチーム2を入れ替えて2試合を予想し、確率平均が最も高いチームが勝者となるという考え方です。**グループステージにおいて、ホームチームがチーム 1 として勝ち、チーム 2 として負けた場合、またはホームチームがチーム 2 として勝ち、チーム 1 として負けた場合、試合は引き分けと見なされます。
advanced_group = []
last_group = ""
for k in table.keys():
for t in table[k]:
t[1] = 0
t[2] = []
for teams in matches:
draw = False
team_1 = find_stats(teams[1])
team_2 = find_stats(teams[2])
features_g1 = find_features(team_1, team_2)
features_g2 = find_features(team_2, team_1)
probs_g1 = gb.predict_proba([features_g1])
probs_g2 = gb.predict_proba([features_g2])
team_1_prob_g1 = probs_g1[0][0]
team_1_prob_g2 = probs_g2[0][1]
team_2_prob_g1 = probs_g1[0][1]
team_2_prob_g2 = probs_g2[0][0]
team_1_prob = (probs_g1[0][0] + probs_g2[0][1])/2
team_2_prob = (probs_g2[0][0] + probs_g1[0][1])/2
if ((team_1_prob_g1 > team_2_prob_g1) & (team_2_prob_g2 > team_1_prob_g2)) | ((team_1_prob_g1 < team_2_prob_g1) & (team_2_prob_g2 < team_1_prob_g2)):
draw=True
for i in table[teams[0]]:
if i[0] == teams[1] or i[0] == teams[2]:
i[1] += 1
elif team_1_prob > team_2_prob:
winner = teams[1]
winner_proba = team_1_prob
for i in table[teams[0]]:
if i[0] == teams[1]:
i[1] += 3
elif team_2_prob > team_1_prob:
winner = teams[2]
winner_proba = team_2_prob
for i in table[teams[0]]:
if i[0] == teams[2]:
i[1] += 3
for i in table[teams[0]]: #adding criterio de desempate (probs por jogo)
if i[0] == teams[1]:
i[2].append(team_1_prob)
if i[0] == teams[2]:
i[2].append(team_2_prob)
if last_group != teams[0]:
if last_group != "":
print("\n")
print("%s组 : "%(last_group))
for i in table[last_group]: #adding crieterio de desempate
i[2] = np.mean(i[2])
final_points = table[last_group]
final_table = sorted(final_points, key=itemgetter(1, 2), reverse = True)
advanced_group.append([final_table[0][0], final_table[1][0]])
for i in final_table:
print("%s -------- %d"%(i[0], i[1]))
print("\n")
print("-"*10+" %s组开始分析 "%(teams[0])+"-"*10)
if draw == False:
print(" %s组 - %s VS. %s: %s获胜 概率为 %.2f"%(teams[0], teams[1], teams[2], winner, winner_proba))
else:
print(" %s组 - %s vs. %s: 平局"%(teams[0], teams[1], teams[2]))
last_group = teams[0]
print("\n")
print(" %s组 : "%(last_group))
for i in table[last_group]: #adding crieterio de desempate
i[2] = np.mean(i[2])
final_points = table[last_group]
final_table = sorted(final_points, key=itemgetter(1, 2), reverse = True)
advanced_group.append([final_table[0][0], final_table[1][0]])
for i in final_table:
print("%s -------- %d"%(i[0], i[1]))
グループステージ予想
---------- グループ a が分析を開始 ----------
グループA - カタールvsエクアドル:エクアドルが勝利するオッズは0.60
グループ A - セネガル vs オランダ: オランダが勝つオッズは 0.59
グループA - カタールvsセネガル:セネガルの勝利オッズ0.58
グループ A - オランダ vs エクアドル: オランダが勝つオッズは 0.66
グループ A - エクアドル vs セネガル: エクアドルの勝利オッズ 0.53
グループA - オランダvsカタール:オランダが勝つオッズ0.69
グループ A:
オランダ -------- 9
エクアドル -------- 6
セネガル -------- 3
カタール -------- 0
---------- グループ B が分析を開始 ----------
グループ B イングランド vs イラン: イングランドの勝利オッズ 0.60
グループ B - アメリカ v ウェールズ: 引き分け
グループ B - ウェールズ vs イラン: ウェールズが勝つ確率 0.54
グループ B - イングランド vs アメリカ: イングランドが勝つ確率 0.58
グループ B - ウェールズ vs イングランド: イングランドが勝つ確率 0.60
グループ B - イラン vs 米国: 米国が勝つ確率 0.57
グループ B:
イングランド -------- 9
米国 -------- 4
ウェールズ -------- 4
イラン -------- 0
---------- グループ C が分析を開始 ----------
グループ C - アルゼンチン vs サウジアラビア: アルゼンチンが勝つ確率 0.70
グループ C - メキシコ v ポーランド: 引き分け
グループ C - ポーランド vs サウジアラビア: ポーランドが勝つ確率 0.64
グループ C - アルゼンチン vs メキシコ: アルゼンチンが勝つ確率 0.62
グループ C - ポーランド vs アルゼンチン: アルゼンチンが勝つ確率 0.64
グループ C - サウジアラビア対メキシコ: メキシコが勝つオッズは 0.64
グループ C:
アルゼンチン -------- 9
ポーランド-------- 4
メキシコ-------- 4
サウジアラビア -------- 0
---------- グループ d が分析を開始----------
グループ D - デンマーク vs チュニジア: デンマークが勝つ確率 0.63
グループ D - フランス vs オーストラリア: フランスが勝つ確率 0.65
グループ D - チュニジア v オーストラリア: 引き分け
グループ D - フランス v デンマーク: 引き分け
グループ D - オーストラリア vs デンマーク: デンマークが勝つ確率 0.65
グループ D - チュニジア vs フランス: フランスが勝つ確率 0.63
グループ D:
フランス -------- 7
デンマーク -------- 7
チュニジア -------- 1
オーストラリア-------- 1
---------- グループ e が分析を開始----------
グループ E - ドイツ vs 日本: ドイツが勝つ確率 0.59
グループ E - スペイン対コスタリカ: スペインが勝つ確率 0.68
グループ E - 日本 vs コスタリカ: 引き分け
グループ E - スペイン対ドイツ: 引き分け
グループ E - 日本 vs スペイン: スペインが勝つ確率 0.62
グループ E - コスタリカ対ドイツ: ドイツが勝つ確率 0.60
グループ E:
スペイン-------- 7
ドイツ -------- 7
日本-------- 1
コスタリカ -------- 1
---------- グループ f の分析開始----------
グループ F - モロッコ vs クロアチア: クロアチアが勝つ可能性は 0.58
グループ F - ベルギー vs カナダ: ベルギーが勝つ確率 0.67
グループ F - ベルギー vs モロッコ: ベルギーの勝利オッズ 0.63
グループ F - クロアチア vs カナダ: クロアチアが勝つ可能性は 0.62
グループ F - クロアチア vs ベルギー: ベルギーが勝つ確率 0.60
グループ F - カナダ v モロッコ: 引き分け
グループ F:
ベルギー -------- 9
クロアチア-------- 6
モロッコ-------- 1
カナダ-------- 1
---------- Gグループが分析を開始----------
グループG - スイスvsカメルーン:スイスが勝つオッズ0.62
グループ G - ブラジル vs セルビア: ブラジルが勝つ確率 0.63
グループ G - カメルーン vs セルビア: セルビアが勝つ確率 0.61
グループ G - ブラジル vs スイス: 引き分け
グループ G - セルビア vs スイス: スイスが勝つオッズは 0.56
グループ G - カメルーン vs ブラジル: ブラジルが勝つ可能性は 0.71
グループ G:
ブラジル-------- 7
スイス -------- 7
セルビア -------- 3
カメルーン -------- 0
---------- グループ h が分析を開始----------
グループH - ウルグアイvs韓国:ウルグアイが勝つオッズ0.60
グループ H - ポルトガル vs ガーナ: ポルトガルが勝つ確率 0.71
グループ H - 韓国 vs ガーナ: 韓国の勝利オッズ 0.69
グループ H - ポルトガル v ウルグアイ: 引き分け
グループ H - ガーナ vs ウルグアイ: 0.69 オッズでウルグアイが勝利
グループ H - 韓国 vs ポルトガル: ポルトガルが勝つ確率 0.63
グループ H:
ポルトガル -------- 7
ウルグアイ -------- 7
韓国 -------- 3
ガーナ -------- 0
プレーオフ予想
フランチャイズはブラジルとスイス、またはフランスとデンマークの間で引き分けであるため、グループステージの予測に驚くべきことはありません. プレーオフステージについては、樹形図で示します
advanced = advanced_group
playoffs = {
"第十六场比赛": [], "四分之一决赛": [], "半决赛": [], "决赛": []}
for p in playoffs.keys():
playoffs[p] = []
actual_round = ""
next_rounds = []
for p in playoffs.keys():
if p == "第十六场比赛":
control = []
for a in range(0, len(advanced*2), 1):
if a < len(advanced):
if a % 2 == 0:
control.append((advanced*2)[a][0])
else:
control.append((advanced*2)[a][1])
else:
if a % 2 == 0:
control.append((advanced*2)[a][1])
else:
control.append((advanced*2)[a][0])
playoffs[p] = [[control[c], control[c+1]] for c in range(0, len(control)-1, 1) if c%2 == 0]
for i in range(0, len(playoffs[p]), 1):
game = playoffs[p][i]
home = game[0]
away = game[1]
team_1 = find_stats(home)
team_2 = find_stats(away)
features_g1 = find_features(team_1, team_2)
features_g2 = find_features(team_2, team_1)
probs_g1 = gb.predict_proba([features_g1])
probs_g2 = gb.predict_proba([features_g2])
team_1_prob = (probs_g1[0][0] + probs_g2[0][1])/2
team_2_prob = (probs_g2[0][0] + probs_g1[0][1])/2
if actual_round != p:
print("-"*10)
print("开始模拟 %s"%(p))
print("-"*10)
print("\n")
if team_1_prob < team_2_prob:
print("%s VS. %s: %s 晋级 概率为 %.2f"%(home, away, away, team_2_prob))
next_rounds.append(away)
else:
print("%s VS. %s: %s 晋级 概率为 %.2f"%(home, away, home, team_1_prob))
next_rounds.append(home)
game.append([team_1_prob, team_2_prob])
playoffs[p][i] = game
actual_round = p
else:
playoffs[p] = [[next_rounds[c], next_rounds[c+1]] for c in range(0, len(next_rounds)-1, 1) if c%2 == 0]
next_rounds = []
for i in range(0, len(playoffs[p])):
game = playoffs[p][i]
home = game[0]
away = game[1]
team_1 = find_stats(home)
team_2 = find_stats(away)
features_g1 = find_features(team_1, team_2)
features_g2 = find_features(team_2, team_1)
probs_g1 = gb.predict_proba([features_g1])
probs_g2 = gb.predict_proba([features_g2])
team_1_prob = (probs_g1[0][0] + probs_g2[0][1])/2
team_2_prob = (probs_g2[0][0] + probs_g1[0][1])/2
if actual_round != p:
print("-"*10)
print("开始模拟 %s"%(p))
print("-"*10)
print("\n")
if team_1_prob < team_2_prob:
print("%s VS. %s: %s 晋级 概率为 %.2f"%(home, away, away, team_2_prob))
next_rounds.append(away)
else:
print("%s VS. %s: %s 晋级 概率为 %.2f"%(home, away, home, team_1_prob))
next_rounds.append(home)
game.append([team_1_prob, team_2_prob])
playoffs[p][i] = game
actual_round = p
第 16 試合のシミュレーションを開始---------
オランダ vs アメリカ: オランダが前進する確率は 0.55
アルゼンチン vs デンマーク: アルゼンチンの昇格確率は 0.59
スペイン vs クロアチア: スペインが予選を通過する可能性は 0.57 です
ブラジル vs ウルグアイ: ブラジルが予選を通過する確率は 0.60
エクアドル対イングランド: 0.65 イングランドの予選のチャンス
ポーランド vs フランス: フランスが予選を通過する確率は 0.60
ドイツ対ベルギー:ベルギーの昇格確率は0.50
スイス対ポルトガル: ポルトガルが前進する確率は 0.52
準々決勝のシミュレーションを開始 ---------
オランダ vs アルゼンチン: オランダが進出する確率は 0.52
スペイン vs ブラジル: ブラジルが予選を通過する確率は 0.51
イングランド vs フランス: フランスが予選を通過する確率は 0.51
ベルギー対ポルトガル: ポルトガルが前進する確率は 0.52
準決勝のシミュレーションを開始 ---------
オランダ vs ブラジル: ブラジルが進出する確率は 0.52
フランス対ポルトガル: ポルトガルの昇格確率は 0.52
模擬決勝スタート ---------
ブラジル vs ポルトガル: ブラジルが前進する確率は 0.52 です
- その樹形図を描く
!pip install pydot pydot-ng graphviz
import networkx as nx
from networkx.drawing.nx_pydot import graphviz_layout
plt.figure(figsize=(15, 10))
G = nx.balanced_tree(2, 3)
labels = []
for p in playoffs.keys():
for game in playoffs[p]:
label = f"{
game[0]}({
round(game[2][0], 2)}) \n {
game[1]}({
round(game[2][1], 2)})"
labels.append(label)
labels_dict = {
}
labels_rev = list(reversed(labels))
for l in range(len(list(G.nodes))):
labels_dict[l] = labels_rev[l]
pos = graphviz_layout(G, prog='twopi')
labels_pos = {
n: (k[0], k[1]-0.08*k[1]) for n,k in pos.items()}
center = pd.DataFrame(pos).mean(axis=1).mean()
nx.draw(G, pos = pos, with_labels=False, node_color=range(15), edge_color="#bbf5bb", width=10, font_weight='bold',cmap=plt.cm.Greens, node_size=5000)
nx.draw_networkx_labels(G, pos = labels_pos, bbox=dict(boxstyle="round,pad=0.3", fc="white", ec="black", lw=.5, alpha=1),
labels=labels_dict)
texts = ["Round \nof 16", "Quarter \n Final", "Semi \n Final", "Final\n"]
pos_y = pos[0][1] + 55
for text in reversed(texts):
pos_x = center
pos_y -= 75
plt.text(pos_y, pos_x, text, fontsize = 18)
plt.axis('equal')
plt.show()
要約する
- ゲームの進行に伴いデータベースが変更されるため、予測はいつでも変更される可能性があります。最新の結果を知りたい場合は、最新のデータを入れてください。
- 予測結果は必ずしも正確とは限りません。あくまでもイメージです。主な目的は学習です。このプロジェクトでは、データの前処理と特徴量エンジニアリングの技術を学習できます。
- このプロジェクトのモデリングは比較的ラフです. グリッド検索で交差検証された勾配ブースティング木は 1 つしかないため、結果は正確ではありません. もちろん、ブラジルが最終的に勝つことも願っています.
- 2022.11.25正午の時点で、ワールドカップは合計16試合が行われ、そのうち11試合が正しく予測されています。ウルグアイと韓国、モロッコとクロアチア、デンマークとチュニジアの引き分けと同様に、アルゼンチンと日本の間の番狂わせは正しく予測されませんでした。
- この予測結果を使用して、さまざまな賭けに参加しないでください。学習の参照のみを目的としています。
最適化の方向
- 新しい王冠の流行、最近のプレーヤーの状態など、予測に適した機能をさらに追加してみてください。
- 機能エンジニアリングにより多くの時間を費やすことができます
- 最後に、この記事のモデルは比較的ずさんなモデルですが、より有利な機械学習モデルを使用してみることができます。
コードのダウンロード
ベースライン:
https://www.kaggle.com/code/sslp23/predicting-fifa-2022-world-cup-with-ml/notebook
随時最適化および更新されるバージョン:
https://aistudio.baidu.com/aistudio/projectdetail/5116425?contributionType=1&sUid=2553954&shared=1&ts=1669358827040