前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >分析一把NBA季后赛

分析一把NBA季后赛

作者头像
龙哥
发布2019-05-13 14:53:07
6350
发布2019-05-13 14:53:07
举报
文章被收录于专栏:Python绿色通道Python绿色通道

NBA激战正酣,首轮除掘金和马刺的较量还没有结束外,其余对决都已经结束,本文将手把手带你可视化分析下各球队的首轮表现,同时将对次轮最受瞩目的火勇大战进行一个简单的前瞻分析!

通过本文,你将学会python中使用matplotlib库绘制柱状图、散点图、雷达图的相关知识!咱们开始吧!

1、数据获取及准备

我们首先获取各球队季后赛首轮的数据,网址是:https://www.basketball-reference.com/playoffs/NBA_2019.html。首先选择Playoffs:

然后,下拉到球队技术统计这里,导出csv,如果无法直接倒出,可以复制到txt文件里面(我就是这么干的):

然后,我们再把对手表现的数据导出,这样基础数据就准备好了:

好了,我们使用代码将数据进行读取,首先导入所需要的库:

代码语言:javascript
复制
import pandas as pdimport matplotlib.pyplot as pltimport numpy as np

将数据使用pandas进行读取,看看数据如何:

代码语言:javascript
复制
team_stats_df = pd.read_csv('data/nba1.txt',sep=',')team_stats_df

同样,将对手表现的数据读入,但是这里列名需要修改一下:

代码语言:javascript
复制
team_opp_stats_df = pd.read_csv('data/nba2.txt',sep=',')team_opp_stats_df.columns = ['Rk','Team','G'] + ['opp' + x for x in team_opp_stats_df.columns[3:]]

将两个表按照队名进行merge,这样oppPTS列,可以表示场均失分,这也是我们在对手表现数据里面主要关注的列:

代码语言:javascript
复制
mergedf = pd.merge(team_stats_df,team_opp_stats_df,on=['Team'])

2、柱状图分析球队场均得失分

绘制柱状图,需要使用的是plt.bar方法,其主要输入的有两个参数,一个是x轴的值,一个是高度,比如,我们绘制一下各球队首轮平均得分的柱状图:

代码语言:javascript
复制
pts = np.array(mergedf[['PTS','oppPTS']].values.tolist())index = np.arange(pts.shape[0])plt.bar(index,pts[:,1])plt.show()

结果如下:

上面的图还是有几个问题的,首先,我们不希望X显示的是数值,我们希望展示各个球队名称的缩写,同时刻度可以不从0开始,这样看上去区分度不是十分的明显。

针对第一个问题,我们需要首先按顺序得到各个球队名称的缩写:

代码语言:javascript
复制
team_name = ['DEN','SA','GSW','PHI','LAC','BKN','POR','HOU','TOR','OKC','UTA','MIL','ORL','BOS','DET','IND']

随后,将x轴的刻度设置为相应的队名:

代码语言:javascript
复制
plt.xticks(index,team_name) # 设置x轴的刻度

针对第二个问题,我们需要限定y轴的范围,使用ylim方法:

代码语言:javascript
复制
plt.ylim(80,140) # y轴的范围

这样,我们得到了下面的图片:

上面这部分的完整代码如下:

代码语言:javascript
复制
team_name = ['DEN','SA','GSW','PHI','LAC','BKN','POR','HOU','TOR','OKC','UTA','MIL','ORL','BOS','DET','IND']plt.ylabel('Score') # 设置y轴的labelplt.xlabel('Team') # 设置x轴的labelplt.xticks(index,team_name) # 设置x轴的刻度plt.yticks(np.arange(80,141,10)) #y轴的刻度plt.ylim(80,140) # y轴的范围plt.bar(index,pts[:,0]) # 绘制直方图plt.rcParams['figure.figsize'] = (12.0, 6.0) # 修改图片大小plt.show()

此时,我们可能想要将球队的得失分表示出来,代码如下:

