如何运用Python绘制NBA投篮图表

简介:

0?wx_fmt=jpeg

我在本文中将介绍如何获取一个选手的投篮数据并通过matplotlib seaborn制成图表。

In [1]: %matplotlib inline

import requests

importmatplotlib.pyplot as plt

import pandas aspd

import seabornas sns


获取数据

stats.nba.com获取的数据是非常简单的。虽然NBA没有提供公共的API ,我们实际上可以通过requests 库来访问NBAstats.nba.com所使用的APIGreg Reda的这篇博客(http://www.gregreda.com/2015/02/15/web-scraping-finding-the-api/)详细讲解了如何访问这个API(或者找到与此类似的任何网页的API )。我们将使用下面程序中提到的网址来获得James Harden的投篮图表数据。



In [2]:shot_chart_url ='http://stats.nba.com/stats/shotchartdetail?CFID=33&CFPAR;'\

'AMS=2014-15&ContextFilter;=&ContextMeasure;=FGA&DateFrom;=&D;'\

'ateTo=&GameID;=&GameSegment;=&LastNGames;=0&LeagueID;=00&Loca;'\

'tion=&MeasureType;=Base&Month;=0&OpponentTeamID;=0&Outcome;=&'\

'PaceAdjust=N&PerMode;=PerGame&Period;=0&PlayerID;=201935&Plu;'\

'sMinus=N&Position;=&Rank;=N&RookieYear;=&Season;=2014-15&Seas;'\

'onSegment=&SeasonType;=Regular+Season&TeamID;=0&VsConferenc;'\

'e=&VsDivision;=&mode;=Advanced&showDetails;=0&showShots;=1&sh;'\

'owZones=0'


上述网址提供给我们的JSON文件包含了我们想要的数据。还要注意的是这个链接包含了用于访问数据的各种API参数。在这个链接中PlayerID参数设置为201935 ,这是James HardenPlayerID

现在让我们用requests来获取我们想要的数据。

In [3]: #获得包含数据的网页

response =requests.get(shot_chart_url)

# 提取用于作为我们表格中列的名称的题头

essay-headers =response.json()['resultSets'][0]['essay-headers']

# 提取投篮数据

shots =response.json()['resultSets'][0]['rowSet']


用提取的投篮数据制作一个 pandas DataFrame

In [4]: shot_df = pd.DataFrame(shots,columns=essay-headers)

# 查看数据表的题头以及所有列

fromIPython.display import display

withpd.option_context('display.max_columns', None):

display(shot_df.head())

0?wx_fmt=jpeg

以上投篮数据包含了所有的James Harden2014-15赛季常规赛期间的出手投篮。我们需要的数据在LOC_XLOC_Y 里面。这些坐标值对应每一次出手投篮,然后我们可以把这些坐标绘制到一组表示篮球场的轴上。

绘制投篮数据图

让我们只是快速输出数据来看看它的样子。


In [5]: sns.set_style("white")

sns.set_color_codes()

plt.figure(figsize=(12,11))

plt.scatter(shot_df.LOC_X,shot_df.LOC_Y)

plt.show()


0?wx_fmt=jpeg

请注意,上述图表歪曲了数据。 x轴的值是实际对应值的倒数。让我们只绘制从右侧的投篮图来看看这个问题。



In [6]: right =shot_df[shot_df.SHOT_ZONE_AREA == "Right Side(R)"]

plt.figure(figsize=(12,11))

plt.scatter(right.LOC_X,right.LOC_Y)

plt.xlim(-300,300)

plt.ylim(-100,500)

plt.show()

0?wx_fmt=jpeg

图上我们可以看到的投篮数据是“右侧”的投篮,而观众的右侧实际上是篮筐的左侧。这是在创建我们最后投篮图时需要注意修改的。


画出篮球场

首先我们需要弄清楚如何在我们的图表中绘制篮球场。通过查看输出的第一个投篮图和数据,我们可以大致估算出篮筐的中心位于原点。我们还可以估计每10个单位在xy轴上表示一英尺。我们可以通过看在DataFrame里的第一个观察值验证证这一点。这次上篮是从右侧底角3点,与LOC_X 226值处距离22英尺。所以这次投篮大约是在离篮筐22.6英尺处发生的。现在我们知道了这一点,就可以在图中画出篮球场了。


篮球场的尺寸可以从下面的图里找到。

0?wx_fmt=jpeg
0?wx_fmt=jpeg

利用这些维度,我们可以将它们转换成适用于我们图表的尺寸,并使用 Matplotlib Patches画出来。我们将使用圆形,矩形和圆弧来绘制篮球场。现在来创建我们绘制篮球场的方程。


注:虽然可以到使用Lines2D绘制线条,我发现使用Rectangles更方便(没有高度或宽度)。

修正( 201584日):我在绘制外场线和半场弧时犯了一个错误。外场线高度从不正确的442.5改为470。中心球场圆弧的中心的y值从395改到422.5 。图表中的ylim值从( 395 -47.5 )改变为( 422.5 -47.5 )。

In [7]: from matplotlib.patches importCircle, Rectangle, Arc

defdraw_court(ax=None, color='black', lw=2, outer_lines=False):

# 如果没有可绘制的坐标值,则使用现有值

if ax is None:

ax = plt.gca()

# 绘制NBA篮球场的各个部分

# 绘制篮筐

# 篮筐直径18英寸,所以半径为9英寸

# 也就是我们系统里面相应的的7.5

hoop =Circle((0, 0), radius=7.5, linewidth=lw, color=color, fill=False)

# 绘制篮板

backboard =Rectangle((-30, -7.5), 60, -1, linewidth=lw, color=color)

# 场地线

# 绘制外场场地线, =16ft, =19ft

outer_box = Rectangle((-80, -47.5), 160,190, linewidth=lw, color=color,

fill=False)

#绘制内场场地线, =12ft, =19ft

inner_box= Rectangle((-60, -47.5), 120, 190, linewidth=lw, color=color,

fill=False)

# 绘制罚球弧顶部

top_free_throw= Arc((0, 142.5), 120, 120, theta1=0, theta2=180,

linewidth=lw,color=color, fill=False)

# 绘制发球弧底部

bottom_free_throw= Arc((0, 142.5), 120, 120, theta1=180, theta2=0,

linewidth=lw,color=color, linestyle='dashed')

# 限制区,是一个以篮筐为中心,半径为4ft的弧

restricted = Arc((0, 0), 80, 80, theta1=0,theta2=180, linewidth=lw,

color=color)

# Three pointline

# 三分线

# Create theside 3pt lines, they are 14ft long before they begin to arc

# 绘制两边的三分线,它们有14ft 长,之后开始成弧形

corner_three_a = Rectangle((-220, -47.5),0, 140, linewidth=lw,

color=color)

corner_three_b = Rectangle((220, -47.5), 0,140, linewidth=lw, color=color)

# 3pt arc -center of arc will be the hoop, arc is 23'9" away from hoop

# 三分线弧形部分 以篮筐为中心 半径为 23'9" 的弧

# I just playedaround with the theta values until they lined up with the threes

# 我仅仅通过调整 theta值让它们与两边衔接上

three_arc = Arc((0, 0), 475, 475,theta1=22, theta2=158, linewidth=lw,

color=color)


# 场中心

center_outer_arc = Arc((0, 422.5), 120,120, theta1=180, theta2=0,

linewidth=lw,color=color)

center_inner_arc = Arc((0, 422.5), 40, 40,theta1=180, theta2=0,

linewidth=lw,color=color)


# 列出轴上要画的球场的元素

court_elements = [hoop, backboard,outer_box, inner_box, top_free_throw,

bottom_free_throw,restricted, corner_three_a,

corner_three_b,three_arc, center_outer_arc,

center_inner_arc]


if outer_lines:

# 画出半场线,底线和边线

outer_lines = Rectangle((-250, -47.5),500, 470, linewidth=lw,

color=color,fill=False) court_elements.append(outer_lines)

# 把球场部分元素加到轴上

for element in court_elements:

ax.add_patch(element)

return ax


让我们来画出球场吧!

In [8]:plt.figure(figsize=(12,11))

draw_court(outer_lines=True)

plt.xlim(-300,300)

plt.ylim(-100,500)

plt.show()



0?wx_fmt=jpeg

绘制投篮图

下面让我们根据球场的数据来绘制投篮图。以下有两种方式可以调整x值:一种是把LOC_X的负倒数传入plt.scatter;另一种是把降序的值传入plt.xlim。我们的选择是后者。

In [9]:plt.figure(figsize=(12,11))

plt.scatter(shot_df.LOC_X, shot_df.LOC_Y)

draw_court(outer_lines=True)

#值沿轴从左到右降序排列

plt.xlim(300,-300)

plt.show()

0?wx_fmt=jpeg

让我们将投篮图上的篮圈移至顶部,与stats.nba.com上随着镜头与统计图表的方向一致。通过从y轴底部到顶部的降序排列的y值,我们实现这个操作。当我们这样做了,便不再需要来调整我们图上的x值。

In [10]: plt.figure(figsize=(12,11))

plt.scatter(shot_df.LOC_X,shot_df.LOC_Y)

draw_court()

#把图的范围调整为半场

plt.xlim(-250,250)

# 沿 y轴从底部到顶部,t值降序排列

# 设置顶部为篮筐的位置

plt.ylim(422.5,-47.5)

#除去轴刻度标签

# plt.tick_参数(标签底部=, 标签左边=False)

plt.show()

0?wx_fmt=jpeg

让我们用seabornjointplot来绘制几幅投篮图

In [11]:# 创建jointplot

joint_shot_chart= sns.jointplot(shot_df.LOC_X, shot_df.LOC_Y, stat_func=None,

kind='scatter', space=0, alpha=0.5)


joint_shot_chart.fig.set_size_inches(12,11)


# 合成的投篮图有三个轴,第一个命名为ax_joint

# 是我们绘制场地和调整设置的轴

ax =joint_shot_chart.ax_joint

draw_court(ax)


#调整轴范围以便定位投篮图的方向

# 绘制半场图,设置顶部为篮筐的位置

ax.set_xlim(-250,250)

ax.set_ylim(422.5,-47.5)


#除去轴标签和刻度线

ax.set_xlabel('')

ax.set_ylabel('')

ax.tick_params(labelbottom='off',labelleft='off')


#添加标题

ax.set_title('JamesHarden FGA \n2014-15 Reg. Season',

y=1.2, fontsize=18)

#添加数据来源与作者

ax.text(-250,445,'DataSource: stats.nba.com'

'\nAuthor: Savvas Tjortjoglou(savvastjortjoglou.com)', fontsize=12)

plt.show()

0?wx_fmt=jpeg


获取选手头像

stats.nba.com网站上获取Jame Harden的头像,放在我们的图里。他的头像是这个:在http://stats.nba.com/media/players/230x185/201935.png,我们可以通过url.requests中的urlretrieve来为我们获取头像

In [12]: import urllib.request

#第一个参数为头像的链接

#第二个参数是我们想要获取的头像

pic =urllib.request.urlretrieve("http://stats.nba.com/media/players/230x185/201935.png",

"201935.png")

#urlretrieve以元组的形式返回我们需要的头像,imread函数以多维数组形式读取图像,这样matplotlib可以绘制图像。

harden_pic =plt.imread(pic[0])


#绘制图像

plt.imshow(harden_pic)

plt.show()

0?wx_fmt=jpeg

现在要在jointplot上绘制Harden的脸,我们将从matplotlib.Offset导入OffsetImageOffsetImage可以使头像出现在图的右上角。现在,让我们像刚才一样绘制投篮图,但是这次我们将先绘制KDE合成图,最后才添加头像。

In [13]: from matplotlib.offsetboximport OffsetImage

#绘制jointplot

#获取主KDE图的色图

#注意:我们可以从cmap中提取一种颜色用于图的边框和顶轴

cmap=plt.cm.YlOrRd_r


#n_levels 为主KDE图设置轮廓线的数量

joint_shot_chart= sns.jointplot(shot_df.LOC_X, shot_df.LOC_Y, stat_func=None,

kind='kde',space=0, color=cmap(0.1),

cmap=cmap,n_levels=50)


joint_shot_chart.fig.set_size_inches(12,11)


#一张合成图有3个轴,第一个是ax_joint,即球场线,也用于调整其它一些设置

ax =joint_shot_chart.ax_joint

draw_court(ax)


#调整轴的范围和方向角来绘制半场,

ax.set_xlim(-250,250)

ax.set_ylim(422.5,-47.5)


#除去轴标签和刻度线

ax.set_xlabel('')

ax.set_ylabel('')

ax.tick_params(labelbottom='off',labelleft='off')


#添加标题

ax.set_title('JamesHarden FGA \n2014-15 Reg. Season',

y=1.2, fontsize=18)


#添加数据源和作者信息

ax.text(-250,445,'DataSource: stats.nba.com'

'\nAuthor: Savvas Tjortjoglou(savvastjortjoglou.com)',

fontsize=12)

#在右上角添加Harden的照片

#首先上传头像,然后设置头像缩放比例

img =OffsetImage(harden_pic, zoom=0.6)


#把(xy)元组作为坐标信息,传入set_offset函数

#把图放置在你设想的地方

img.set_offset((625,621))


#添加头像

ax.add_artist(img)


plt.show()

0?wx_fmt=jpeg

另一个用hexbins绘制的合成图

In [14]:#绘制合成图

cmap=plt.cm.gist_heat_r

joint_shot_chart= sns.jointplot(shot_df.LOC_X, shot_df.LOC_Y, stat_func=None,

kind='hex',space=0, color=cmap(.2), cmap=cmap)


joint_shot_chart.fig.set_size_inches(12,11)


#合成图有3个轴,第一个轴是ax_joint,即球场线

ax =joint_shot_chart.ax_joint

draw_court(ax)

#调整轴的范围和方向角来绘制半场

ax.set_xlim(-250,250)

ax.set_ylim(422.5,-47.5)


# 除去轴标签和刻度线

ax.set_xlabel('')

ax.set_ylabel('')

ax.tick_params(labelbottom='off',labelleft='off')


# 添加标题

ax.set_title('FGA2014-15 Reg. Season', y=1.2, fontsize=14)


#添加数据源和作者信息

ax.text(-250,445,'DataSource: stats.nba.com'

'\nAuthor: Savvas Tjortjoglou',fontsize=12)


# Harden的照片放在右上角

img =OffsetImage(harden_pic, zoom=0.6)

img.set_offset((625,621))

ax.add_artist(img)


plt.show()

0?wx_fmt=jpeg

修改:根据Ogi010的建议,使用matplotlib包中翠绿色(Viridis)色图重新创建KDE画板


In [15]:#导入含翠绿色色图

from option_dimport test_cm as viridis

#设置翠绿色的色图

plt.register_cmap(cmap=viridis)

cmap =plt.get_cmap(viridis.name)


#n_levels设置主KDE图的轮廓线数量

joint_shot_chart= sns.jointplot(shot_df.LOC_X, shot_df.LOC_Y, stat_func=None,

kind='kde',space=0, color=cmap(0.1),

cmap=cmap,n_levels=50)


joint_shot_chart.fig.set_size_inches(12,11)


#合成图有3个轴,第一个轴是ax_joint,即球场线,也用来调整其它设置

ax =joint_shot_chart.ax_joint

draw_court(ax,color="white", lw=1)


#调整轴的范围和方向角来绘制半场

ax.set_xlim(-250,250)

ax.set_ylim(422.5,-47.5)


#除去轴标签和刻度

ax.set_xlabel('')

ax.set_ylabel('')

ax.tick_params(labelbottom='off',labelleft='off')


#添加标题

ax.set_title('JamesHarden FGA \n2014-15 Reg. Season',

y=1.2, fontsize=18)


#添加数据源和作者信息

ax.text(-250,445,'DataSource: stats.nba.com'

'\nAuthor: Savvas Tjortjoglou',fontsize=12)


#在右上角添加Harden的头像

#首先上传头像,然后调整头像大小以适合合成图

img =OffsetImage(harden_pic, zoom=0.6)

#将(xy)元组作为坐标信息传入set_offset函数

# to place theplot where you want, I just played around

#把图像放置在你设想的位置

img.set_offset((625,621))

#添加头像

ax.add_artist(img)


plt.show()

0?wx_fmt=jpeg

In [16]: importsys

print('Python version:', sys.version_info)

import IPython

print('IPython version:', IPython.__version__)

print('Requests verstion', requests.__version__)

print('Urllib.requests version', urllib.request.__version__)

import matplotlib as mpl

print('Matplotlib version:', mpl.__version__)

print('Seaborn version:', sns.__version__)

print('Pandas version:', pd.__version__)

Python version: sys.version_info(major=3, minor=4, micro=3,releaselevel='final', serial=0)

IPython version:3.2.0

Requestsverstion 2.7.0

Urllib.requestsversion 3.4

Matplotlibversion: 1.4.3

Seaborn version:0.6.0

Pandas version:0.16.2


原文发布时间为:2015-08-18

本文来自云栖社区合作伙伴“大数据文摘”,了解相关信息可以关注“BigDataDigest”微信公众号

相关文章
|
1月前
|
数据可视化 数据挖掘 UED
Python中的数据可视化:使用Matplotlib创建交互式图表
传统的数据可视化工具通常只能生成静态图表,而在数据分析和展示中,交互式图表能够提供更丰富的用户体验和更深入的数据探索。本文将介绍如何利用Python中的Matplotlib库创建交互式图表,让数据分析变得更加生动和直观。
|
1月前
|
Python
如何使用Python的Plotly库创建交互式图表?
如何使用Python的Plotly库创建交互式图表?
16 0
|
1天前
|
Python
如何使用Python的Plotly库创建交互式图表?
Plotly是Python的交互式图表库,支持多种图表类型,如折线图、散点图、柱状图。使用步骤包括安装库、导入模块、准备数据、创建图表对象、添加数据和设置属性,最后显示或保存图表。
15 6
|
4天前
|
机器学习/深度学习 算法 Python
python在Scikit-learn中用决策树和随机森林预测NBA获胜者
python在Scikit-learn中用决策树和随机森林预测NBA获胜者
14 0
|
1月前
|
数据采集 数据可视化 数据挖掘
Python中如何使用pandas和matplotlib库绘制图表
Python中如何使用pandas和matplotlib库绘制图表
32 0
|
1月前
|
Python
Python生成图表
Python生成图表
20 0
|
2月前
|
数据可视化 数据处理 Python
Python 与数据可视化的完美结合:使用 Matplotlib 和 Seaborn 制作图表
【2月更文挑战第1天】 数据可视化是现代数据科学中不可或缺的一环。Python 语言具有强大的数据处理和分析能力,加上丰富的可视化库,更是成为了数据可视化的首选工具。本文将介绍 Python 中两个常用的可视化库 Matplotlib 和 Seaborn,并通过实例演示它们的使用方法,帮助读者更好地理解数据可视化的思路和技巧。
|
2月前
|
数据可视化 数据挖掘 数据处理
Python数据可视化:利用Matplotlib打造精美图表
数据可视化在数据分析和展示中起着至关重要的作用,Matplotlib作为Python中最流行的数据可视化库之一,提供了丰富的绘图功能和灵活性。本文将介绍如何利用Matplotlib库来创建各种类型的精美图表,助力你更好地展示数据和分析结果。
|
2月前
|
数据采集 存储 数据可视化
微博数据可视化分析:利用Python构建信息图表展示话题热度
微博数据可视化分析:利用Python构建信息图表展示话题热度
|
2月前
|
数据可视化 Python
Python 绘制动态图表
在数据可视化中,动态图表能够更加生动直观地展示数据的变化趋势,使观众更好地理解和分析数据。Python 拥有许多强大的库,如`Matplotlib`、`Seaborn`和`Plotly`等,可以帮助我们创建各种类型的动态图表。