该内容已被发布者删除 该内容被自由微信恢复
文章于 2017年4月2日 被检测为删除。
查看原文
被用户删除
其他

如何用 Python 追踪 NBA 球员的移动轨迹 (2)

2015-10-02 伯乐在线 Python开发者

(点击上方公号,可快速关注)


昨天的文章《如何用 Python 追踪 NBA 球员的移动轨迹 (1)》中介绍了如何获得数据,接下来我们将介绍剩余的内容来完成最终的轨迹追踪功能!


绘制移动轨迹


下面我们通过动画绘制 James Harden 的移动轨迹。我们可以使用从 stas.nba.com 得到的动画上画好的球场来绘制球场。你可以在这里找到 SVG 图片。我将它转换成一个 PNG 文件,这样可以更容易地使用 matplotlib 来绘制。也要注意 x 或者 y 轴上的每 1 个单位表示篮球场上的 1 英尺。


# get Harden's movements

harden = df[df.player_name=="James Harden"]

# read in the court png file

court = plt.imread("fullcourt.png")



plt.figure(figsize=(15, 11.5))

# Plot the movemnts as scatter plot

# using a colormap to show change in game clock

plt.scatter(harden.x_loc, harden.y_loc, c=harden.game_clock,

cmap=plt.cm.Blues, s=1000, zorder=1)

# Darker colors represent moments earlier on in the game

cbar = plt.colorbar(orientation="horizontal")

cbar.ax.invert_xaxis()

# This plots the court

# zorder=0 sets the court lines underneath Harden's movements

# extent sets the x and y axis values to plot the image within.

# The original animation plots in the SVG coordinate space

# which has x=0, and y=0 at the top left.

# So, we set the axis values the same way in this plot.

# In the list we pass to extent 0,94 representing the x-axis

# values and 50,0 representing the y-axis values

plt.imshow(court, zorder=0, extent=[0,94,50,0])

# extend the x-values beyond the court b/c Harden

# goes out of bounds

plt.xlim(0,101)

plt.show()



我们也可以仅使用 matplotlib Patches 来重新创建球场的大部分。我们使用传统的笛卡尔坐标系,而不使用 SVG 坐标系,所以 y 值将会是负数,而不是正数。


from matplotlib.patches import Circle, Rectangle, Arc

# Function to draw the basketball court lines

def draw_court(ax=None, color="gray", lw=1, zorder=0):

if ax is None:

ax = plt.gca()

# Creates the out of bounds lines around the court

outer = Rectangle((0,-50), width=94, height=50, color=color,

zorder=zorder, fill=False, lw=lw)

# The left and right basketball hoops

l_hoop = Circle((5.35,-25), radius=.75, lw=lw, fill=False,

color=color, zorder=zorder)

r_hoop = Circle((88.65,-25), radius=.75, lw=lw, fill=False,

color=color, zorder=zorder)

# Left and right backboards

l_backboard = Rectangle((4,-28), 0, 6, lw=lw, color=color,

zorder=zorder)

r_backboard = Rectangle((90, -28), 0, 6, lw=lw,color=color,

zorder=zorder)

# Left and right paint areas

l_outer_box = Rectangle((0, -33), 19, 16, lw=lw, fill=False,

color=color, zorder=zorder)

l_inner_box = Rectangle((0, -31), 19, 12, lw=lw, fill=False,

color=color, zorder=zorder)

r_outer_box = Rectangle((75, -33), 19, 16, lw=lw, fill=False,

color=color, zorder=zorder)

r_inner_box = Rectangle((75, -31), 19, 12, lw=lw, fill=False,

color=color, zorder=zorder)

# Left and right free throw circles

l_free_throw = Circle((19,-25), radius=6, lw=lw, fill=False,

color=color, zorder=zorder)

r_free_throw = Circle((75, -25), radius=6, lw=lw, fill=False,

color=color, zorder=zorder)

# Left and right corner 3-PT lines

# a represents the top lines

# b represents the bottom lines

l_corner_a = Rectangle((0,-3), 14, 0, lw=lw, color=color,

zorder=zorder)

l_corner_b = Rectangle((0,-47), 14, 0, lw=lw, color=color,

zorder=zorder)

r_corner_a = Rectangle((80, -3), 14, 0, lw=lw, color=color,

zorder=zorder)

r_corner_b = Rectangle((80, -47), 14, 0, lw=lw, color=color,

zorder=zorder)

# Left and right 3-PT line arcs

l_arc = Arc((5,-25), 47.5, 47.5, theta1=292, theta2=68, lw=lw,

color=color, zorder=zorder)

r_arc = Arc((89, -25), 47.5, 47.5, theta1=112, theta2=248, lw=lw,

color=color, zorder=zorder)

# half_court

# ax.axvline(470)

half_court = Rectangle((47,-50), 0, 50, lw=lw, color=color,

zorder=zorder)

hc_big_circle = Circle((47, -25), radius=6, lw=lw, fill=False,

color=color, zorder=zorder)

hc_sm_circle = Circle((47, -25), radius=2, lw=lw, fill=False,

color=color, zorder=zorder)

court_elements = [l_hoop, l_backboard, l_outer_box, outer,

l_inner_box, l_free_throw, l_corner_a,

l_corner_b, l_arc, r_hoop, r_backboard,

r_outer_box, r_inner_box, r_free_throw,

r_corner_a, r_corner_b, r_arc, half_court,

hc_big_circle, hc_sm_circle]

