足球是世界上最火爆的运动之一,如何运用机器学习来预测足球比赛结果,是每一个足球爱好者所向往的!
本场 Chat 适合有 Python 基础的机器学习初学者,我们带你一起熟悉机器学习的开发流程,帮你快速建立起自己的英超比赛预测模型!
你将获取到如下内容:
- 人工智能在线建模平台介绍;
- 熟悉机器学习开发流程;
- 熟悉 Python 数据挖掘库 NumPy、Pandas、Scikit-Learn 等开发库的基本使用;
- 熟悉常见分类算法(逻辑回归、SVM 和 XGBoost)及其评价指标;
- 部署完成一个属于自己的预测模型。
足球是世界上最火爆的运动之一,世界杯期间也往往是球迷们最亢奋的时刻。比赛狂欢季除了炸出了熬夜看球的铁杆粉丝,也让足球竞猜也成了大家茶余饭后最热衷的话题。甚至连原来不怎么看足球的人,也是暗中努力恶补了很多足球相关知识,想通过赛事竞猜先赚一个小目标。今天我们将介绍如何用机器学习来预测足球比赛结果!
本 Chat 采用 Python 编程语言,使用 人工智能建模平台 Mo 作为在线开发环境进行编程,通过获取 2000 年到 2018 年共 19 年英超的比赛数据,然后基于监督学习中逻辑回归模型、支持向量机模型和 XGBoost 模型,对英超比赛结果进行预测。
下面我们一起来看看预测英超比赛结果的机器学习步骤:
主要流程步骤
- 获取数据和读取数据的信息
- 数据清洗和预处理
- 特征工程
- 建立机器学习模型并进行预测
- 总结与展望
1. 获取数据和读取数据的信息
首先我们进入 Mo 工作台,创建一个空白项目,点击 开始开发 ,进入内嵌 JupyterLab 的 Notebook 开发环境。
接着我们需要在项目中上传数据集。
英超每年举办一个赛季,在每年的 8 月到第二年的 5 月进行,共有 20 支球队,实行主客场双循环赛制,每个赛季共 38 轮比赛(其中 19 场主场比赛,19 场客场比赛),每轮比赛共计 10 场比赛,所以每个赛季,英超共有 380 场比赛。
数据集地址
数据集中特征说明文档
如果您已经在 MO 平台新建项目,可以在平台直接导入数据集,流程如下:
1.1 读取 csv 数据接口解释
- 采用 Pandas 读取、写入数据 API 汇总网址读取 csv 数据一般采用 pandas.readcsv():pandas.readcsv(filepathorbuffer, sep =',' , delimiter = None)
- filepathorbuffer:文件路径
- sep:指定分隔符,默认是逗号
- delimiter:定界符,备选分隔符(如果指定改参数,则sep失效)
- usecols: 指定读取的列名,列表形式
# 导入必须的包import warningswarnings.filterwarnings('ignore') # 防止警告文件的包import pandas as pd # 数据分析包import osimport matplotlib.pyplot as plt # 可视化包import matplotlib%matplotlib inlineimport seaborn as sns # 可视化包from time import timefrom sklearn.preprocessing import scale # 标准化操作from sklearn.model_selection import train_test_split # 将数据集分成测试集和训练集from sklearn.metrics import f1_score # F1得分import xgboost as xgb # XGBoost模型from sklearn.svm import SVC ## 支持向量机分类模型from sklearn.linear_model import LogisticRegression # 逻辑回归模型from sklearn.model_selection import GridSearchCV # 超参数调参模块from sklearn.metrics import make_scorer # 模型评估import joblib # 模型的保存与加载模块
下面开始我们的表演:
# 获取地址中的所有文件loc = './/football//' # 存放数据的路径res_name = [] # 存放数据名的列表filecsv_list = [] # 获取数据名后存放的列表def file_name(file_name): # root:当前目录路径 dirs:当前目录下所有子目录 files:当前路径下所有非目录文件 for root,dirs,files in os.walk(file_name): files.sort() # 排序,让列表里面的元素有顺序 for i,file in enumerate(files): if os.path.splitext(file)[1] == '.csv': filecsv_list.append(file) res_name.append('raw_data_'+str(i+1)) print(res_name) print(filecsv_list)file_name(loc)
['raw_data_1', 'raw_data_2', 'raw_data_3', 'raw_data_4', 'raw_data_5', 'raw_data_6', 'raw_data_7', 'raw_data_8', 'raw_data_9', 'raw_data_10', 'raw_data_11', 'raw_data_12', 'raw_data_13', 'raw_data_14', 'raw_data_15', 'raw_data_16', 'raw_data_17', 'raw_data_18', 'raw_data_19']['2000-01.csv', '2001-02.csv', '2002-03.csv', '2003-04.csv', '2004-05.csv', '2005-06.csv', '2006-07.csv', '2007-08.csv', '2008-09.csv', '2009-10.csv', '2010-11.csv', '2011-12.csv', '2012-13.csv', '2013-14.csv', '2014-15.csv', '2015-16.csv', '2016-17.csv', '2017-18.csv', '2018-19.csv']
1.2 时间列表
获取每一年的数据后,将每一年的年份放入到 time_list 列表中:
time_list = [filecsv_list[i][0:4] for i in range(len(filecsv_list))]time_list
['2000','2001','2002','2003','2004','2005','2006','2007','2008','2009','2010','2011','2012','2013','2014','2015','2016','2017','2018']
1.3 用 Pandas.read_csv() 接口读取数据
读取时将数据与 res_name 中的元素名一一对应。
for i in range(len(res_name)): res_name[i] = pd.read_csv(loc+filecsv_list[i],error_bad_lines=False) print('第%2s个文件是%s,数据大小为%s'%(i+1,filecsv_list[i],res_name[i].shape))
第 1个文件是2000-01.csv,数据大小为(380, 45)第 2个文件是2001-02.csv,数据大小为(380, 48)第 3个文件是2002-03.csv,数据大小为(316, 48)第 4个文件是2003-04.csv,数据大小为(335, 57)第 5个文件是2004-05.csv,数据大小为(335, 57)第 6个文件是2005-06.csv,数据大小为(380, 68)第 7个文件是2006-07.csv,数据大小为(380, 68)第 8个文件是2007-08.csv,数据大小为(380, 71)第 9个文件是2008-09.csv,数据大小为(380, 71)第10个文件是2009-10.csv,数据大小为(380, 71)第11个文件是2010-11.csv,数据大小为(380, 71)第12个文件是2011-12.csv,数据大小为(380, 71)第13个文件是2012-13.csv,数据大小为(380, 74)第14个文件是2013-14.csv,数据大小为(380, 68)第15个文件是2014-15.csv,数据大小为(381, 68)第16个文件是2015-16.csv,数据大小为(380, 65)第17个文件是2016-17.csv,数据大小为(380, 65)第18个文件是2017-18.csv,数据大小为(380, 65)第19个文件是2018-19.csv,数据大小为(304, 62)
1.4 删除特定文件的空值
经过查看第 15 个文件读取的第 381 行为空值,故采取删除行空值操作。
1.4.1 删除空值的接口
- Pandas.dropna(axis=0,how='any')
- axis: 0 表示是行;1表示是列
- how:'all'表示只去掉所有值均缺失的行、列;any表示只去掉有缺失值的行、列
1.4.2 接口运用
res_name[14] = res_name[14].dropna(axis=0,how='all')res_name[14].tail()
Div | Date | HomeTeam | AwayTeam | FTHG | FTAG | FTR | HTHG | HTAG | HTR | ... | BbAv<2.5 | BbAH | BbAHh | BbMxAHH | BbAvAHH | BbMxAHA | BbAvAHA | PSCH | PSCD | PSCA | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
375 | E0 | 24/05/15 | Hull | Man United | 0.0 | 0.0 | D | 0.0 | 0.0 | D | ... | 1.99 | 25.0 | 0.50 | 1.76 | 1.71 | 2.27 | 2.19 | 3.20 | 3.76 | 2.27 |
376 | E0 | 24/05/15 | Leicester | QPR | 5.0 | 1.0 | H | 2.0 | 0.0 | H | ... | 2.41 | 28.0 | -1.00 | 1.98 | 1.93 | 1.98 | 1.93 | 1.53 | 4.94 | 6.13 |
377 | E0 | 24/05/15 | Man City | Southampton | 2.0 | 0.0 | H | 1.0 | 0.0 | H | ... | 2.66 | 28.0 | -1.00 | 2.00 | 1.94 | 2.03 | 1.93 | 1.60 | 4.35 | 6.00 |
378 | E0 | 24/05/15 | Newcastle | West Ham | 2.0 | 0.0 | H | 0.0 | 0.0 | D | ... | 2.25 | 25.0 | -0.50 | 1.82 | 1.78 | 2.20 | 2.10 | 1.76 | 4.01 | 4.98 |
379 | E0 | 24/05/15 | Stoke | Liverpool | 6.0 | 1.0 | H | 5.0 | 0.0 | H | ... | 1.99 | 25.0 | 0.25 | 2.07 | 2.02 | 1.88 | 1.85 | 3.56 | 3.60 | 2.17 |
5 rows × 68 columns
1.5 删除行数不是 380 的文件名
考虑到英超一般是 19 个球队,每个球队需要打 20 场球,故把行数不是 380 的数据删除掉,并找到器原 CSV 文件一一对应。
for i in range(len(res_name),0,-1): # 采用从大到小的遍历方式,然后进行删除不满足条件的。 if res_name[i-1].shape[0] != 380: key = 'res_name[' + str(i) + ']' print('删除的数据是:%s年的数据,文件名:%s大小是:%s'%(time_list[i-1],key,res_name[i-1].shape)) res_name.pop(i-1) time_list.pop(i-1) continue
删除的数据是:2018年的数据,文件名:res_name[19]大小是:(304, 62)删除的数据是:2004年的数据,文件名:res_name[5]大小是:(335, 57)删除的数据是:2003年的数据,文件名:res_name[4]大小是:(335, 57)删除的数据是:2002年的数据,文件名:res_name[3]大小是:(316, 48)
1.6 查看某一个数据集前n行数据
- 文件名.head(n)n:默认是5,想获取多少行数据就填写数字值。
读取数据前五行操作:
res_name[0].head()
Div | Date | HomeTeam | AwayTeam | FTHG | FTAG | FTR | HTHG | HTAG | HTR | ... | IWA | LBH | LBD | LBA | SBH | SBD | SBA | WHH | WHD | WHA | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | E0 | 19/08/00 | Charlton | Man City | 4 | 0 | H | 2 | 0 | H | ... | 2.7 | 2.20 | 3.25 | 2.75 | 2.20 | 3.25 | 2.88 | 2.10 | 3.2 | 3.10 |
1 | E0 | 19/08/00 | Chelsea | West Ham | 4 | 2 | H | 1 | 0 | H | ... | 4.2 | 1.50 | 3.40 | 6.00 | 1.50 | 3.60 | 6.00 | 1.44 | 3.6 | 6.50 |
2 | E0 | 19/08/00 | Coventry | Middlesbrough | 1 | 3 | A | 1 | 1 | D | ... | 2.7 | 2.25 | 3.20 | 2.75 | 2.30 | 3.20 | 2.75 | 2.30 | 3.2 | 2.62 |
3 | E0 | 19/08/00 | Derby | Southampton | 2 | 2 | D | 1 | 2 | A | ... | 3.5 | 2.20 | 3.25 | 2.75 | 2.05 | 3.20 | 3.20 | 2.00 | 3.2 | 3.20 |
4 | E0 | 19/08/00 | Leeds | Everton | 2 | 0 | H | 2 | 0 | H | ... | 4.5 | 1.55 | 3.50 | 5.00 | 1.57 | 3.60 | 5.00 | 1.61 | 3.5 | 4.50 |
5 rows × 45 columns
读取数据前10行:
res_name[0].head(10)
Div | Date | HomeTeam | AwayTeam | FTHG | FTAG | FTR | HTHG | HTAG | HTR | ... | IWA | LBH | LBD | LBA | SBH | SBD | SBA | WHH | WHD | WHA | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | E0 | 19/08/00 | Charlton | Man City | 4 | 0 | H | 2 | 0 | H | ... | 2.7 | 2.20 | 3.25 | 2.75 | 2.20 | 3.25 | 2.88 | 2.10 | 3.20 | 3.10 |
1 | E0 | 19/08/00 | Chelsea | West Ham | 4 | 2 | H | 1 | 0 | H | ... | 4.2 | 1.50 | 3.40 | 6.00 | 1.50 | 3.60 | 6.00 | 1.44 | 3.60 | 6.50 |
2 | E0 | 19/08/00 | Coventry | Middlesbrough | 1 | 3 | A | 1 | 1 | D | ... | 2.7 | 2.25 | 3.20 | 2.75 | 2.30 | 3.20 | 2.75 | 2.30 | 3.20 | 2.62 |
3 | E0 |