代码语言:javascript
复制
fig, ax = plt.subplots()bar_width = 0.35opacity = 0.4
rects1 = ax.bar(index, pts[:,0], bar_width,                alpha=opacity, color='b',                label='OFFENSE')
rects2 = ax.bar(index + bar_width, pts[:,1], bar_width,                alpha=opacity, color='r',                label='DEFENSE')
ax.set_xlabel('Team')ax.set_ylabel('PTS')ax.set_xticks(index + bar_width / 2)ax.set_xticklabels(team_name)ax.set_yticks(np.arange(80,141,10)) #y轴的刻度ax.set_ylim(80,140) # y轴的范围ax.legend()
plt.show()

绘制的结果如下:

3、散点图分析球队场均得失分

这一部分,我们使用散点图来分析球队场均得失分,使用plt.scatter函数,分别指定各个点的x和y值:

代码语言:javascript
复制
plt.scatter(pts[:,0], pts[:,1])plt.xlabel('Offense PTS') # 设置x轴的labelplt.ylabel('Defense PTS') # 设置y轴的labelplt.show()

得到的效果如下:

简单的绘制散点图,什么都看不出来,此时,我们提出两个需求,首先,能不能将各个球队的简称放到各个点的旁边,这样我们就能清楚知道哪个点代表哪个球队。其次,能不能在图中添加两条直线,分别表示球队得分和失分的平均值?我们一个一个来解决。

首先,将球队标记加入到图中,我们使用plt.annotate函数,该函数需要传入三个参数,依次是注释文本内容,被注释的坐标点,注释文字的坐标位置:

代码语言:javascript
复制
plt.scatter(pts[:,0], pts[:,1])plt.xlabel('Offense PTS') # 设置x轴的labelplt.ylabel('Defense PTS') # 设置y轴的labelfor i in range(len(pts[:,0])):    plt.annotate(team_name[i], xy = (pts[i,0], pts[i,1]), xytext = (pts[i,0]+0.1, pts[i,1]+0.1)) # 这里xy是需要标记plt.show()

此时的效果就能满足我们的第一个需求:

对于第二个需求,我们使用plt.vlines和plt.hlines方法,对于plt.vlines方法,它是在图中绘制一条竖直线,因此需要指定x轴的坐标,同时需要指定y轴的起始坐标和结束坐标,对于plt.hlines方法,它是在图中绘制一条水平线,因此需要指定y轴的坐标,同时需要指定x轴的起始坐标和结束坐标:

代码语言:javascript
复制
offense_mean = np.mean(pts[:,0])defense_mean = np.mean(pts[:,1])print(offense_mean,defense_mean)plt.scatter(pts[:,0], pts[:,1])plt.vlines(np.mean(pts[:,0]), np.min(pts[:,1])-5,np.max(pts[:,1])+5,colors = "r", linestyles = "dashed")plt.hlines(np.mean(pts[:,1]), np.min(pts[:,0])-5,np.max(pts[:,0])+5,colors = "r", linestyles = "dashed")plt.ylim(np.min(pts[:,1])-5,np.max(pts[:,1])+5) # y轴的范围plt.xlim(np.min(pts[:,0])-5,np.max(pts[:,0])+5) # x轴的范围plt.xlabel('Offense PTS') # 设置x轴的labelplt.ylabel('Defense PTS') # 设置y轴的labelfor i in range(len(pts[:,0])):    plt.annotate(team_name[i], xy = (pts[i,0], pts[i,1]), xytext = (pts[i,0]+0.1, pts[i,1]+0.1)) # 这里xy是需要标记plt.show()

结果如下:

基于上面的散点图,我们就能很快将十六支球队分为四个象限。 1)第一象限:进攻好,防守差,包含的球队有金州勇士、费城76人、洛杉矶快船、布鲁克林篮网 2)第二象限:进攻差,防守差,包含的球队有底特律活塞、俄克拉荷马雷霆、圣安东尼奥马刺 3)第三象限:进攻差,防守好,包含的球队有休斯顿火箭、奥兰多魔术、印第安纳步行者、犹他爵士、多伦多猛龙、波士顿凯尔特人 4)第四象限:进攻好,防守好,包含的球队有密尔沃基雄鹿、波特兰开拓者、丹佛掘金

哈哈,是不是跟你想象的不太一样,比如火箭怎么能说是进攻差的球队呢!因为他们的对手爵士是全联盟常规赛防守第一的球队,这种因素我们是无法通过这种图分析出来的。也就是说,数据不能表明一切!

4、基于雷达图的火勇对决前瞻

