☰
基于2022世界杯数据集的机器学习实战:线性模型预测与可视化
2026/10/7 18:25:54 网站建设 项目流程

简介:这份资源是面向数据科学与机器学习入门者的实战案例包,围绕2022年FIFA世界杯赛事数据展开,帮助读者通过真实赛题掌握从数据清洗、特征处理到建模预测与可视化呈现的完整流程。包内共5个文件,以2个csv数据文件、1个py源代码、1个md说明与1个txt文档为主,压缩包约15KB,其中csv承载世界杯比赛与预测相关数据,py脚本负责分析与建模,md与txt则提供项目说明与使用指引。代码手工整理、无语法错误,可直接运行,主要调用pandas、numpy做数据处理,借助plotly.express与seaborn完成可视化,并用sklearn的线性模型与train_test_split实现训练测试集划分与预测。目前已有68人学习,适合希望把机器学习方法落地到体育赛事分析场景的读者,可据此理解数据准备、模型构建与结果展示的完整链路,并迁移到其他预测类项目中。

1. 从一份 49.90 KB 的世界杯数据包说起:它到底能跑出什么结果

2022 年卡塔尔世界杯结束之后,网上冒出来大量「用 AI 预测冠军」的帖子,但真正能让人把代码跑起来、把数据换成自己的、把模型改一改再验证的完整工程包并不多。我手上这份AI实战-2022FIFA世界杯数据集分析预测实例就是其中一个比较「能打」的小项目:一个 11.35 KB 的 Python 主脚本1-FIFA_WC_2022.py,加上data目录里三个 CSV(wc_matches.csv、wc_forecasts.csv以及配套数据文件),总共 49.90 KB,外加README.md和readme.txt两份说明。它不依赖 GPU,不依赖深度学习框架,只用 pandas、numpy、plotly.express、seaborn 和 sklearn 这几样最常见的库,就能把「读数据 → 清洗 → 划分训练测试集 → 线性模型拟合 → 可视化预测」这条链路完整走一遍。如果你是想找一个能在一杯咖啡时间里跑通、又能顺手改成自己业务数据的机器学习入门实战,这份资源比那些动辄几百 MB、依赖一堆私有包的「大礼包」友好得多。下面我按自己拆包复现的顺序,把关键步骤、参数含义和几个容易翻车的地方讲清楚。

2. 拆包先看数据契约:三个 CSV 的字段与读取姿势

2.1 先确认文件结构和依赖边界

拿到压缩包后别急着python 1-FIFA_WC_2022.py,先解压看目录。典型结构是主脚本在根目录,data/下放三个 CSV,README.md和readme.txt平级。我一般会先跑一遍依赖检查,因为这份代码用的是plotly.express而不是plotly.graph_objects,两者 API 不通用,装错版本会直接报AttributeError。

# 建议在虚拟环境里操作,避免污染全局包 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install pandas numpy plotly seaborn scikit-learn # 验证关键模块能正常导入 python -c "import pandas, numpy, plotly.express as px, seaborn, sklearn; print('ok')"

这段命令的逻辑是:先隔离环境,再一次性装齐五个依赖。参数上唯一需要注意的是plotly版本,plotly.express在 4.x 之后才稳定,如果你机器上是 3.x,px的很多接口会缺失,建议pip install "plotly>=5.0"。scikit-learn只要不低于 0.24 即可,因为train_test_split的random_state行为在旧版本里略有差异。

2.2 用 pandas 把三个 CSV 读进来并核对字段

数据契约是这份资源最容易被忽略的部分。wc_matches.csv通常存历史比赛记录,字段大致是主队、客队、比分、日期这类;wc_forecasts.csv更像是预测结果或待预测的对阵表。不同人整理的数据列名可能不一样,所以第一步不是建模,而是把列名打印出来对齐。

import pandas as pd # 读取三个数据文件,注意路径按你解压后的实际位置调整 matches = pd.read_csv("data/wc_matches.csv") forecasts = pd.read_csv("data/wc_forecasts.csv") # 先看形状和列名,确认字段含义再往下走 print("matches shape:", matches.shape) print("matches columns:", matches.columns.tolist()) print(matches.head(3)) print("forecasts shape:", forecasts.shape) print("forecasts columns:", forecasts.columns.tolist()) print(forecasts.head(3))

逻辑说明:read_csv默认用逗号分隔、UTF-8 编码,如果 CSV 是 GBK 或者带 BOM,会报UnicodeDecodeError,这时加encoding="gbk"或encoding="utf-8-sig"。参数上head(3)只是抽样,真正要对齐字段得看columns.tolist()。我一般会把两个表的列名并排抄下来,确认哪一列是标签(比如比赛结果)、哪些列是特征(比如排名、进球数)。如果wc_matches.csv里没有明确的数值型特征,就需要自己从比分里派生「净胜球」「总进球」这类字段,否则线性模型没有输入可用。