# Add the court elements onto the axes

for element in court_elements:

ax.add_patch(element)

return ax


plt.figure(figsize=(15, 11.5))

# Plot the movemnts as scatter plot

# using a colormap to show change in game clock

plt.scatter(harden.x_loc, -harden.y_loc, c=harden.game_clock,

cmap=plt.cm.Blues, s=1000, zorder=1)

# Darker colors represent moments earlier on in the game

cbar = plt.colorbar(orientation="horizontal")

# invert the colorbar to have higher numbers on the left

cbar.ax.invert_xaxis()

draw_court()

plt.xlim(0, 101)

plt.ylim(-50, 0)

plt.show()



计算移动距离


我们可以通过获取相邻点的欧氏距离计算出一个球员的移动距离,然后添加那些距离。


def travel_dist(player_locations):

# get the differences for each column

diff = np.diff(player_locations, axis=0)

# square the differences and add them,

# then get the square root of that sum

dist = np.sqrt((diff ** 2).sum(axis=1))

# Then return the sum of all the distances

return dist.sum()


# Harden's travel distance

dist = travel_dist(harden[["x_loc", "y_loc"]])

dist


输出:


197.44816608512659


我们可以使用 groupby 和 apply 得到每个球员总的移动距离。以球员分组,获取他们每个人的坐标位置,然后 apply 上面的 distance 函数。


player_travel_dist = df.groupby('player_name')[['x_loc', 'y_loc']].apply(travel_dist)

player_travel_dist


输出:


player_name

Blake Griffin 153.076637

Chris Paul 176.198330

DeAndre Jordan 119.919877

Dwight Howard 123.439590

JJ Redick 184.504145

James Harden 197.448166

Jason Terry 173.308880

Josh Smith 162.226100

Matt Barnes 161.976406

Trevor Ariza 153.389365

ball 328.317612

dtype: float64


计算平均速度


计算一个球员的平均速度非常简单。我们只需以时间来划分距离。


# get the number of seconds for the play

seconds = df.game_clock.max() - df.game_clock.min()

# feet per second

harden_fps = dist / seconds

# convert to miles per hour

harden_mph = 0.681818 * harden_fps

harden_mph


输出 :


4.7977089702005902


我们可以使用之前创建的 player_travel_dist Series 来获取每个球员的平均速度。


player_speeds = (player_travel_dist/seconds) * 0.681818

player_speeds


输出:


player_name

Blake Griffin 3.719544

Chris Paul 4.281368

DeAndre Jordan 2.913882

Dwight Howard 2.999406

JJ Redick 4.483188

James Harden 4.797709

Jason Terry 4.211159

Josh Smith 3.941863

Matt Barnes 3.935796

Trevor Ariza 3.727143

ball 7.977650

dtype: float64


计算球员之间的距


下面将看下在比赛中,Harden 与其他每个球员之间的距离。


首先获取 Harden 的位置。

harden_loc = df[df.player_name=="James Harden"][["x_loc", "y_loc"]]

harden_loc.head()



现在让我们以 player_name 进行分组,并且获取每个球员和篮球的位置。


group = df[df.player_name!="James Harden"].groupby("player_name")[["x_loc", "y_loc"]]


我们可以利用 group 中 scipy 库的 euclidean 函数来 apply 一个函数。然后为每个球员返回一个列表,该列表包含比赛中 James Harden 与该球员之间的距离。


from scipy.spatial.distance import euclidean


输出:


# Function to find the distance between players

# at each moment

def player_dist(player_a, player_b):

return [euclidean(player_a.iloc[i], player_b.iloc[i])

for i in range(len(player_a))]


每个球员的位置以 player_a 传进 player_dist 函数中,而 Harden 的位置则以 player_b 传进该函数。


harden_dist = group.apply(player_dist, player_b=(harden_loc))

harden_dist


输出:


player_name

Blake Griffin [27.182922508363593, 27.055820685362697, 26.94...

Chris Paul [47.10168680005101, 46.861684798626264, 46.618...

DeAndre Jordan [16.413678482610162, 16.48314022711995, 16.556...

Dwight Howard [14.282883583198455, 14.35720390798292, 14.433...

JJ Redick [5.697440979685529, 5.683098128626677, 5.67370...

Jason Terry [51.685939334067434, 51.40228120171322, 51.096...

Josh Smith [44.06513224475787, 43.81023267813696, 43.5637...

Matt Barnes [37.5405670597302, 37.59395273374297, 37.68516...

Trevor Ariza [41.47340873263252, 41.414794206955804, 41.348...

ball [52.976156009708745, 52.70430545836839, 52.435...

dtype: object


注意到篮球的列表中只有 690 项,而球员的则有 700 项。


len(harden_dist["ball"])


输出:


690


输入:


len(harden_dist["Blake Griffin"])


输出:


700


现在我们知道如何得到球员之间的距离,接下来让我们试着看下 James Harden 突破到篮底是如何影响地板上的一些间距。


让我们再看看 moments 动画。然后仔细查看在 Harden 突破期间会出现什么。


IFrame('http://stats.nba.com/movement/#!/?GameID=0041400235&GameEventID=308',

width=700, height=400)



Python开发者

微信号:PythonCoder

可能是东半球最好的 Python 微信号

--------------------------------------

投稿网址:top.jobbole.com

商务合作QQ:2302462408

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存