勇士在今天的比赛中,凭借杜兰特的神奇表现,击败快船与火箭会师西部半决赛,这也是半决赛对决中最受人瞩目的。那么,我们通过雷达图来分析下两队在季后赛首轮的表现吧。这里要说明的是,我们的数据是在勇船第六场之前获取的,因此只有勇士五场的数据。

我们先来看看两队的基础数据,我们挑选了场均得分,场均失分、命中率、三分命中率、篮板、助攻、抢断、盖帽、失误等九项数据:

代码语言:javascript
复制
two_team_stats = mergedf[(mergedf['Team']=='Houston Rockets') | (mergedf['Team']=='Golden State Warriors')]hou_and_gsw_df = two_team_stats[['FG%','3P%','TRB','AST','STL','BLK','PTS','oppPTS','TOV']]print(hou_and_gsw_df)

结果如下,其中2代表勇士,7代表火箭:

接下来,我们想通过雷达图来对比一下二者的这九项数据,matplotlib里面的雷达图貌似必须是统一刻度的,至少我目前还没有找到如何设置为不同刻度。因此,我们首先将二者的数据,用统一的标准,转换到0-1区间内:

代码语言:javascript
复制
hou_and_gsw_data[:,0] = hou_and_gsw_data[:,0] / 0.55hou_and_gsw_data[:,1] = hou_and_gsw_data[:,1] / 0.55hou_and_gsw_data[:,2] = hou_and_gsw_data[:,2] / 55hou_and_gsw_data[:,3] = hou_and_gsw_data[:,3] / 40hou_and_gsw_data[:,4] = hou_and_gsw_data[:,4] / 12hou_and_gsw_data[:,5] = hou_and_gsw_data[:,5] / 12hou_and_gsw_data[:,6] = hou_and_gsw_data[:,6] / 130hou_and_gsw_data[:,7] = hou_and_gsw_data[:,7] / 130hou_and_gsw_data[:,8] = hou_and_gsw_data[:,8] / 20

绘制雷达图,使用的是极坐标系,因此,我们需要设置一下:

代码语言:javascript
复制
fig=plt.figure(figsize=(14,8))ax1=fig.add_subplot(1,1,1,polar=True) #设置第一个坐标轴为极坐标体系

随后,我们获取勇士和火箭的数据,并取得对应的数据标签:

代码语言:javascript
复制
gsw=hou_and_gsw_data[0,:] #提取GSW的信息hou=hou_and_gsw_data[1,:] #提取HOU的信息label=np.array([j for j in hou_and_gsw_df.columns]) #提取标签

随后,我们基于label的数量,对整圆进行切分,在切分的同时,我们需要加入切分后的第一个元素,以形成一个闭环:

代码语言:javascript
复制
angle = np.linspace(0, 2*np.pi, len(gsw), endpoint=False) #有几个label,就把整圆360°分成几份angles = np.concatenate((angle, [angle[0]])) #增加第一个angle到所有angle里,以实现闭合gsw = np.concatenate((gsw, [gsw[0]])) #增加gsw的第一项数据,以实现闭合hou = np.concatenate((hou, [hou[0]])) #增加hou的第一项数据,以实现闭合

随后,便可以绘制我们的雷达图:

代码语言:javascript
复制
ax1.set_thetagrids(angles*180/np.pi, label, fontproperties="Microsoft Yahei") #设置网格标签ax1.plot(angles,gsw,"o-",label='GSW')ax1.plot(angles,hou,"o-",label='HOU')ax1.set_theta_zero_location('NW') #设置极坐标0°位置ax1.set_rlim(0,1) #设置显示的极径范围ax1.fill(angles,gsw,facecolor='g', alpha=0.2) #填充颜色ax1.set_title("HOU VS GSW",fontproperties="SimHei",fontsize=16) #设置标题plt.legend(loc = 'best')plt.show()

结果如下:

从雷达图来看,火箭除了在抢断和失分上占据一定优势外,其他各项数据均落后于勇士。不过还是刚才的那句话,数据不能体现一切,希望火勇双方能给我们带来一场精彩绝伦的较量。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python绿色通道 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、数据获取及准备
  • 2、柱状图分析球队场均得失分
  • 3、散点图分析球队场均得失分
  • 4、基于雷达图的火勇对决前瞻
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档