目录
基于NBA2018-2019赛季常规赛球员数据进行数据挖掘
1. 挖掘背景与目标
1.1 挖掘背景
美国职业篮球联赛(National Basketball Association),简称NBA,于1946年6月6日在纽约成立,是由北美三十支队伍组成的男子职业篮球联盟,汇集了世界上最顶级的球员,是美国四大职业体育联盟之一。诞生了:迈克尔·乔丹,比尔·拉塞尔,卡里姆·阿布杜尔-贾巴尔,威尔特·张伯伦,埃尔文·约翰逊,拉里·伯德,奥斯卡·罗伯特森,约翰·哈弗利切克,里克·巴里,约翰·斯托克顿,卡尔·马龙,埃尔金·贝勒,大卫-罗宾逊,朱利叶斯·欧文,查尔斯·巴克利,哈基姆·奥拉朱旺,沙奎尔·奥尼尔,阿伦·艾弗森,德克·诺维茨基,蒂姆·邓肯,马努·吉诺比利,科比·布莱恩特,文斯·卡特,特雷西·麦克格雷迪,勒布朗·詹姆斯,凯文·杜兰特 ,斯蒂芬·库里,凯里·欧文,莱昂纳德,姚明,韦德,拉塞尔·威斯布鲁克,扬尼斯·阿德托昆博,詹姆斯·哈登等著名球员,是世界水平最高篮球赛事。
近年来,NBA联盟出现了很多“垃圾”合同,也就是球员自身实力与球队给予的工资不相符合。因此如何判断一个球员是否具有相应工资的实力成为了众多NBA球队的难题。为此,针对NBA球员数据特点,利用数据挖掘技术研究球员所具有的实际价值有着重要的意义
1.2 挖掘目标
根据NBA2018-2019赛季常规赛球员数据情况,构建主成分分析模型,进行数据挖掘和分析,最后由加权法计算出各个球员的主成分综合得分,然后根据综合得分进行排名,从而确定球员的实际价值。
2. 分析方法与过程
2.1 分析方法(主成分分析)
本文采用主成分分析方法进行数据挖掘分析,是因为NBA各球员的数据特征值很多,不知道依据什么来判定一个球员的好坏与实际价值,虽然大多数人可能都认为得分能力是很好的依据,这是不可否定的,但还是想进一步的探索球员数据的内在价值,所以才采用了主成分分析方法。在此说明,本文不介绍主成分分析方法的思想和步骤,不明白的可自己查阅相关资料。
2.1 分析过程
图2-1 主成分分析流程图
1)从NBA中国官方网站获取原始数据;2)对样本数据进行探索性分析,剔除与球员实际价值没有关联的数据特征;3)对样本数据进行预处理,包括数据清洗、属性规约和数据变换;4)构建建模样本集;5)构建主成分分析模型;6)根据模型输出的结果进行分析。
3. 获取数据
利用网络爬虫技术从NBA官方网站爬取球员得分在前50名的各项数据,原始数据包括的球员的得分排名、姓名、所在球队、位置、出场数、首发场数、场均篮板、场均助攻、上场时间、效率值、2分命中率、3分命中率、进攻、防守、场均盖帽、场均抢断、场均失误、场均犯规、场均得分等19项数据。爬虫代码见附录。部分数据如图3-1可见。
图3-1 NBA球员部分数据
4. 数据探索性分析与预处理
4.1探索性分析
数据的探索性分析是对数据进行初步研究,发现数据的内在规律特征,有助于选择合适的数据预处理和数据分析技术。本文主要采用条形图分析得分在前20名的球员的得分能力,以及上场时间与场均得分的散点图探索二者之间是否具有正相关关系。
4.1.1 条形图分析
如图2-2可以看出,2018-2019常规赛球员詹姆士哈登的得分能力明显高于其他球员,在之后的球员得分能力出现断崖式现象,其他球员差距不是很大,属于循序渐进的减少,由此可见詹姆士哈登得分能力属于第一档,其他球员无法相比较,要是以得分能力来判断一个球员的价值,那么毫无疑问,詹姆士哈登的价值最高。代码见附录。
图2-2 球员得分条形图
4.1.2 散点图分析
如图2-3,绘制的是得分能力前20名球员的上场时间与场均得分的散点图。由图可以看出,上场时间与场均得分并无正相关关系,以我们正常的思维,可能会认为上场时间与场均的会是正相关,随着上场时间的增加,得分也会随之增加,但是NBA是全世界最好的篮球比赛联盟,得分前20的球员也就是意味着他们是世界上得分能力最好的20个人,他们单位时间内得分能力爆炸,即使用很少的时间也能得很多的分,另一反面,如果他们上场时间多但是得分依旧没有变化,那可能是因为他们在帮助队友得分,自己在做那个球队的串联者。如果将所有普通球员的上场时间与场均得分画散点图,那可能就是正相关关系。代码见附录。
图2-3 散点图
4.2 数据预处理
4.2.1 数据清洗
数据清洗的目的是从建模的相关需要方面考虑,筛选出需要的数据。由于原始数据中并不是所有的数据都需要进行分析,因此需要在数据处理时,将多余的数据进行过滤。本文中主要进行如下操作。
- 考虑到受伤和轮休球员的原因,将上场场次过滤掉。
- 由于得分能力在前50的球员基本都是首发登场,所以过滤掉首发场次。
- 球员的犯规次数与球员的实际价值没有太多关联,因此过滤掉。
- 由于是主成分分析建模找出最高实际价值球员,那么只需要考虑场均得分前20的球员,其他球员过滤掉。
4.2.2 属性规约
由于本文采用的是主成分分析,所以需要将非数值型数据属性过滤掉。
4.2.3 数据变换
看待一个球员的控球和传球能力,不是只看他的助攻,还要看其失误的次数,如果一个球员一场比赛有5个助攻,但他的失误也有5个,而另一名球员一场比赛助攻4个,但失误只有2个,那么可以认为后一名球员的控球与传球能力要好于前一名球员。由于原始数据没有直接给出此类指标,因此,我们通过原始数据进行构造这一指标—助攻失误比,计算方式如下:
助攻失误比 = 场均助攻 / 场均失误
有了助攻失误比这一指标,就可以将助攻与失误这两个指标删除。以上所有的数据预处理使用Python编程语言实现,代码见附录。
5. 构建主成分分析模型与结果分析
5.1 构建建模数据
由于本文采用主成分分析,所以需要将前一阶段已经处理好的数据进行标准化处理,才能形成建模数据。标准化过后的部分数据如图5-1所示。
图5-1 标准化后数据
5.2 构建模型与结果分析
本文先利用Python语言进行数据预处理后,再利用R语言对标准化过后的数据进行建立主成分分析模型,由于R语言读入数据的问题,需要将数据的第一列的第一行球员标签删除,为了方便起见,采用手动删除并保存数据,之后的数据可以进行建模。R语言建模代码见附录。
5.2.1 导入数据
如图5-2所示,将处理过后的数据进行导入,利用ls与dim函数查看数据的变量个数和维度。
图5-2 数据导入与显示
5.2.2 计算相关系数矩阵
如图5-3相关系数矩阵可知,场均篮板与防守的相关性很高,高达0.9875,说明了防守好的人在篮板球的控制上是非常好的;三分命中率和罚球命中率有一定的正相关性但与防守是负相关,这可能说明三分命中率高的人可能防守不好;场均得分与效率的相关性是0.58,说明得分高的人可能效率值也会很高;另一方面,助攻失误比和场均抢断与其他变量的相关性都不是很高,说明这两个变量对于得分前20名的球员的波动性很小。
图5-3 相关系数
5.2.3 PCA综合信息
如图5-4所示,建立主成分分析模型,并输出模型的一些综合信息。其中有各主成分的标准差大小,方差占比大小,累计贡献率大小,可以看出,第一主成分解释了原始数据的44.55%的数据信息量,说明第一主成分拟合的很好,且前五个主成分可以解释原始数据的89.7%信息量。由图中载荷矩阵可以看书,第一主成分在场均篮板、防守、场均盖帽、效率以及进攻上的载荷值较大,可以视为进攻防守型上的主成分;第二主成分在场均时间、场均抢断以及场均得分上的载荷值较大,可以视为单位时间的得分能力上的主成分;第三主成分在三分命中率、罚球命中率以及场均得分的载荷值较大,可以视为命中率及得分上的主成分;第四主成分在两分命中率和助攻失误比上的载荷值较大,第五主成分在三分命中率、场均抢断的载荷值较大。
图5-4 主成分模型综合信息
5.2.4 确定主成分个数
由图5-5可知各个主成分的特征值大小,以及前五个主成分的累计贡献率。再有图5-6的碎石图可以确定主成分的个数应该定为5个,因为需要解释原始数据85%以上的信息量
图5-5 特征值及方差贡献率
图5-6 碎石图
5.2.5 主成分散点图分析
由图5-7可以看出扬尼斯安特托昆博、乔尔恩比德、安东尼戴维斯在第一主成分上的得分较高,说明他们是进攻防守型球员,既能进攻得分又能在防守端表现的很好;詹姆斯哈登、保罗乔治在第二主成分上的得分很高,说明他们是单位时间内的得分能力很高,且他们的场均抢断也很不错;图的左下方说明有很多球员在罚球命中率上很高;图中中心是科怀伦纳德,说明这名球员是一名综合型选手,在各个方面得分都不是很高,但都是正常水平。
图5-7 主成分散点图
5.2.6 主成分得分
由图5-8可知前五个主成分的得分情况,由图5-9可以看到各个球员的综合得分以及排名情况。排名第一的是扬尼斯安特托昆博,可以说明扬尼斯安特托昆博这名球员在本文中的主成分分析模型中得分最高,也是具有最高实际价值的球员,从实际方面来看,扬尼斯安特托昆博也是今年的常规赛MVP最佳候选人,前不久又与雄鹿队队续约,薪资高达每年5000多万美元,由此说明雄鹿队管理层也是看上了扬尼斯安特托昆博的实际价值。排在扬尼斯安特托昆博后两名球员分别是乔尔恩比德和安东尼戴维斯,在主成分散点图中就可以看出他们三个的第一主成分得分很高,从而在综合得分上影响很大。由综合排名情况可以给球队管理层作为一个球员实际价值的参考,以此来考虑签约相关问题。
图5-8 前五个主成分得分
图5-9 综合得分
6. 总结
本文对NBA球员得分在前20名的球员数据进行数据挖掘,因为数据变量过多,无法确定球员的实际价值,所以采用主成分分析方法将多个数据变量转化为少数几个综合变量,最后计算各个球员在综合变量上的总得分情况,从而来确定一个球员的实际价值,结合事实分析得出,NBA2018-2019赛季常规赛最具有实际价值的球员是扬尼斯安特托昆博。此外,在本此数据挖掘中,交叉使用了Python与R两种语言,在使用中感受到两种语言各有所长和所短,可以交叉使用,取其长补其所短。
本文不足之处很多,在此只说以下几点不足之处:
- 只采用了单一的方法进行建模,没有与之比较的方法。
- 数据预处理过于简单,不够深入,可能无法真正的体现数据的内在价值。
- 本文虽然得出的最具有实际价值的球员,但从最后的综合得分可以看出,本文所用数据建立的模型对于锋线球员有利,对于后卫线球员不利,库里是NBA联盟最近几年表现很好的球员,可此模型没有体现出库里这名球员的价值。所以模型有待改进。
附录
1. 爬虫代码如下
# -*- coding: utf-8 -*-
"""
作者:赵修锐
日期:2020/4/27
作用:爬取NBA TOP 50 球员的数据并写入CSV或JSON文件
"""
import json
import csv
import requests
url = 'https://china.nba.com/static/data/league/playerstats_All_All_All_0_All_false_2018_2_All_Team_points_All_perGame.json'
header = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1)'
+ ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36'}
def get_page(url):
try:
r = requests.get(url, headers=header)
if r.status_code == 200:
print(r.json())
return r.json()
except requests.ConnectionError as e:
print('Error', e.args)
def parse_page(json):
if json:
items = json.get('payload').get('players')
for item in items:
it = item['playerProfile']
its = item['teamProfile']
stat = item['statAverage']
player={}
player['排名'] = item['rank']
player['球员']=it['displayName']
player['球队'] = its['name']
player['位置'] = it['position']
player['场数'] = stat['games']
player['先发'] = stat['gamesStarted']
player['场均篮板'] = stat['rebsPg']
player['场均助攻'] = stat['assistsPg']
player['分钟'] = stat['minsPg']
player['效率'] = stat['efficiency']
player['2分命中率%'] = stat['fgpct']
player['3分命中率%'] = stat['tppct']
player['罚球命中率%'] = stat['ftpct']
player['进攻'] = stat['offRebsPg']
player['防守'] = stat['defRebsPg']
player['场均抢断'] = stat['stealsPg']
player['场均盖帽'] = stat['blocksPg']
player['失误'] = stat['turnoversPg']
player['犯规'] = stat['foulsPg']
player['场均得分'] = stat['pointsPg']
yield player
def write_tofile():
fieldnames = ['排名', '球员', '球队', '位置', '场数', '先发', '场均篮板',
'场均助攻', '分钟', '效率', '2分命中率%', '3分命中率%', '罚球命中率%',
'进攻', '防守', '场均抢断', '场均盖帽', '失误', '犯规', '场均得分']
with open('NBA_TOP50球员数据.csv', 'a', encoding='utf_8') as f:
writer = csv.writer(f)
writer.writerow(fieldnames)
for result in parse_page(get_page(url)):
# 输出json文件
# with open('NBA_TOP50球员数据.json', 'a', encoding='utf-8') as f:
# f.write(json.dumps(result, indent=1, ensure_ascii=False))
# 输出csv文件
with open('NBA_TOP50球员数据.csv', 'a', encoding='utf-8') as f:
fieldnames = ['排名', '球员', '球队', '位置', '场数', '先发', '场均篮板',
'场均助攻', '分钟', '效率', '2分命中率%', '3分命中率%', '罚球命中率%',
'进攻', '防守', '场均抢断', '场均盖帽', '失误', '犯规', '场均得分']
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writerow(result)
def main():
write_tofile()
if __name__ == '__main__':
main()
2. 数据探索性分析及预处理代码如下
import pandas as pd
# 参数初始化
inputfile = 'NBA_TOP50球员数据.csv'
outputfile = 'zscoredfile1.csv' #标准化后的数据
data = pd.read_csv(inputfile, index_col='球员', engine='python', encoding='utf-8')
# 读入数据并以球员姓名作为行标签
# print(data.columns) # 查看列名
# print(data.head()) # 查看数据前几行数据信息
data = data.iloc[:20] # 获取前20行数据
# data.columns = ['X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X7', 'X8', 'X9', 'X10', 'X11', 'X12', 'X13', 'X14', 'X15']
data = data[['场均篮板', '场均助攻', '分钟', '效率', '2分命中率%', '3分命中率%',
'罚球命中率%', '进攻', '防守', '场均抢断', '场均盖帽', '失误', '犯规', '场均得分']]
new = data['场均助攻']/data['失误'] # 助攻失误比
new = pd.DataFrame(new) # 转换成DataFrame结构数据
data = pd.concat([data, new], axis=1) # 与之前的数据连接
data = data[['场均篮板', '分钟', '效率', '2分命中率%', '3分命中率%',
'罚球命中率%', '进攻', '防守', '场均抢断', '场均盖帽', '场均得分', 0]]
data.columns = ['场均篮板', '时间', '效率', '两分命中率', '三分命中率',
'罚球命中率', '进攻', '防守', '场均抢断', '场均盖帽', '场均得分', '助攻失误比']
print(data)
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号
data['场均得分'].plot(kind='bar')
plt.ylabel('场均得分')
plt.xlabel('球员')
plt.title('TOP_20得分条形图')
plt.savefig('df.png')
plt.scatter(data['时间'], data['场均得分'])
plt.ylabel('场均得分')
plt.xlabel('上场时间')
plt.title('TOP_20散点图')
plt.show()
3. 主成分分析R语言代码如下
data = read.table("C:/R语言/zscoredfile1.csv", sep=",",header=T) #数据的导入
ls(data) #ls()函数列出所有变量
dim(data) #维度
rownames(data)=data[[1]] #数组各行名字定义为数据文件的的第一列
data = data[,2:13]
data
class(data) #查看数据类型
cor(data) #相关系数矩阵
pca=princomp(data,cor=TRUE) #主成分分析
summary(pca,loadings=TRUE) #列出结果,包含特征向量
y=eigen(cor(data)) #求出cor(data)的特征值和特征向量
y$values #输出特征值
sum(y$values[1:5])/sum(y$values) #求前5个主成分的累计方差贡献率
pca$loadings[,1:5] #输出前5个主成分的载荷矩阵
screeplot(pca,type='lines') #画出碎石图
biplot(pca) #画出主成分散点图
s=pca$scores[,1:5] #输出前5个主成分的得分
s
scores=0.0
for (i in 1:5)
scores=(y$values[i]*s[,i])/(sum(y$values[1:5]))+scores
sort=cbind(s,scores) #输出综合得分信息
class(sort)
paiming=sort[order(sort[,6],decreasing=T),] #按最后一列降序排序
rank=c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
rank=cbind(paiming,rank)
rank[,c(6,7)] #获取综合得分最终排名
写在最后
纯个人原创,转载或者其他目的使用请告知,谢谢!