您的位置:首页 > 编程语言 > Python开发

Python数据挖掘入门与实践 第三章 用决策树预测获胜球队(一)pandas的数据预处理与决策树(Decision tree)

2019-04-09 09:54 507 查看

作为一个NBA球迷,看到这一章还是挺激动的。
不过内容有点难,研究了半天。。。

要是赌球的,用这章的预测+凯利公式,是不是就能提升赢钱概率了?

数据预处理

回归书本内容,既然要分析,首先需要有数据:
打开basketball-reference数据链接:


点开Get table as CSV (for Excel),这样我们就能获得数据了。
常规赛是4月16日截至,我们使用常规赛的数据~
直接crtl+C,crtl+V到txt,直接改名csv即可。
(就几个月份,如果要弄的数据多的,可以爬虫)

我们先用pandas来读取一下:

import pandas as pd
data_filename = r'保存路径\文件名.csv'
dataset = pd.read_csv(data_filename)
print(dataset)

可以看到,自动识别成了一个1230 rows x 10 columns的<class ‘pandas.core.frame.DataFrame’>。(图片略掉)

各列名称为:
Date Start (ET) Visitor/Neutral PTS Home/Neutral PTS.1 Unnamed: 6 Unnamed: 7 Attend. Notes
其中,空白自己识别为:Unnamed:列号。
现在,我们把它重命名一下,防止客队和主队的分数看不清楚。
同时把日期转换成数据格式:parse_dates=[0] ##代表第0列的数据日期格式进行转换。

import pandas as pd
data_filename = r'保存路径\文件名.csv'
dataset = pd.read_csv(data_filename, parse_dates=[0])
dataset.columns = ["Date","Start","Visitor Team","VisitorPts", "Home Team", "HomePts","Score Type","OT?", "Attend.", "Notes"]
print(dataset)



可以看到这次的格式就相当整齐,且每一列的表头都完善了。

提取新特征

篮球比赛肯定有输赢,得分高的队伍,就是赢家,所以可以通过简单的判断,来增加一列数据,来判断home是否战胜了vistor:
dataset[“HomeWin”]= dataset[“VisitorPts”] < dataset[“HomePts”]
在把这列单独抽取,作为我们的预测是否准确的判断值:
y_true = dataset[“HomeWin”].values

为了预测这个结果,我们先通过简单的方法:
看看这个队伍上一轮,是否取得了胜利,
如果球队是第一次出现,则默认他们上一场是输球。(当然这里是可以改进的。)
实现方法为:
from collections import defaultdict
dataset[“HomeLastWin”] = False
dataset[“VisitorLastWin”] = False
won_last = defaultdict(int)

字典的键为球队,值为是否赢得上一场比赛。
遍历所有行,在此过程中,更新每一行,为其增加两个特征值:两支球队在上场比赛有没有获胜,并且更新defaultdict:
for index, row in dataset.iterrows(): ##遍历所有行,index是行号,row是具体内容。
home_team = row[“Home Team”] ##提取主队名
visitor_team = row[“Visitor Team”] ##提取客队名
row[“HomeLastWin”] = won_last[home_team] ##第n行的主队上一轮否赢球进行赋值,默认为0(False)
row[“VisitorLastWin”] = won_last[visitor_team] ##第n行的客队上一轮否赢球进行赋值,默认为0(False)
dataset.ix[index] = row ##
won_last[home_team] = row[“HomeWin”] ##更新defaultdict
won_last[visitor_team] = not row[“HomeWin”] ##更新defaultdict

我们可以看一下,20~25行的数据,与每一行的结构究竟是如何的:
if index==25:print(row,type(row)) ## 打印index==25的row
print(dataset.ix[20:25]) ##循坏外,打印20~25行(是包含25的)的DataFrame
整理代码并看结果:

import pandas as pd
import numpy as np
from collections import defau
4000
ltdict

