博舍

采用 Python 机器学习预测足球比赛结果 预测足球比赛结果app

采用 Python 机器学习预测足球比赛结果

采用Python机器学习预测足球比赛结果import.发布于2019-06-2616:28/3786人阅读

摘要:采用机器学习预测足球比赛结果足球是世界上最火爆的运动之一,世界杯期间也往往是球迷们最亢奋的时刻。特征工程在机器学习中占有非常重要的作用,一般认为括特征构建特征提取特征选择三大部分。

采用Python机器学习预测足球比赛结果

足球是世界上最火爆的运动之一,世界杯期间也往往是球迷们最亢奋的时刻。比赛狂欢季除了炸出了熬夜看球的铁杆粉丝,也让足球竞猜也成了大家茶余饭后最热衷的话题。甚至连原来不怎么看足球的人,也是暗中努力恶补了很多足球相关知识,想通过赛事竞猜先赚一个小目标。今天我们将介绍如何用机器学习来预测足球比赛结果!

本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.read_csv():

pandas.read_csv(filepath_or_buffer,sep=",",delimiter=None)

filepath_or_buffer:文件路径

sep:指定分隔符,默认是逗号

delimiter:定界符,备选分隔符(如果指定改参数,则sep失效)

usecols:指定读取的列名,列表形式

#导入必须的包importwarningswarnings.filterwarnings("ignore")#防止警告文件的包importpandasaspd#数据分析包importosimportmatplotlib.pyplotasplt#可视化包importmatplotlib%matplotlibinlineimportseabornassns#可视化包fromtimeimporttimefromsklearn.preprocessingimportscale#标准化操作fromsklearn.model_selectionimporttrain_test_split#将数据集分成测试集和训练集fromsklearn.metricsimportf1_score#F1得分importxgboostasxgb#XGBoost模型fromsklearn.svmimportSVC##支持向量机分类模型fromsklearn.linear_modelimportLogisticRegression#逻辑回归模型fromsklearn.model_selectionimportGridSearchCV#超参数调参模块fromsklearn.metricsimportmake_scorer#模型评估importjoblib#模型的保存与加载模块

下面开始我们的表演:

#获取地址中的所有文件loc=".//football//"#存放数据的路径res_name=[]#存放数据名的列表filecsv_list=[]#获取数据名后存放的列表deffile_name(file_name):#root:当前目录路径dirs:当前目录下所有子目录files:当前路径下所有非目录文件forroot,dirs,filesinos.walk(file_name):files.sort()#排序,让列表里面的元素有顺序fori,fileinenumerate(files):ifos.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]foriinrange(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中的元素名一一对应。

