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

在这篇文章中,我介绍了如何从 stats.nba.com 上现场实况运动动画中提取一些额外的信息。

In [1]:

In [2]:

我们将会提取季后赛快船和火箭系列赛第 5 场比赛中一个回合的信息。在那个回合中,James Harden 突破到篮下,撕破快船的防守,然后传球给 Trevor Ariza,后者投入一个空位 3 分球。

我按照下面的方法嵌入运动动画。

In [3]:

Out[3]:

 

获取数据

通过下面的 URL,我们可以连接从 stats.nba.com API 得到的数据。在 URL 中有两个参数。eventid 是这个特定回合的 ID 号。gameid 是这场季后赛的 ID 号。

In [4]:

下面将会使用 requests 来获取数据

In [5]:

Out[5]:

我们想要的数据可以在 home(主场球员的数据)、visitors(客场球员的数据)和 moments(包含上面用来绘制球员运动动画信息的数据)中找到。

In [6]:

下面看一下字典 home 包含的信息。

In [7]:

Out[7]:

visitor 字典包含了同类信息,不过它是关于快船队的信息。

In [8]:

Out[8]:

下面看看 moments 列表的内容。

In [9]:

Out[9]:

从长度可知上述的动画是由 700 个项/时刻组成。但是这些时刻包括了什么信息呢?让我们看下第一个时刻。

In [10]:

Out[10]:

首先,在 moments 里的时刻或者项是一个包含一堆信息的列表。下面我们一个一个地查看列表里的项。

  1. moments[0] 的第一项是这个时刻发生的时期或者节(一场篮球比赛分为 4 节)
  2. 我不知道第二项代表什么。如果你知道的话请告诉我。
  3. 第三项是比赛用时钟剩下的时间。
  4. 第四项是投篮时限钟剩下的时间。
  5. 我不知道第五项代表什么。
  6. 第六项是由 11 个列表组成的列表,每个列表包含一个球员在球场中的坐标或者篮球的坐标。

1.这 11 个列表的第一个列表包含篮球的信息。

1.开始的两项代表 teamidplayerid 值,这两个值标识这个列表为篮球。
2.接下来的两项是 x 和 y 值,这两个值代表在球场上篮球的位置。
3.第五项代表篮球的半径。根据球的高度,这个值在动画的过程中始终是改变的。半径越大,球越高。所以如果一个球员投篮,球会变大,在投球弧线的顶点达到最大的尺寸,然后随着球下降,它的尺寸也变小。

2.列表第六项中,随后的 10 个列表代表球场上的 10 个球员。这些列表的信息与篮球的信息一样。

1.开始的两项是 teamid 和 playerid,这两个值标识这个列表为某个特定的球员。
2.接下来的两项代表球场上球员位置的 x 和 y 坐标值。
3.最后一项是球员的半径,该值无关紧要。

现在我们对 moments 数据的含义已有所了解,接下来将它放到 pandas DataFrame 中。

首先我们为 DataFrame 创建列标签。

In [11]:

然后,我们单独创建一个列表,用于保存每个球员的 moments 数据。

In [12]:

In [13]:

Out[13]:

将我们最新创建的 moments 列表传进 pd.DataFrame,和列标签一起创建 DataFrame

In [14]:

In [15]:

Out[15]:

我们还没完成。我们应该添加包含球员名字和球衣号码的列。首先将所有球员放到一个列表中。

In [16]:

使用 players 列表,我们可以创建一个字典,其中球员 ID 是字典的键,包含球员名字和球衣号码的列表作为字典的值。

In [17]:

In [18]:

Out[18]:

更新 id_dict 来包括球的 id。

In [19]:

然后,在 player_id 列上使用 map 方法来创建一个 player_name 列和 player_jersey 列。我们将使用 lambda 创建一个匿名函数,该函数根据传进函数的 player_id 值返回正确的 player_name 和 player_jersey

换句话说,下面代码做的事情是在 player_id 列中迭代球员的 ID,然后将每个球员 ID 传进匿名函数。这个函数会返回与球员 ID 相关联的球员名和球衣号码,并且将那些值添加到 DataFrame

In [20]:

In [21]:

Out[21]:

绘制移动轨迹

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

In [22]:

In [23]:

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

In [24]:

In [25]:

计算移动距离

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

关于获取相邻点欧氏距离的链接

In [26]:

In [27]:

Out[27]:

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

In [28]:

Out[28]:

计算平均速度

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

In [29]:

Out[29]:

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

In [30]:

Out[30]:

计算球员之间的距离

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

首先获取 Harden 的位置。

In [31]:

In [32]:

Out[32]:

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

In [33]:

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

In [34]:

In [35]:

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

In [36]:

In [37]:

Out[37]:

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

In [38]:

Out[38]:

In [39]:

Out[39]:

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

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

In [40]:

Out[40]:

当 Harden 突破到篮框时,DeAndre Jordan 从 Dwight Howards 旁离开去防守篮框,而 Matt Barnes 轮换来挡住 Howards(但是摔倒了),留给 Ariza 空位。Harden 看到 Ariza,传球给他,在 Chris Paul 尝试冲过来防守的同时,Ariza 投篮了。所有的这些发生在从距离第三节结束还有 11:46 到 11:42 分钟期间,而投篮时限钟从 Harden 开始突破的 10.1 秒跑到 Ariza 投出篮球的 6.2 秒。实际上我们可以在 Ariza 的投篮日志页面中找到更多关于 Ariza 投篮尝试的信息。

In [41]:

从动画中,看上去 Harden 在进攻时间剩下大约 7.7 到 7.8 秒的时候传球的。我们可以仔细查看他和球之间的距离来确定。

In [42]:

In [43]:

下面将绘制在这期间,一些球员之间距离的变化。我们将会绘制 Harden 和 Jordan、Howard 和 Barnes、Ariza 和 Barnes 以及 Ariza 和 Paul 之间距离的变化。

In [44]:

In [45]:

In [46]:

In [47]:

我创建了一个小的 Python 模块,你可以在这里找到,里面包含了本文用过的一些函数。

打赏支持我翻译更多好文章,谢谢!

打赏译者

打赏支持我翻译更多好文章,谢谢!

1 6 收藏 1 评论

关于作者:Sam Lin

伪程序员 个人主页 · 我的文章 · 43 ·   

相关文章

可能感兴趣的话题



直接登录
最新评论
跳到底部
返回顶部