data_filename = r'保存路径\文件名.csv'
dataset = pd.read_csv(data_filename, parse_dates=[0])
dataset.columns = ["Date","Start","Visitor Team","VisitorPts", "Home Team", "HomePts","Score Type","OT?", "Attend.", "Notes"]
dataset["HomeWin"] = dataset["VisitorPts"] < dataset["HomePts"]
y_true = dataset["HomeWin"].values

dataset["HomeLastWin"] = False
dataset["VisitorLastWin"] = False
won_last = defaultdict(int)
for index, row in dataset.iterrows():
home_team = row["Home Team"] ##提取主队名
visitor_team = row["Visitor Team"] ##提取客队名
row["HomeLastWin"] = won_last[home_team] ##第n行的主队上一轮否赢球进行赋值,默认为0(False)
row["VisitorLastWin"] = won_last[visitor_team] ##第n行的客队上一轮否赢球进行赋值,默认为0(False)
dataset.ix[index] = row ##

won_last[home_team] = row["HomeWin"]
won_last[visitor_team] = not row["HomeWin"]
if index==25:print(row,type(row))

print(dataset.ix[20:25])

可以看到,row的结果非常清晰,且for index, row in dataset.iterrows():
这句非常像enumerate()函数。


决策树(Decision tree)

接下来就要进入正题了:
既然获取了y_true这个真值,又获取了HomeLastWinVisitorLastWin这2个条件,那么就可以进行决策树的预测了。

决策树是一种有监督的机器学习算法,它看起来就像是由一系列节点组成的流程图,其中位于上层节点的值决定下一步走向哪个节点。


首先是训练阶段,用训练数据构造一棵树。上一章的近邻算法没有训练阶段,但是决策树需要。从这个意义上说,近邻算法是一种惰性算法,在用它进行分类时,它才开始干活。相反,决策树跟大多数机器学习方法类似,是一种积极学习的算法,在训练阶段完成模型的创建。
 其次是预测阶段,用训练好的决策树预测新数据的类别。以上图为例,[“is raining”,“very
windy”]的预测结果为“Bad”(坏天气)。

决策树中的参数

退出准则是决策树的一个重要特性。构建决策树时,最后几步决策仅依赖于少数个体,随意性大。使用特定节点作出推测容易导致过拟合训练数据,而使用退出准则可以防止决策精度过高。
除了设定退出准则外,也可以先创建一棵完整的树,再对其进行修剪,去掉对整个过程没有提供太多信息的节点。这个过程叫作剪枝(pruning)。
scikit-learn库实现的决策树算法给出了退出方法,使用下面这两个选项就可以达到目的。
min_samples_split:指定创建一个新节点至少需要的个体数量。。
min_samples_leaf:指定为了保留节点,每个节点至少应该包含的个体数量。
第一个参数控制着决策节点的创建,第二个参数决定着决策节点能否被保留。 决策树的另一个参数是创建决策的标准,常用的有以下两个。
基尼不纯度(Gini impurity):用于衡量决策节点错误预测新个体类别的比例。
信息增益(Informationgain):用信息论中的熵来表示决策节点提供多少新信息。

使用决策树

from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(random_state=14)

然后可以通过主客队上一场的输赢情况来进行预测。
X_previouswins = dataset[[“HomeLastWin”, “VisitorLastWin”]].values
依旧是深究过的cross_val_score函数来进行评分。
from sklearn.model_selection import cross_val_score
scores = cross_val_score(clf, X_previouswins, y_true,scoring=‘accuracy’)
print(“Accuracy: {0:.1f}%”.format(np.mean(scores) * 100))

整理代码如下:

import pandas as pd
import numpy as np
from collections import defaultdict
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score

data_filename = r'保存路径\文件名.csv'
dataset = pd.read_csv(data_filename, parse_dates=[0])
dataset.columns = ["Date","Start","Visitor Team","VisitorPts", "Home Team", "HomePts","Score Type","OT?", "Attend.", "Notes"]
dataset["HomeWin"] = dataset["VisitorPts"] < dataset["HomePts"]
y_true = dataset["HomeWin"].values