foriinrange(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()DivDateHomeTeamAwayTeamFTHGFTAGFTRHTHGHTAGHTR...BbAvBbAHBbAHhBbMxAHHBbAvAHHBbMxAHABbAvAHAPSCHPSCDPSCA375E024/05/15HullManUnited0.00.0D0.00.0D...1.9925.00.501.761.712.272.193.203.762.27376E024/05/15LeicesterQPR5.01.0H2.00.0H...2.4128.0-1.001.981.931.981.931.534.946.13377E024/05/15ManCitySouthampton2.00.0H1.00.0H...2.6628.0-1.002.001.942.031.931.604.356.00378E024/05/15NewcastleWestHam2.00.0H0.00.0D...2.2525.0-0.501.821.782.202.101.764.014.98379E024/05/15StokeLiverpool6.01.0H5.00.0H...1.9925.00.252.072.021.881.853.563.602.17

5rows×68columns

1.5删除行数不是380的文件名

考虑到英超一般是19个球队,每个球队需要打20场球,故把行数不是380的数据删除掉,并找到器原CSV文件一一对应。

foriinrange(len(res_name),0,-1):#采用从大到小的遍历方式,然后进行删除不满足条件的。ifres_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()DivDateHomeTeamAwayTeamFTHGFTAGFTRHTHGHTAGHTR...IWALBHLBDLBASBHSBDSBAWHHWHDWHA0E019/08/00CharltonManCity40H20H...2.72.203.252.752.203.252.882.103.23.101E019/08/00ChelseaWestHam42H10H...4.21.503.406.001.503.606.001.443.66.502E019/08/00CoventryMiddlesbrough13A11D...2.72.253.202.752.303.202.752.303.22.623E019/08/00DerbySouthampton22D12A...3.52.203.252.752.053.203.202.003.23.204E019/08/00LeedsEverton20H20H...4.51.553.505.001.573.605.001.613.54.50

5rows×45columns

读取数据前10行:

res_name[0].head(10)DivDateHomeTeamAwayTeamFTHGFTAGFTRHTHGHTAGHTR...IWALBHLBDLBASBHSBDSBAWHHWHDWHA0E019/08/00CharltonManCity40H20H...2.72.203.252.752.203.252.882.103.203.101E019/08/00ChelseaWestHam42H10H...4.21.503.406.001.503.606.001.443.606.502E019/08/00CoventryMiddlesbrough13A11D...2.72.253.202.752.303.202.752.303.202.623E019/08/00DerbySouthampton22D12A...3.52.203.252.752.053.203.202.003.203.204E019/08/00LeedsEverton20H20H...4.51.553.505.001.573.605.001.613.504.505E019/08/00LeicesterAstonVilla00D00D...2.52.353.202.602.253.252.752.403.252.506E019/08/00LiverpoolBradford10H00D...8.01.354.008.001.364.008.001.334.008.007E019/08/00SunderlandArsenal10H00D...2.14.303.201.703.303.102.053.753.001.908E019/08/00TottenhamIpswich31H21H...4.71.453.606.501.503.506.501.443.606.509E020/08/00ManUnitedNewcastle20H10H...5.01.403.757.001.403.757.501.403.757.00

10rows×45columns读取最后5行操作:

res_name[0].tail()DivDateHomeTeamAwayTeamFTHGFTAGFTRHTHGHTAGHTR...IWALBHLBDLBASBHSBDSBAWHHWHDWHA375E019/05/01ManCityChelsea12A11D...1.654.03.601.674.203.401.704.003.11.80376E019/05/01MiddlesbroughWestHam21H21H...3.201.83.253.751.903.203.501.833.43.50377E019/05/01NewcastleAstonVilla30H20H...2.902.43.252.502.383.302.502.253.42.60378E019/05/01SouthamptonArsenal32H01A...2.352.53.252.372.633.252.302.623.52.20379E019/05/01TottenhamManUnited31H11D...2.102.63.202.372.603.252.352.623.32.25

5rows×45columns

读取最后4行操作:

res_name[0].tail(4)DivDateHomeTeamAwayTeamFTHGFTAGFTRHTHGHTAGHTR...IWALBHLBDLBASBHSBDSBAWHHWHDWHA376E019/05/01MiddlesbroughWestHam21H21H...3.201.83.253.751.903.203.501.833.43.50377E019/05/01NewcastleAstonVilla30H20H...2.902.43.252.502.383.302.502.253.42.60378E019/05/01SouthamptonArsenal32H01A...2.352.53.252.372.633.252.302.623.52.20379E019/05/01TottenhamManUnited31H11D...2.102.63.202.372.603.252.352.623.32.25

4rows×45columns

1.8获取某一年主场队伍的名称res_name[0]["HomeTeam"].unique()array(["Charlton","Chelsea","Coventry","Derby","Leeds","Leicester","Liverpool","Sunderland","Tottenham","ManUnited","Arsenal","Bradford","Ipswich","Middlesbrough","Everton","ManCity","Newcastle","Southampton","WestHam","AstonVilla"],dtype=object)1.9解析数据集列表头含义

数据集行数已经固定,一般都是380行,而列数可能每年统计指标有变化,不一定相等,而且我们也比较关心列数表表头。由于比较小,可以直接看数据集列数,这样比较快,也可以代码实现,找到最大的列数,然后获取列数的表头进行一般性介绍解释。

#获取列表头最大的列数,然后获取器参数shape_list=[res_name[i].shape[1]foriinrange(len(res_name))]foriinrange(len(res_name)):ifres_name[i].shape[1]==max(shape_list):print("%s年数据是有最大列数:%s,列元素表头:%s"%(time_list[i],max(shape_list),res_name[i].columns))2012年数据是有最大列数:74,列元素表头:Index(["Div","Date","HomeTeam","AwayTeam","FTHG","FTAG","FTR","HTHG","HTAG","HTR","Referee","HS","AS","HST","AST","HF","AF","HC","AC","HY","AY","HR","AR","B365H","B365D","B365A","BWH","BWD","BWA","GBH","GBD","GBA","IWH","IWD","IWA","LBH","LBD","LBA","PSH","PSD","PSA","WHH","WHD","WHA","SJH","SJD","SJA","VCH","VCD","VCA","BSH","BSD","BSA","Bb1X2","BbMxH","BbAvH","BbMxD","BbAvD","BbMxA","BbAvA","BbOU","BbMx>2.5","BbAv>2.5","BbMx

我们看到数据包括Date(比赛的时间),Hometeam(主场队伍名),Awayteam(客场队伍名),FTHG(主场球队全场进球数),HTHG(主场球队半场进球数),FTR(全场比赛结果)等等,更多关于数据集中特征信息可以参考数据集特征说明文档。

2.数据清洗和预处理

我们挑选Hometeam,Awayteam,FTHG,FTAG,FTR这五列数据,作为我们的原始的特征数据,后面基于这些原始特征,我们再构造一些新的特征。

2.1 挑选信息列

HomeTeam:主场球队名

AwayTeam:客场球队名

FTHG:全场主场球队进球数

FTAG: 全场客场球队进球数

FTR: 比赛结果(H=主场赢,D=平局,A=客场赢)

#将挑选的信息放在一个新的列表中columns_req=["HomeTeam","AwayTeam","FTHG","FTAG","FTR"]playing_statistics=[]#创造处理后数据名存放处playing_data={}#键值对存储数据foriinrange(len(res_name)):playing_statistics.append("playing_statistics_"+str(i+1))playing_statistics[i]=res_name[i][columns_req]print(time_list[i],"playing_statistics["+str(i)+"]",playing_statistics[i].shape)2000playing_statistics[0](380,5)2001playing_statistics[1](380,5)2005playing_statistics[2](380,5)2006playing_statistics[3](380,5)2007playing_statistics[4](380,5)2008playing_statistics[5](380,5)2009playing_statistics[6](380,5)2010playing_statistics[7](380,5)2011playing_statistics[8](380,5)2012playing_statistics[9](380,5)2013playing_statistics[10](380,5)2014playing_statistics[11](380,5)2015playing_statistics[12](380,5)2016playing_statistics[13](380,5)2017playing_statistics[14](380,5)2.2分析原始数据

我们首先预测所有主场球队全都胜利,然后预测所有的客场都会胜利,对结果进行对比分析:

2.2.1统计所有主场球队都会胜利的准确率defpredictions_0(data):"""当我们统计所有主场球队都赢,那么我们预测的结果是什么返回值是预测值和实际值"""predictions=[]for_,gameindata.iterrows():ifgame["FTR"]=="H":predictions.append(1)else:predictions.append(0)#返回预测结果returnpd.Series(predictions)#那我们对19年全部主场球队都赢的结果进行预测,获取预测的准确率。avg_acc_sum=0foriinrange(len(playing_statistics)):predictions=predictions_0(playing_statistics[i])acc=sum(predictions)/len(playing_statistics[i])avg_acc_sum+=accprint("%s年数据主场全胜预测的准确率是%s"%(time_list[i],acc))print("共%s年的平均准确率是:%s"%(len(playing_statistics),avg_acc_sum/len(playing_statistics)))2000年数据主场全胜预测的准确率是0.48421052631578952001年数据主场全胜预测的准确率是0.43421052631578952005年数据主场全胜预测的准确率是0.50526315789473692006年数据主场全胜预测的准确率是0.47894736842105262007年数据主场全胜预测的准确率是0.46315789473684212008年数据主场全胜预测的准确率是0.455263157894736872009年数据主场全胜预测的准确率是0.50789473684210532010年数据主场全胜预测的准确率是0.47105263157894742011年数据主场全胜预测的准确率是0.452012年数据主场全胜预测的准确率是0.43684210526315792013年数据主场全胜预测的准确率是0.47105263157894742014年数据主场全胜预测的准确率是0.452631578947368442015年数据主场全胜预测的准确率是0.41315789473684212016年数据主场全胜预测的准确率是0.49210526315789472017年数据主场全胜预测的准确率是0.45526315789473687共15年的平均准确率是:0.464736842105263172.2.2统计所有客场球队都会胜利的准确率defpredictions_1(data):"""当我们统计所有客场球队都赢,那么我们预测的结果是什么返回值是预测值和实际值"""predictions=[]for_,gameindata.iterrows():ifgame["FTR"]=="A":predictions.append(1)else:predictions.append(0)#返回预测结果returnpd.Series(predictions)#那我们对19年客场球队都赢的结果进行预测,获取预测的准确率。foriinrange(len(playing_statistics)):predictions=predictions_1(playing_statistics[i])acc=sum(predictions)/len(playing_statistics[i])print("%s年数据客场全胜预测的准确率是%s"%(time_list[i],acc))2000年数据客场全胜预测的准确率是0.252001年数据客场全胜预测的准确率是0.32005年数据客场全胜预测的准确率是0.292105263157894762006年数据客场全胜预测的准确率是0.26315789473684212007年数据客场全胜预测的准确率是0.27368421052631582008年数据客场全胜预测的准确率是0.28947368421052632009年数据客场全胜预测的准确率是0.23947368421052632010年数据客场全胜预测的准确率是0.236842105263157882011年数据客场全胜预测的准确率是0.305263157894736852012年数据客场全胜预测的准确率是0.27894736842105262013年数据客场全胜预测的准确率是0.32368421052631582014年数据客场全胜预测的准确率是0.30263157894736842015年数据客场全胜预测的准确率是0.305263157894736852016年数据客场全胜预测的准确率是0.28684210526315792017年数据客场全胜预测的准确率是0.28421052631578947

综上比较:我们可以看出主场胜利的概率相对于输和平局来说,确实概率要大。

2.3我们想知道Arsenal作为主场队伍时,他们的表现,如何求出2005-06所有比赛累计进球数?

我们知道2005-06年数据在playing_statistics[2]中:

defscore(data):"""Arsenal作为主场队伍时,累计进球数"""scores=[]for_,gameindata.iterrows():ifgame["HomeTeam"]=="Arsenal":scores.append(game["FTHG"])returnnp.sum(scores)Arsenal_score=score(playing_statistics[2])print("Arsenal作为主场队伍在2005年时,累计进球数:%s"%(Arsenal_score))Arsenal作为主场队伍在2005年时,累计进球数:482.4我们想知道各个球队作为主场队伍时,他们的表现如何?

先试试求2005-06所有比赛各个球队累计进球数。

print(playing_statistics[5].groupby("HomeTeam").sum()["FTHG"])HomeTeamArsenal31AstonVilla27Blackburn22Bolton21Chelsea33Everton31Fulham28Hull18Liverpool41ManCity40ManUnited43Middlesbrough17Newcastle24Portsmouth26Stoke22Sunderland21Tottenham21WestBrom26WestHam23Wigan17Name:FTHG,dtype:int643.特征工程

特征工程指的是把原始数据转变为模型的训练数据的过程,它的目的就是获取更好的训练数据特征,得到更好的训练模型。特征工程能使得模型的性能得到提升,有时甚至在简单的模型上也能取得不错的效果。特征工程在机器学习中占有非常重要的作用,一般认为括特征构建、特征提取、特征选择三大部分。

3.1构造特征

因为这个比赛是一年一个赛季,是有先后顺序的,那我们就可以统计到截止到本场比赛之前,整个赛季内,主客场队伍的净胜球的数量。那么对于每一个赛季的每一周,都统计出每个球队到本周为止累计的进球数和丢球数之差,也就是净胜球的数量。

3.1.1计算每个队周累计净胜球数量

处理后的数据,我们可以通过看某一年的某几条数据来体现,比如:05-06年的后五条数据

defget_goals_diff(playing_stat):#创建一个字典,每个team的name作为keyteams={}foriinplaying_stat.groupby("HomeTeam").mean().T.columns:teams[i]=[]#对于每一场比赛foriinrange(len(playing_stat)):#全场比赛,主场队伍的进球数HTGS=playing_stat.iloc[i]["FTHG"]#全场比赛,客场队伍的进球数ATGS=playing_stat.iloc[i]["FTAG"]#把主场队伍的净胜球数添加到team这个字典中对应的主场队伍下teams[playing_stat.iloc[i].HomeTeam].append(HTGS-ATGS)#把客场队伍的净胜球数添加到team这个字典中对应的客场队伍下teams[playing_stat.iloc[i].AwayTeam].append(ATGS-HTGS)#创建一个GoalsDifference的dataframe#行是team列是matchweek,#39解释:19个球队,每个球队分主场客场2次,共38个赛次,但是range取不到最后一个值,故38+1=39GoalsDifference=pd.DataFrame(data=teams,index=[iforiinrange(1,39)]).TGoalsDifference[0]=0#累加每个队的周比赛的净胜球数foriinrange(2,39):GoalsDifference[i]=GoalsDifference[i]+GoalsDifference[i-1]returnGoalsDifferencedefget_gss(playing_stat):#得到净胜球数统计GD=get_goals_diff(playing_stat)j=0#主客场的净胜球数HTGD=[]ATGD=[]#全年一共380场比赛foriinrange(380):ht=playing_stat.iloc[i].HomeTeamat=playing_stat.iloc[i].AwayTeamHTGD.append(GD.loc[ht][j])ATGD.append(GD.loc[at][j])if((i+1)%10)==0:j=j+1#把每个队的HTGDATGD信息补充到dataframe中playing_stat.loc[:,"HTGD"]=HTGDplaying_stat.loc[:,"ATGD"]=ATGDreturnplaying_statforiinrange(len(playing_statistics)):playing_statistics[i]=get_gss(playing_statistics[i])####查看构造特征后的05-06年的后五条数据playing_statistics[2].tail()HomeTeamAwayTeamFTHGFTAGFTRHTGDATGD375FulhamMiddlesbrough10H-11-9376ManUnitedCharlton40H34-10377NewcastleChelsea10H451378PortsmouthLiverpool13A-2330379WestHamTottenham21H-416

通过以上数据:我们发现376行数据的特点,截止到这一场比赛之前,本赛季主场曼联队的净胜球数是34,客场查尔顿队的净胜球数是-10。

3.1.2统计主客场队伍到当前比赛周的累计得分

统计整个赛季主客场队伍截止到当前比赛周的累计得分。一场比赛胜利计3分,平局计1分,输了计0分。我们根据本赛季本周之前的比赛结果来统计这个值。我们继续观看 05-06年的后五条数据:

#把比赛结果转换为得分,赢得三分,平局得一分,输不得分defget_points(result):ifresult=="W":return3elifresult=="D":return1else:return0defget_cuml_points(matchres):matchres_points=matchres.applymap(get_points)foriinrange(2,39):matchres_points[i]=matchres_points[i]+matchres_points[i-1]matchres_points.insert(column=0,loc=0,value=[0*iforiinrange(20)])returnmatchres_pointsdefget_matchres(playing_stat):#创建一个字典,每个team的name作为keyteams={}foriinplaying_stat.groupby("HomeTeam").mean().T.columns:teams[i]=[]#把比赛结果分别记录在主场队伍和客场队伍中#H:代表主场赢#A:代表客场赢#D:代表平局foriinrange(len(playing_stat)):ifplaying_stat.iloc[i].FTR=="H":#主场赢,则主场记为赢,客场记为输teams[playing_stat.iloc[i].HomeTeam].append("W")teams[playing_stat.iloc[i].AwayTeam].append("L")elifplaying_stat.iloc[i].FTR=="A":#客场赢,则主场记为输,客场记为赢teams[playing_stat.iloc[i].AwayTeam].append("W")teams[playing_stat.iloc[i].HomeTeam].append("L")else:#平局teams[playing_stat.iloc[i].AwayTeam].append("D")teams[playing_stat.iloc[i].HomeTeam].append("D")returnpd.DataFrame(data=teams,index=[iforiinrange(1,39)]).Tdefget_agg_points(playing_stat):matchres=get_matchres(playing_stat)cum_pts=get_cuml_points(matchres)HTP=[]ATP=[]j=0foriinrange(380):ht=playing_stat.iloc[i].HomeTeamat=playing_stat.iloc[i].AwayTeamHTP.append(cum_pts.loc[ht][j])ATP.append(cum_pts.loc[at][j])if((i+1)%10)==0:j=j+1#主场累计得分playing_stat.loc[:,"HTP"]=HTP#客场累计得分playing_stat.loc[:,"ATP"]=ATPreturnplaying_statforiinrange(len(playing_statistics)):playing_statistics[i]=get_agg_points(playing_statistics[i])#查看构造特征后的05-06年的后五条数据playing_statistics[2].tail()HomeTeamAwayTeamFTHGFTAGFTRHTGDATGDHTPATP375FulhamMiddlesbrough10H-11-94545376ManUnitedCharlton40H34-108047377NewcastleChelsea10H4515591378PortsmouthLiverpool13A-23303879379WestHamTottenham21H-4165265

我们处理得到HTP(本赛季主场球队截止到本周的累计得分),ATP(本赛季客场球队截止到本周的累计得分)。我们再看376行,截止到这一场比赛,本赛季,曼联队一共积了80分,查尔顿队积了47分。

3.1.3 统计某支队伍最近三场比赛的表现

前面我们构造的特征反映了一只队伍本赛季的历史总表现,我们看看队伍在最近三场比赛的表现。我们用:

HM1代表主场球队上一次比赛的输赢,

AM1代表客场球队上一次比赛是输赢。

同理,HM2AM2就是上上次比赛的输赢,HM3AM3就是上上上次比赛的输赢。

我们继续观看处理后05-06年的后5五条数据:

defget_form(playing_stat,num):form=get_matchres(playing_stat)form_final=form.copy()foriinrange(num,39):form_final[i]=""j=0whilejHomeTeamAwayTeamFTHGFTAGFTRHTGDATGDHTPATPHM1AM1HM2AM2HM3AM3375FulhamMiddlesbrough10H-11-94545LDWDWL376ManUnitedCharlton40H34-108047DLLLWW377NewcastleChelsea10H4515591DLWWWW378PortsmouthLiverpool13A-23303879WWWWLW379WestHamTottenham21H-4165265WWLDLL3.1.4加入比赛周特征(第几个比赛周)

然后我们把比赛周的信息也放在里面,也就是这一场比赛发生在第几个比赛周。特征构造后的结果,我们可以直接查看05-06年的后5条数据:

defget_mw(playing_stat):j=1MatchWeek=[]foriinrange(380):MatchWeek.append(j)if((i+1)%10)==0:j=j+1playing_stat["MW"]=MatchWeekreturnplaying_statforiinrange(len(playing_statistics)):playing_statistics[i]=get_mw(playing_statistics[i])#查看构造特征后的05-06年的后五条数据playing_statistics[2].tail()HomeTeamAwayTeamFTHGFTAGFTRHTGDATGDHTPATPHM1AM1HM2AM2HM3AM3MW375FulhamMiddlesbrough10H-11-94545LDWDWL38376ManUnitedCharlton40H34-108047DLLLWW38377NewcastleChelsea10H4515591DLWWWW38378PortsmouthLiverpool13A-23303879WWWWLW38379WestHamTottenham21H-4165265WWLDLL383.1.5合并比赛的信息

我们打算把数据集比赛的信息都合并到一个表里面,然后我们把我们刚才计算得到的这些得分数据,净胜球数据除以周数,就得到了周平均后的值。结果就可以通过查看构造特征后数据集的后5条数据。

#将各个DataFrame表合并在一张表中playing_stat=pd.concat(playing_statistics,ignore_index=True)#HTGD,ATGD,HTP,ATP的值除以week数,得到平均分cols=["HTGD","ATGD","HTP","ATP"]playing_stat.MW=playing_stat.MW.astype(float)forcolincols:playing_stat[col]=playing_stat[col]/playing_stat.MW#查看构造特征后数据集的后5五条数据playing_stat.tail()HomeTeamAwayTeamFTHGFTAGFTRHTGDATGDHTPATPHM1AM1HM2AM2HM3AM3MW5695NewcastleChelsea3.00.0H-0.2894740.7105261.0789471.842105LDLWLW38.05696SouthamptonManCity0.01.0A-0.4736842.0526320.9473682.552632WWDDWW38.05697SwanseaStoke1.02.0A-0.710526-0.8947370.8684210.789474LLLDLD38.05698TottenhamLeicester5.04.0H0.973684-0.0789471.9473681.236842WWLLWL38.05699WestHamEverton3.01.0H-0.578947-0.3157891.0263161.289474DDWWLW38.0

我们看到数据集最后一行的行数是5699,加上第一行为0行,则一共5700条数据;我们总共统计了15年的数据,每一年有380条数据,计算后发现我们统计后的数据集大小是准确的。

3.2删除某些数据

前面我们根据初始的特征构造出了很多的特征。这其中有一部分是中间的特征,我们需要把这些中间特征抛弃掉。因为前三周的比赛,每个队的历史胜负信息不足,所以我们打算弃掉前三周的数据。

#抛弃前三周的比赛playing_stat=playing_stat[playing_stat.MW>3]playing_stat.drop(["HomeTeam","AwayTeam","FTHG","FTAG","MW"],1,inplace=True)#我们查看下此时的数据的特征playing_stat.keys()Index(["FTR","HTGD","ATGD","HTP","ATP","HM1","AM1","HM2","AM2","HM3","AM3"],dtype="object")3.3分析我们构造的数据

在前面,我们计算了每一的年主客场的胜率,现在我们看看有效数据中,是主场胜利的多呢,还是客场胜利的多呢?

#比赛总数n_matches=playing_stat.shape[0]#特征数n_features=playing_stat.shape[1]-1#主场获胜的数目n_homewins=len(playing_stat[playing_stat.FTR=="H"])#主场获胜的比例win_rate=(float(n_homewins)/(n_matches))*100#Printtheresultsprint("比赛总数:{}".format(n_matches))print("总特征数:{}".format(n_features))print("主场胜利数:{}".format(n_homewins))print("主场胜率:{:.2f}%".format(win_rate))比赛总数:5250总特征数:10主场胜利数:2451主场胜率:46.69%

通过统计结果看到:我们主场胜率46.69%与我们第2.2.1小节原始数据分析的结果是一致的,说明我们前面构造的特征是有效的,比较贴近实际的。

3.4解决样本不均衡问题

通过构造特征之后,发现主场获胜的比例接近50%,所以对于这个三分类的问题,标签比例是不均衡的。

我们把它简化为二分类问题,也就是主场球队会不会胜利,这也是一种解决标签比例不均衡的问题的方法。

#定义target,也就是否主场赢defonly_hw(string):ifstring=="H":return"H"else:return"NH"playing_stat["FTR"]=playing_stat.FTR.apply(only_hw)3.5将数据分为特征值和标签值#把数据分为特征值和标签值X_all=playing_stat.drop(["FTR"],1)y_all=playing_stat["FTR"]#特征值的长度len(X_all)52503.6数据归一化、标准化

我们对所有比赛的特征HTP进行最大最小值归一化。

defconvert_1(data):max=data.max()min=data.min()return(data-min)/(max-min)r_data=convert_1(X_all["HTGD"])#数据标准化fromsklearn.preprocessingimportscalecols=[["HTGD","ATGD","HTP","ATP"]]forcolincols:X_all[col]=scale(X_all[col])3.7转换特征数据类型#把这些特征转换成字符串类型X_all.HM1=X_all.HM1.astype("str")X_all.HM2=X_all.HM2.astype("str")X_all.HM3=X_all.HM3.astype("str")X_all.AM1=X_all.AM1.astype("str")X_all.AM2=X_all.AM2.astype("str")X_all.AM3=X_all.AM3.astype("str")defpreprocess_features(X):"""把离散的类型特征转为哑编码特征"""output=pd.DataFrame(index=X.index)forcol,col_datainX.iteritems():ifcol_data.dtype==object:col_data=pd.get_dummies(col_data,prefix=col)output=output.join(col_data)returnoutputX_all=preprocess_features(X_all)print("Processedfeaturecolumns({}totalfeatures):{}".format(len(X_all.columns),list(X_all.columns)))Processedfeaturecolumns(22totalfeatures):["HTGD","ATGD","HTP","ATP","HM1_D","HM1_L","HM1_W","AM1_D","AM1_L","AM1_W","HM2_D","HM2_L","HM2_W","AM2_D","AM2_L","AM2_W","HM3_D","HM3_L","HM3_W","AM3_D","AM3_L","AM3_W"]#预览处理好的数据print("Featurevalues:")display(X_all.head())Featurevalues:HTGDATGDHTPATPHM1_DHM1_LHM1_WAM1_DAM1_LAM1_W...HM2_WAM2_DAM2_LAM2_WHM3_DHM3_LHM3_WAM3_DAM3_LAM3_W300.7248210.339985-0.043566-0.603098100100...000100101031-0.702311-1.088217-1.097731-2.192828010100...0010001010320.0112550.339985-0.570649-0.603098010100...000100101033-0.345528-0.374116-1.097731-1.662918010100...0010001100340.0112551.054086-0.5706490.456723100001...0001001010

5rows×22columns

3.8皮尔逊相关热力图

我们生成一些特征的相关图,以查看特征与特征之间的相关性。为此,我们将利用Seaborn绘图软件包,使我们能够非常方便地绘制热力图,如下所示:

importmatplotlib.pyplotaspltimportseabornassns#防止中文出现错误plt.rcParams["font.sans-serif"]=["SimHei"]plt.rcParams["axes.unicode_minus"]=False#制成皮尔森热图#把标签映射为0和1y_all=y_all.map({"NH":0,"H":1})#合并特征集和标签train_data=pd.concat([X_all,y_all],axis=1)colormap=plt.cm.RdBuplt.figure(figsize=(21,18))plt.title("PearsonCorrelationofFeatures",y=1.05,size=15)sns.heatmap(train_data.astype(float).corr(),linewidths=0.1,vmax=1.0,square=True,cmap=colormap,linecolor="white",annot=True)

通过上图我们可以看出特征HTP特征和HTGD特征相关性很强,同样ATP特征和ATGD特征相关性很强,可以表明多重共线性的情况。这个我们也很容易理解,主场周平均得分数越高,那么主场周平均净胜球数也同样越高。如果我们考虑这些变量,我们可以得出结论,它们给出了几乎相同的信息,因此实际上发生了多重共线性,这里我们会考虑删除HTP和"ATP"这两个特征,保留HTGD和ATGD这两个特征。皮尔森热图非常适合检测这种情况,并且在特征工程中,它们是必不可少的工具。同时,我们也可以看出上上上次球队的比赛结果对目前比赛的结果影响较小,这里我们考虑保留这些特征。

考虑到样本集特征HTP和HTGD,ATP和ATGD的相关性都超过了90%,故我们删除特征HTP,ATP:

X_all=X_all.drop(["HTP","ATP"],axis=1)

看看与FTR最相关的10个特征

#FTRcorrelationmatrixplt.figure(figsize=(14,12))k=10#numberofvariablesforheatmapcols=abs(train_data.astype(float).corr()).nlargest(k,"FTR")["FTR"].indexcm=np.corrcoef(train_data[cols].values.T)sns.set(font_scale=1.25)hm=sns.heatmap(cm,cbar=True,annot=True,square=True,fmt=".2f",annot_kws={"size":10},yticklabels=cols.values,xticklabels=cols.values)plt.show()

我们可以看出最相关的特征是HTGD,表明一个球队主场周平均净胜球数越高,他们赢的概率也就越大。

4.建立机器学习模型并进行预测4.1切分数据

将数据集随机分成为训练集和测试集,并返回划分好的训练集测试集样本和训练集测试集标签。我们直接采用train_test_split接口进行处理。

4.1.1 train_test_splitAPI接口介绍

X_train,X_test,y_train,y_test=cross_validation.train_test_split(train_data,train_target,test_size=0.3,random_state=0)

参数解释:

train_data:被划分的样本特征集

train_target:被划分的样本标签

test_size:如果是浮点数,在0-1之间,表示样本占比;如果是整数的话就是样本的数量

random_state:是随机数的种子。

返回值解释:

x_train:训练集特征值

x_test:测试集特征值

y_train:训练集目标值

y_test:测试集目标值

随机数种子:其实就是该组随机数的编号,在需要重复试验的时候,保证得到一组一样的随机数。比如你每次都填1,其他参数一样的情况下你得到的随机数组是一样的。但填0或不填,每次都会不一样。随机数的产生取决于种子,随机数和种子之间的关系遵从以下两个规则:种子不同,产生不同的随机数;种子相同,即使实例不同也产生相同的随机数。

4.1.2代码处理分割数据fromsklearn.model_selectionimporttrain_test_splitX_train,X_test,y_train,y_test=train_test_split(X_all,y_all,test_size=0.3,random_state=2,stratify=y_all)4.2相关模型及其接口介绍

下面我们分别使用逻辑回归、支持向量机和XGBoost这三种不同的模型,来看看他们的表现。我们先定义一些辅助函数,记录模型的训练时长和评估时长,计算模型的准确率和f1分数。我们首先介绍一下这三个模型联系与区别和相关的接口:

4.2.1逻辑回归介绍

逻辑回归模型是:假设数据服从伯努利分布,通过极大化似然函数的方法,运用梯度下降来求解参数,来达到将数据二分类的目的。该模型的主要优点是解释性比较好;如果特征工程做得好,模型效果也非常不错;训练速度也比较快;输出结果也很容易调整。但是该模型的缺点也很突出,比如:准确率不是很高,比较难处理数据不均衡问题等。

4.2.2逻辑回归模型接口介绍

API:sklearn.linear_model.LogisticRegression(penalty="l2",dual=False,tol=0.0001,C=1.0,fit_intercept=True,intercept_scaling=1,class_weight=None,random_state=None,solver="liblinear",max_iter=100,multi_class="ovr",verbose=0,warm_start=False,n_jobs=1)

主要参数解析:

penalty:正则化参数,l1or l2,default:l2;

C:正则化系数λ的倒数,default:1.0;

fit_intercept:是否存在截距,default:True

solver:损失函数的优化方法,有以下四种可供选择{newton-cg,lbfgs,liblinear,sag}, default:liblinear

multi_class:分类方式选择,一般有{ovr,multinomial},default:ovr;

class_weight:类型权重参数,默认为None

random_state:随机数种子,默认为无

tol:迭代终止判据的误差范围

n_jobs:并行数,为-1时跟CPU核数一致,默认值为1。

以上是主要参数的简单解析,如果大家想深入了解,可以参看官方网址。

4.2.3支持向量机介绍

SVM(SupportVectorMachine)是一种二类分类模型。它的基本模型是在特征空间中寻找间隔最大化的分离超平面的线性分类器。

(1)当训练样本线性可分时,通过硬间隔最大化,学习一个线性分类器,即线性可分支持向量机;(2)当训练数据近似线性可分时,引入松弛变量,通过软间隔最大化,学习一个线性分类器,即线性支持向量机;(3)当训练数据线性不可分时,通过使用核技巧及软间隔最大化,学习非线性支持向量机。

4.2.4支持向量机分类模型API

sklearn.svm.SVC(C=1.0,kernel="rbf",degree=3,gamma="auto",coef0=0.0,shrinking=True,probability=False,tol=0.001,cache_size=200,class_weight=None,verbose=False,max_iter=-1,decision_function_shape=None,random_state=None)

主要参数解析:

C:C-SVC的惩罚参数C,默认值是1.0。C越大,相当于惩罚松弛变量,希望松弛变量接近0,即对误分类的惩罚增大,趋向于对训练集全分对的情况,这样对训练集测试时准确率很高,但泛化能力弱。C值小,对误分类的惩罚减小,允许容错,将他们当成噪声点,泛化能力较强。

kernel:核函数,默认是rbf,可以是‘linear’,‘poly’,‘rbf’,‘sigmoid’,‘precomputed’

0–线性:u"v

1–多项式:(gamma_u"_v+coef0)^degree

2–RBF函数:exp(-gamma|u-v|^2)

3–sigmoid:tanh(gamma_u"_v+coef0)

degree:多项式poly函数的维度,默认是3,选择其他核函数时会被忽略。

gamma:rbf,poly和sigmoid的核函数参数。默认是auto,则会选择1/n_features

coef0:核函数的常数项。对于poly和sigmoid有用。

max_iter:最大迭代次数。-1为无限制。

decision_function_shape:ovo,ovrorNone,default=None。

主要调节的参数有:C、kernel、degree、gamma、coef0;参数详解请参考官网。

4.2.5XGBoost原理介绍

XGBoost是Boosting算法的其中一种,Boosting算法的思想是许多弱分类器集成在一起,形成一个强分类器,基本原理是下一棵决策树输入样本会与前面决策树的训练和预测相关。以为XGBoost是一种提升树模型,所以他是将许多树模型集成在一起,形成一个很强的分类器。而所用到的树模型则是CART回归树模型。

4.2.6XGBoost接口介绍

XGBoost.XGBRegressor(max_depth=3,learning_rate=0.1,n_estimators=100,silent=True,objective="reg:linear",booster="gbtree",n_jobs=1,nthread=None,gamma=0,min_child_weight=1,max_delta_step=0,subsample=1,colsample_bytree=1,colsample_bylevel=1,reg_alpha=0,reg_lambda=1,scale_pos_weight=1,base_score=0.5,random_state=0,seed=None,missing=None,**kwargs)

主要参数解析:

booster:模型类别,主要有2种,gbtree和gbliner,默认是:gbtree;

nthread:使用CPU个数,为-1时表示使用全部CPU进行并行运算(默认),等于1时表示使用1个CPU进行运算;

scale_pos_weight:正样本的权重,在二分类任务中,当正负样本比例失衡时,设置正样本的权重,模型效果更好。例如,当正负样本比例为1:10时,scale_pos_weight=10;

n_estimatores:总共迭代的次数,即决策树的个数;

early_stopping_rounds:在验证集上,当连续n次迭代,分数没有提高后,提前终止训练

max_depth:树的深度,默认值为6,典型值3-10;

min_child_weight:值越大,越容易欠拟合;值越小,越容易过拟合(值较大时,避免模型学习到局部的特殊样本),默认为1;

learning_rate:学习率,控制每次迭代更新权重时的步长,默认0.3;

gamma:惩罚项系数,指定节点分裂所需的最小损失函数下降值;

alpha:L1正则化系数,默认为1;

lambda:L2正则化系数,默认为1;

seed:随机种子。

如想详细学习该API,可以参考官网网址。

4.3建立机器学习模型并评估4.3.1建立模型fromtimeimporttimefromsklearn.metricsimportf1_scoredeftrain_classifier(clf,X_train,y_train):"""训练模型"""#记录训练时长start=time()clf.fit(X_train,y_train)end=time()print("训练时间{:.4f}秒".format(end-start))defpredict_labels(clf,features,target):"""使用模型进行预测"""#记录预测时长start=time()y_pred=clf.predict(features)end=time()print("预测时间in{:.4f}秒".format(end-start))returnf1_score(target,y_pred,pos_label=1),sum(target==y_pred)/float(len(y_pred))deftrain_predict(clf,X_train,y_train,X_test,y_test):"""训练并评估模型"""#Indicatetheclassifierandthetrainingsetsizeprint("训练{}模型,样本数量{}。".format(clf.__class__.__name__,len(X_train)))#训练模型train_classifier(clf,X_train,y_train)#在测试集上评估模型f1,acc=predict_labels(clf,X_train,y_train)print("训练集上的F1分数和准确率为:{:.4f},{:.4f}。".format(f1,acc))f1,acc=predict_labels(clf,X_test,y_test)print("测试集上的F1分数和准确率为:{:.4f},{:.4f}。".format(f1,acc))4.3.2分别初始化,训练和评估模型importxgboostasxgbfromsklearn.linear_modelimportLogisticRegressionfromsklearn.svmimportSVC#分别建立三个模型clf_A=LogisticRegression(random_state=42)clf_B=SVC(random_state=42,kernel="rbf",gamma="auto")clf_C=xgb.XGBClassifier(seed=42)train_predict(clf_A,X_train,y_train,X_test,y_test)print("")train_predict(clf_B,X_train,y_train,X_test,y_test)print("")train_predict(clf_C,X_train,y_train,X_test,y_test)print("")训练LogisticRegression模型,样本数量3675。训练时间0.0050秒预测时间in0.0010秒训练集上的F1分数和准确率为:0.6232,0.6648。预测时间in0.0010秒测试集上的F1分数和准确率为:0.6120,0.6457。训练SVC模型,样本数量3675。训练时间0.5755秒预测时间in0.3620秒训练集上的F1分数和准确率为:0.6152,0.6746。预测时间in0.1486秒测试集上的F1分数和准确率为:0.5858,0.6400.训练XGBClassifier模型,样本数量3675...训练时间0.4079秒预测时间in0.0110秒训练集上的F1分数和准确率为:0.6652,0.7067.预测时间in0.0060秒测试集上的F1分数和准确率为:0.5844,0.6279。

通过运行结果,我们发现:

在训练时间上,逻辑回归耗时最短,XGBoost耗时最长,为2秒多。

在预测时间上,逻辑回归耗时最短,支持向量机耗时最长。

在训练集上F1分数方面,XGBoost得分最高,支持向量机得分最低,但是差距不是很大。

在训练集上准确率方面分析,XGBoost得分最高,逻辑回归最低。

在测试集上F1分数方面分析,逻辑回归的最好,其余两个模型基本相等,相对较低。

在测试集上准确率方面分析,逻辑回归支持向量机**2个模型基本相等,稍微比XBGoost高一点。

4.4超参数调整

我们使用sklearn的GridSearch来进行超参数调参。

fromsklearn.model_selectionimportGridSearchCVfromsklearn.metricsimportmake_scorerimportxgboostasxgb#设置想要自动调参的参数parameters={"n_estimators":[90,100,110],"max_depth":[5,6,7],}#初始化模型clf=xgb.XGBClassifier(seed=42)f1_scorer=make_scorer(f1_score,pos_label=1)#使用grdisearch自动调参grid_obj=GridSearchCV(clf,scoring=f1_scorer,param_grid=parameters,cv=5)grid_obj=grid_obj.fit(X_train,y_train)#得到最佳的模型clf=grid_obj.best_estimator_#print(clf)#查看最终的模型效果f1,acc=predict_labels(clf,X_train,y_train)print("F1scoreandaccuracyscorefortrainingset:{:.4f},{:.4f}。".format(f1,acc))f1,acc=predict_labels(clf,X_test,y_test)print("F1scoreandaccuracyscorefortestset:{:.4f},{:.4f}。".format(f1,acc))预测云服务器混合云机器学习预测比赛结果机器学习pythonpython机器学习

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/18657.html

上一篇:谈谈机器学习与传统编程之间的区别下一篇:机器学习算法基础(使用Python代码)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。

上一篇

下一篇