之前发过一篇用python展示NBA球员出手位置偏好的视频:
有些同学想要其中的代码和讲解。再加上当时用的NBA官方接口现在极不稳定,几乎无法使用,所以我这里就再发一篇图文,简单讲解下代码,以及新的替代接口。
项目代码地址:
https://gitee.com/crossin/snippet/tree/master/nba-fpa
NBA 2018-19 赛季已经落下帷幕,猛龙击败勇士,成为新科冠军球队。(注:这篇文章是2019年写的)
我想,不如来做个 NBA 相关的数据分析案例好了。实际上,数据分析团队对于现在的 NBA 球队已经算是标配了,无论是在球员选择还是战术制定上都起着重要的作用。当下整个联盟越来越盛行的“魔球化”打法,其实也正是基于数据分析的产物。
于是,我们就借助官方数据和 Python 的绘图函数,来看一看不同的球员都是在什么位置出手投篮的。
0. 效果展示
照例先看结果,每个点是一次投篮,蓝色点是投中,红色点是未中。挑几个有特点的:
哈登,魔球得很明显了,长两分这种低效率的事绝对不干
西蒙斯
德拉赞,中投小王子
字母哥,篮下都给你扣糊了!
库里,这个得放全场……
科比,生涯累计图,全方位无死角,包括篮板后负角度
下面来看具体步骤,我们以林书豪作为例子。
1. 获取数据
NBA 有一个官方统计网站:stats.nba.com,上面有各种数据,可以说是一个宝藏了。
从网页上,我没有找到这次所需的数据。但通过一番搜索,我找到了网站的开放接口 API。相关的接口和文档你可以从这个项目里查看:
nba_py - stats.nba.com API for python
https://github.com/seemethere/nba_py/
通过以下接口,我们可以获取某个球员在指定赛季的投篮详细数据:
https://stats.nba.com/stats/shotchartdetail?CFID=33&CFPARAMS=2018-19&ContextFilter=&ContextMeasure=FGA&DateFrom=&DateTo=&GameID=&GameSegment=&LastNGames=0&LeagueID=00&Location=&MeasureType=Base&Month=0&OpponentTeamID=0&Outcome=&PaceAdjust=N&PerMode=PerGame&Period=0&PlayerID=201935&PlusMinus=N&Position=&Rank=N&RookieYear=&Season=2018-19&SeasonSegment=&SeasonType=Regular+Season&TeamID=0&VsConference=&VsDivision=&mode=Advanced&showDetails=0&showShots=1&showZones=0&PlayerPosition=
其中参数 PlayerID 是查询球员的 ID,这个 ID 可以通过在网页上搜索球员姓名,从对应的 URL 中获取。比如林书豪就是 202391
https://stats.nba.com/player/202391/
链接中的两处 2018-19 是赛季参数,你可以改成其他赛季,不过我试过不能太早,大约 96 年往前就没数据了,而且早期数据精度要差些。
其他参数可以不用管。
用 requests 库可轻松获取结果(需加上 headers):
response = requests.get(url, headers=headers,timeout=5)
注意:如果上述投篮详细数据你无法请求得到数据,说明此接口已失效。所以我又找了2个替代方案:
1. 使用别人下载好的数据:https://github.com/toddwschneider/nba-shots-db
2. 一个付费API服务:https://probasketballapi.com/docs/shotcharts 可以免费试用一周,拿来做个练习足够(但部分球员部分赛季数据有缺失)
这里我来讲下第2个方法。从上面的地址点进去可以看到接口和说明。注册一下,拿到 API_KEY 就可以请求了。
接口地址:
https://probasketballapi.com/shotcharts
参数:
player_id - 球员ID
season - 赛季
比如获取林书豪 2013-14 赛季:
2. 解析数据
返回的数据是 JSON 格式,用 pandas 转成 DataFrame 格式,方便后续处理:
data = r.json()
shot_df = pd.DataFrame(data)
我们关心的数据就是 loc_x、loc_y(出手位置)和 shot_made(是否投中)这三列。
3. 绘制投篮点
使用 matplotlib 库的散点图绘制 scatter 方法就可以把投篮点画出来,这个我们之前的各种案例里也用过不少次了:
made = shot_df[shot_df['shot_made']==1]
miss = shot_df[shot_df['shot_made']==0]
plt.scatter(miss.loc_x, miss.loc_y, color='r', marker='.', alpha=0.3)
plt.scatter(made.loc_x, made.loc_y, color='b', marker='.', alpha=0.3)
我们通过参数设定点的大小和透明度,让显示更清楚。
4. 绘制球场
投篮点画出来了,不过只是这样不够直观。我们再来加个球场的图。
这个是通过 matplotlib 里的 Circle、Rectangle、Arc 等方法拼出来的,没啥特别的技术,但代码比较繁琐,这边就不贴了。(只要你愿意,甚至可以用它画出小猪佩奇)
5. 添加头像
最后,再给数据图加上一个头像,让它看起来更完整。头像地址:
https://stats.nba.com/media/players/230x185/202391.png
或者
https://ak-static.cms.nba.com/wp-content/uploads/headshots/nba/latest/260x190/202391.png
文件名依然是球员的 ID,但这个地址并不是对所有球员有效,可做下异常处理。
pic = urllib.request.urlretrieve("http://stats.nba.com/media/players/230x185/202391.png")
# imread 读取的图像可以被 matplotlib 绘制
head_pic = plt.imread(pic[0])
# 将球员图片放置于右上角,并设置缩小等级以配合整个图片
img = OffsetImage(head_pic, zoom=0.6)
# (x, y) 控制将球员放在你想要放的位置
img.set_offset((540,640))
# 添加球员图片
fig.gca().add_artist(img)
如果在你的电脑上位置不对,记得修改下 offset 的值(不同系统,甚至不同编译器都可能存在差异)。
最终效果:
项目代码地址:
https://gitee.com/crossin/snippet/tree/master/nba-fpa
一起学,不孤单
圆老司 @ Crossin的编程教室