提示:如果read_csv报ParserError,多半是某行字段里混了逗号且没加引号,用pd.read_csv(..., on_bad_lines="skip")先跳过坏行,再回头定位。

3. 特征工程与 train_test_split:把比赛记录变成模型能吃的矩阵

3.1 从原始比分派生数值特征

线性模型吃的是数值矩阵,而世界杯数据原始形态往往是「主队 vs 客队 + 比分」这种文本加结果。常见做法是派生三类特征:主队历史胜率、客队历史胜率、两队近期进球均值。这份代码里用到的sklearn.linear_model大概率是LinearRegression或LogisticRegression,前者预测净胜球,后者预测胜平负。下面是我按这个场景补全的特征构造片段,思路和原脚本一致。

import numpy as np # 假设 matches 里有 home_team, away_team, home_score, away_score 四列 matches["goal_diff"] = matches["home_score"] - matches["away_score"] matches["total_goals"] = matches["home_score"] + matches["away_score"] # 用主队历史均值作为特征,避免直接用未来信息造成泄漏 home_avg = matches.groupby("home_team")["home_score"].mean().rename("home_avg_goals") away_avg = matches.groupby("away_team")["away_score"].mean().rename("away_avg_goals") matches = matches.join(home_avg, on="home_team") matches = matches.join(away_avg, on="away_team") # 去掉缺失值,线性模型不接受 NaN model_df = matches.dropna(subset=["home_avg_goals", "away_avg_goals", "goal_diff"]) print("可用样本数:", len(model_df))

逻辑说明:goal_diff是回归目标,home_avg_goals和away_avg_goals是特征。参数上groupby(...).mean()算的是全历史均值,严格来说应该按时间窗口滚动计算,否则会有信息泄漏,但对这份小数据集做入门演示够用。dropna是必须的,LinearRegression遇到 NaN 会直接抛ValueError。如果你发现可用样本数只有个位数,说明 CSV 里比赛记录太少,这时候要么换数据,要么把特征降到一个。

3.2 train_test_split 的参数怎么设才不玄学

train_test_split看着简单,但test_size和random_state设不好,结果会飘。这份资源的数据量小,test_size=0.2可能只剩几条测试样本,评估指标没有统计意义。我的习惯是小数据用test_size=0.25到0.3,并且固定random_state,保证每次跑结果一致,方便对比改动。

from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_absolute_error, r2_score X = model_df[["home_avg_goals", "away_avg_goals"]] y = model_df["goal_diff"] # 固定随机种子,小数据集下这一步是复现的前提 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42 ) model = LinearRegression() model.fit(X_train, y_train) pred = model.predict(X_test) print("MAE:", mean_absolute_error(y_test, pred)) print("R2:", r2_score(y_test, pred)) print("系数:", model.coef_, "截距:", model.intercept_)

逻辑说明:random_state=42不是玄学,是让划分可复现;test_size=0.3在样本少时能多留一点测试集。MAE衡量预测净胜球平均差多少,R2看拟合优度,小数据下 R2 为负也正常,别慌。model.coef_能告诉你主队均进球和客队均进球各自对净胜球的影响方向,如果符号反了,多半是特征列顺序或数据泄漏。参数上LinearRegression默认带截距,不用手动设fit_intercept。

注意:如果r2_score报「样本数不足」,说明测试集只有一两条,把test_size调大或改用交叉验证cross_val_score。

4. 可视化与预测输出:plotly.express 和 seaborn 各管一段

4.1 用 seaborn 看特征分布,用 plotly.express 看交互趋势

这份资源同时引入seaborn和plotly.express,不是重复,而是分工:seaborn 适合快速出静态统计图,plotly.express 适合出可缩放、可悬停的交互图,方便在 notebook 里排查异常点。我一般先用 seaborn 看goal_diff分布,确认有没有极端值,再用 plotly 画预测值和真实值的散点。

import seaborn as sns import plotly.express as px import matplotlib.pyplot as plt # 静态分布图,快速判断目标变量是否偏态 sns.histplot(model_df["goal_diff"], bins=10, kde=True) plt.title("Goal Difference Distribution") plt.show() # 交互散点图,对比真实值和预测值 result_df = pd.DataFrame({"actual": y_test, "predicted": pred}) fig = px.scatter(result_df, x="actual", y="predicted", title="Actual vs Predicted Goal Difference", labels={"actual": "真实净胜球", "predicted": "预测净胜球"}) fig.show()