dataset["HomeLastWin"] = False
dataset["VisitorLastWin"] = False
won_last = defaultdict(int)
for index, row in dataset.iterrows():
home_team = row["Home Team"] ##提取主队名
visitor_team = row["Visitor Team"] ##提取客队名
row["HomeLastWin"] = won_last[home_team] ##第n行的主队上一轮否赢球进行赋值,默认为0(False)
row["VisitorLastWin"] = won_last[visitor_team] ##第n行的客队上一轮否赢球进行赋值,默认为0(False)
dataset.ix[index] = row ##

won_last[home_team] = row["HomeWin"]
won_last[visitor_team] = not row["HomeWin"]

clf = DecisionTreeClassifier(random_state=14)
X_previouswins = dataset[["HomeLastWin", "VisitorLastWin"]].values
scores = cross_val_score(clf, X_previouswins, y_true,scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

结果为:Accuracy: 57.8%
比起随机预测的50%来准确了一丢丢。。。
从数据集中构建有效特征(Feature Engineering,特征工程)是数据挖掘的难点所在,好的特征直接关系到结果的正确率——甚至比选择合适的算法更重要!
那么接下来思考这2个问题:
 一般而言,什么样的球队水平更高?
 两支球队上一次相遇时,谁是赢家?
通常,上一赛季的常规赛结果,对下一赛季还是有指导意义的。
(但不排除马刺摆烂获取邓肯后的突飞猛进,或者是詹黄远走洛杉矶骑士彻底摆烂之类的情况)
获取2013年常规赛的战绩:
保存成CSV之后,继续用pandas读取数据:
standings_filename = r’保存路径\文件名.csv’
standings = pd.read_csv(standings_filename)
print(standings)


可以看到,详细记载了总战绩,主场,客场,对东部,对西部,分区,分差小于3分,每个月战绩等等数据。
现在,我们只使用一列数据:排名!
dataset[“HomeTeamRanksHigher”] = 0 ##之前的dataset新增一列数据:主队排名更高为1,默认为0。
for index, row in dataset.iterrows(): ##继续循环每一行
home_team = row[“Home Team”]
visitor_team = row[“Visitor Team”] ##获取主客队名称。
但是。。。有个情况是,2014年,新奥尔良黄蜂改名为了鹈鹕。。。所以需要进行替换。
if home_team == “New Orleans Pelicans”:
home_team = “New Orleans Hornets”
elif visitor_team == “New Orleans Pelicans”:
visitor_team = "New Orleans Hornets"

获取主队的排名:
home_rank = standings[standings[“Team”] ==home_team][“Rk”].values[0]
即,首先对standings这个DataFrame中的Team列的所有数据进行判断,是否是home_team:standings[“Team”]==home_team
然后获取这个home_team的排名。
同理可以获取客队的排名:
visitor_rank = standings[standings[“Team”]==visitor_team][“Rk”].values[0]
然后更新 “HomeTeamRanksHigher"的数据:
row[“HomeTeamRanksHigher”] = int(home_rank > visitor_rank)
最后,令dataset.ix[index] = row
这样子,除了"HomeLastWin”, “VisitorLastWin”,我们又有了新的特征:“HomeTeamRanksHigher”,从新进行决策树计算:
X_homehigher = dataset[[“HomeLastWin”, “VisitorLastWin”,“HomeTeamRanksHigher”]].values
clf = DecisionTreeClassifier(random_state=14)
scores = cross_val_score(clf, X_homehigher, y_true,scoring=‘accuracy’)
print(“Accuracy: {0:.1f}%”.format(np.mean(scores) * 100))

汇总代码,并且计算结果:

import pandas as pd
import numpy as np
from collections import defaultdict
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score

data_filename = r'保存路径\文件名.csv'
dataset = pd.read_csv(data_filename, parse_dates=[0])
dataset.columns = ["Date","Start","Visitor Team","VisitorPts", "Home Team", "HomePts","Score Type","OT?", "Attend.", "Notes"]
dataset["HomeWin"] = dataset["VisitorPts"] < dataset["HomePts"]
y_true = dataset["HomeWin"].values

dataset["HomeLastWin"] = False
dataset["VisitorLastWin"] = False
won_last = defaultdict(int)
for index, row in dataset.iterrows():
home_team = row["Home Team"] ##提取主队名
visitor_team = row["Visitor Team"] ##提取客队名
row["HomeLastWin"] = won_last[home_team] ##第n行的主队上一轮否赢球进行赋值,默认为0(False)
row["VisitorLastWin"] = won_last[visitor_team] ##第n行的客队上一轮否赢球进行赋值,默认为0(False)
dataset.ix[index] = row ##

won_last[home_team] = row["HomeWin"]
won_last[visitor_team] = not row["HomeWin"]

clf = DecisionTreeClassifier(random_state=14)
X_previouswins = dataset[["HomeLastWin", "VisitorLastWin"]].values
scores = cross_val_score(clf, X_previouswins, y_true,scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))
standings_filename =r'保存路径\文件名.csv'
standings = pd.read_csv(standings_filename)