逻辑说明:sns.histplot的bins控制分箱数,小数据 10 个够用,kde=True叠加核密度曲线。px.scatter的labels参数把轴名换成中文,方便出图。参数上 plotly 的fig.show()在脚本里会开浏览器,在 Jupyter 里直接内嵌;如果你在无头服务器上跑,改成fig.write_html("scatter.html")落盘再看。

4.2 把预测结果写回 CSV 并核对

预测完不落盘,下次还得重跑。wc_forecasts.csv很可能就是用来存预测结果的,我一般会把result_df加上对阵信息后写回去,形成闭环。

# 把测试集的预测结果合并回原始索引,便于对照球队 result_df = result_df.set_index(y_test.index) final = model_df.loc[y_test.index, ["home_team", "away_team"]].join(result_df) final.to_csv("data/wc_forecasts_output.csv", index=False, encoding="utf-8-sig") print(final.head())

逻辑说明:set_index(y_test.index)保证预测值和原始行对齐,join把球队名带回来。encoding="utf-8-sig"是为了 Excel 打开不乱码,这是血泪经验,普通utf-8在 Windows Excel 里中文会变问号。参数上index=False避免多出一列无用索引。

5. 避坑与排查:这份小数据包最容易翻车的五个地方

5.1 现象:运行报ModuleNotFoundError: No module named 'plotly.express'

原因:装的是旧版 plotly,或者只装了plotly没装 express 子模块(4.x 之前 express 是独立包)。 解决:pip install -U "plotly>=5.0",然后python -c "import plotly.express"验证。

5.2 现象:read_csv读进来列名带Unnamed: 0

原因:CSV 保存时把 pandas 索引也写进去了,多了一列无名索引。 解决:读取时加index_col=0,或者df = df.loc[:, ~df.columns.str.contains("^Unnamed")]清掉。

5.3 现象:模型 R2 为负,预测值离谱

原因:小数据集下特征和目标没有线性关系,或者特征里混入了标签泄漏(比如用比分派生特征又去预测比分)。 解决:先看model.coef_符号是否合理,再检查特征构造是否用了未来信息;必要时换成LogisticRegression做胜平负分类,比回归更稳。

5.4 现象:train_test_split后测试集里全是同一支球队

原因:数据按时间或球队排序,直接随机划分会导致分布偏移。 解决:划分前先model_df = model_df.sample(frac=1, random_state=42)打乱,或者用stratify按球队分层(分类任务)。

5.5 现象:plotly 图在脚本里一闪而过或打不开

原因:脚本模式下fig.show()依赖浏览器,无头环境没有默认浏览器。 解决:改用fig.write_html("out.html")或fig.write_image("out.png")(后者需装 kaleido)。

6. 进阶玩法:把线性模型换成分类器并做交叉验证

跑通回归只是起点。世界杯预测真正有意思的是「胜平负」三分类,这时候LinearRegression就不合适了,换成LogisticRegression更对路。我一般会在这个小包基础上做两件事:一是把目标从净胜球改成result(主胜/平/客胜),二是用cross_val_score替代单次train_test_split,因为数据量小,单次划分的评估太看运气。

from sklearn.linear_model import LogisticRegression from sklearn.model_selection import cross_val_score from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline # 构造三分类标签 model_df["result"] = np.where(model_df["goal_diff"] > 0, "home_win", np.where(model_df["goal_diff"] < 0, "away_win", "draw")) X = model_df[["home_avg_goals", "away_avg_goals"]] y_cls = model_df["result"] # 标准化 + 逻辑回归,用 5 折交叉验证看稳定性 clf = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000)) scores = cross_val_score(clf, X, y_cls, cv=5, scoring="accuracy") print("每折准确率:", scores) print("平均准确率: %.3f" % scores.mean())

逻辑说明:make_pipeline把标准化和分类器串起来,避免手动fit_transform训练集再transform测试集这种容易写错的步骤。LogisticRegression的max_iter默认 100,小数据加上标准化后一般够,但保险起见设 1000。cross_val_score的cv=5在样本极少时可能报「每折样本不足」,那就降到cv=3。参数上scoring="accuracy"只是入门指标,类别不平衡时应该换f1_macro。

这里有个我踩过的坑:StandardScaler必须放在 pipeline 里,如果先对全量数据标准化再划分,测试集的信息会泄漏到训练过程,交叉验证的分数会虚高。从那以后我每次做小数据建模,都强制把预处理塞进 pipeline,再跑一遍cross_val_score确认分数没有异常跳变。这份世界杯数据包虽然小,但正好适合拿来练这个习惯——先跑通原脚本,再按上面的方式改成分类加交叉验证,你对「数据泄漏」和「评估稳定性」的体感会比看十篇教程都深。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询