dataset["HomeTeamRanksHigher"] = 0
for index, row in dataset.iterrows():
home_team = row["Home Team"]
visitor_team = row["Visitor Team"]
if home_team == "New Orleans Pelicans":
home_team = "New Orleans Hornets"
elif visitor_team == "New Orleans Pelicans":
visitor_team = "New Orleans Hornets"
home_rank = standings[standings["Team"]==home_team]["Rk"].values[0]
visitor_rank = standings[standings["Team"]==visitor_team]["Rk"].values[0]
row["HomeTeamRanksHigher"] = int(home_rank > visitor_rank)
dataset.ix[index] = row

X_homehigher = dataset[["HomeLastWin","VisitorLastWin","HomeTeamRanksHigher"]].values
clf = DecisionTreeClassifier(random_state=14)
scores = cross_val_score(clf, X_homehigher, y_true,scoring='accuracy')
print("Accuracy: {0:.1f}%".format(np.mean(scores) * 100))

可以看到,从Accuracy: 57.8%,提升到了Accuracy: 60.3%。
但是球场上不存在强者恒强,弱者恒弱的情况,于是应该思考:是否还能提升准确率呢??
我们想到了,球队A打法,就是克制球队B,尽管他们总体胜率可能不如B,但是每一次相遇,A总能让B吃苦头。
于是我们增加另一列数据:AB相遇,上一次的胜者是谁?
首先创建一个 defaultdict(int)来存储并更新
last_match_winner = defaultdict(int)
dataset[“HomeTeamWonLast”] = 0

遍历所有的行,获取主客队名:
for index, row in dataset.iterrows():
home_team = row[“Home Team”]
visitor_team = row[“Visitor Team”]

创建一个交锋双方的元组,如果是主队获胜,那么dataset[“HomeTeamWonLast”] = 1且更新 defaultdict。
teams = tuple(sorted([home_team, visitor_team]))
row[“HomeTeamWonLast”] = 1 if last_match_winner[teams] ==row[“Home Team”] else 0
dataset.ix[index] = row

winner = row[“Home Team”] if row[“HomeWin”] else row[“Visitor Team”]
last_match_winner[teams] = winner

X_lastwinner = dataset[[“HomeTeamRanksHigher”, “HomeTeamWonLast”]].values
clf = DecisionTreeClassifier(random_state=14)
scores = cross_val_score(clf, X_lastwinner, y_true,scoring=‘accuracy’)
print(“Accuracy: {0:.1f}%”.format(np.mean(scores) * 100))

汇总代码,可以得到新的预测结果为:Accuracy: 60.6%。
又提高了0.3%。

下一文,开始讲随机森林。

参考文献:
1.https://pandas.pydata.org/pandas-docs/stable/reference/frame.html#indexing-iteration
2.https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html#examples-using-sklearn-tree-decisiontreeclassifier

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: