全文链接:https://tecdat.cn/?p=41666

原文出处:拓端数据部落公众号

在体育数据分析领域不断发展的当下,数据科学家们致力于挖掘数据背后的深层价值,为各行业提供更具洞察力的决策依据。近期,我们团队完成了一项极具意义的咨询项目,旨在通过先进的数据分析手段,深入探究篮球比赛中犯规判罚的内在规律。

本专题文章围绕篮球犯规判罚数据展开研究,运用贝叶斯项目反应理论(Bayesian Item Response Theory,IRT),结合 PyMC 软件强大的建模能力,对官方 2015 - 2021 年篮球比赛相关报告数据进行了全面分析。在项目过程中,我们首先对原始数据进行细致的收集与处理,提取关键信息;接着构建拉施模型(Rasch Model),定义模型参数并利用 PyMC 实现模型搭建;随后通过后验采样与收敛检查确保模型的可靠性;最后对后验结果进行多维度分析,不仅发现了模型中部分池化的效果,还揭示了球员表现与位置之间的潜在关系,为篮球比赛的策略制定和裁判判罚研究提供了新的视角。

专题项目文件已分享在交流社群,阅读原文进群和 500 + 行业人士共同交流和成长。在这里,您可以与众多志同道合的数据爱好者、行业专家一同探讨数据分析在体育领域的应用,碰撞思维的火花,共同推动数据分析技术在各行业的发展与创新。

文章脉络:

在篮球比赛中,犯规判罚是一个重要且复杂的环节。每一次犯规判罚的背后,都涉及到不同角色球员之间的互动,比如犯规球员和被侵犯球员。而这些判罚结果不仅与球员的角色有关,还可能与每个球员自身的潜在能力相关。那么,如何研究这种涉及众多球员、具有层级结构的多主体互动场景呢?
贝叶斯项目反应理论结合现代强大的统计软件为我们提供了一种优雅且有效的建模选择。接下来,我们将详细介绍如何运用该理论对篮球犯规判罚数据进行分析。

动机

在篮球比赛中,我们观察到的是一个二元结果,即犯规是否被吹罚,这一结果来自于两个不同角色球员(犯规球员和被侵犯球员)在一次篮球进攻中的互动。而且,每个犯规或被侵犯的球员可能会在多次进攻中被观察到。所以,我们希望估计每个球员作为犯规或被侵犯球员时,其潜在能力对观察到的判罚结果的贡献。通过这种方式,我们可以对球员进行从高到低的排名,量化排名中的不确定性,并发现犯规判罚中涉及的额外层级结构。

拉施模型(Rasch Model)

我们的数据来源于官方的篮球比赛相关报告,涵盖了2015年到2021年期间的比赛数据。在这个数据集中,每一行数据代表一次进攻,涉及两名球员(犯规球员和被侵犯球员),并且记录了这次进攻中犯规是否被吹罚。
我们为每个球员定义了两个潜在变量:

  • theta:用于估计球员在被侵犯时被吹罚犯规的能力。
  • b:用于估计球员在犯规时不被吹罚犯规的能力。
    这两个参数的值越高,对该球员所在的球队越有利。我们使用标准的拉施模型来估计这两个参数,假设在第k次进攻中,裁判吹罚犯规的对数几率比等于相应球员的theta-b。同时,我们对所有的thetab设置了层级超先验,以考虑球员之间的共享能力以及不同球员的观察数量差异。

分析过程

我们的分析主要包括以下四个步骤:数据收集与处理、拉施模型的定义与实例化、后验采样与收敛检查、后验结果分析。

数据收集与处理

首先,我们从原始数据集中导入数据。我们只导入了五列数据,分别是:

  • committing:犯规球员的名字。
  • disadvantaged:被侵犯球员的名字。
  • decision:对这次进攻的判定结果,有四个取值:CNC(正确未吹罚)、INC(错误未吹罚)、IC(错误吹罚)、CC(正确吹罚)。
  • committing_position:犯规球员的位置,取值包括G(后卫)、F(前锋)、C(中锋)、G-FF-GF-CC-F
  • disadvantaged_position:被侵犯球员的位置,取值与犯规球员位置类似。
    我们已经从原始数据集中删除了涉及球员少于两名的进攻(比如走步或违例),并且原始数据集不包含球员位置信息,这是我们自己添加的。
    以下是部分数据导入的代码(AI提示词:使用Python的pandas库从指定路径或数据获取函数导入篮球犯规数据):
try: df_orig = pd.read_csv(os.path.join("..", "data", sponse_nba.csv"), index_col=0)except FileNotFoundError: df_orig = pd.read_csv(pm.get_data("itemnba.csv"), index_col=0)df_orig.head()

接下来,我们对数据进行三步处理:

  1. 创建一个数据框df,删除df_orig中的位置信息;同时创建一个数据框df_position,收集所有球员及其相应位置(这个数据框在笔记本的最后才会用到)。
  2. df添加一个名为foul_called的列,如果一次进攻中吹罚了犯规,则该列赋值为1,否则为0。
  3. 为犯规球员和被侵犯球员分配ID,并使用这个索引来识别每次观察到的进攻中的相应球员。
    以下是数据处理的部分代码(AI提示词:使用Python的pandas库对导入的篮球犯规数据进行处理,包括创建新数据框、添加新列、对球员进行索引等操作):

项目反应模型

模型定义

我们用以下符号表示:

  • (N_d)和(N_c)分别表示被侵犯球员和犯规球员的数量。
  • (K)表示进攻的次数。
  • (k)表示一次进攻。
  • (y_k)表示在第(k)次进攻中观察到的犯规判罚结果(吹罚或未吹罚)。
  • (p_k)表示在第(k)次进攻中吹罚犯规的概率。
  • (i(k))表示第(k)次进攻中的被侵犯球员。
  • (j(k))表示第(k)次进攻中的犯规球员。
    我们假设每个被侵犯球员由潜在变量(\theta_i)((i = 1,2,…,N_d))描述,每个犯规球员由潜在变量(b_j)((j = 1,2,…,N_c))描述。
    然后,我们将每个观察值(y_k)建模为独立伯努利试验的结果,其概率为(p_k),其中:
    [ p_k = \text{sigmoid}(\eta_k) = \left(1 + e{-\eta_k}\right){-1}, \quad \text{with} \quad \eta_k = \theta_{i(k)} - b_{j(k)} ]
    通过定义(使用非中心参数化):

先验/超先验为:
 

PyMC实现

我们使用PyMC实现上述模型。为了方便跟踪球员(因为我们有数百名犯规和被侵犯球员),我们使用pymc.Modelcoords参数。以下是模型实现的代码(AI提示词:使用Python的PyMC库实现上述定义的篮球犯规判罚的项目反应模型,设置好相关的先验、超先验、确定变量和似然等部分):

我们绘制模型以显示thetab变量的层级结构(和非中心参数化):

pm.model_to_graphviz(model)

采样与收敛

我们从拉施模型中进行采样:

with model: trace = pm.sample(1000, tune=1500, random_seed=RANDOM_SEED)

我们绘制获得的跟踪的能量差异。同时,我们假设采样器已经收敛,因为它通过了所有自动的PyMC收敛检查:

az.plot_energy(trace);

后验分析

部分池化的可视化

我们首先绘制每个被侵犯和犯规球员的原始平均概率(来自数据)与后验平均概率之间的差异((y)轴),以及每个被侵犯和犯规球员的观察数量((x)轴)。这些图表明,正如预期的那样,我们模型的层级结构倾向于将观察数量较少的球员的后验估计值拉向全局平均值。
以下是部分代码(AI提示词:使用Python的相关库对后验结果进行处理,计算原始平均概率与后验平均概率的差异,并绘制差异与观察数量的关系图):

# 全局后验均值的μ_theta和μ_bmu_theta_mean, mu_b_mean = trace.posterior["mu_theta"].mean(), 0# 每个被侵犯球员的数据原始均值disadvantaged_raw_mean = df.groupby("disadvantaged")["foul_called"].mean()# 每个犯规球员的数据原始均值committing_raw_mean = df.groupby("committing")["foul_called"].mean()# 每个被侵犯球员的后验均值disadvantaged_posterior_mean = ( 1 / (1 + np.exp(-trace.posterior["theta"].mean(dim=["chain", "draw"]))).to_pandas())# 每个犯规球员的后验均值committing_posterior_mean = ( 1 / (1 + np.exp(-(mu_theta_mean - trace.posterior["b"].mean(dim=["chain", "draw"])))).to_pandas())# 计算每个被侵犯和犯规球员的原始和后验均值的差异def diff(a, b):

表现最佳和最差的犯规与被侵犯球员

由于我们成功估计了被侵犯(theta)和犯规(b)球员的技能,我们可以检查哪些球员在我们的模型中表现更好或更差。我们使用森林图绘制后验结果,分别绘制了在潜在技能thetab方面排名前十和后十的球员。
以下是相关代码(AI提示词:使用Python的相关库对后验结果进行处理,根据潜在技能对球员进行排序,并绘制森林图展示排名前十和后十的球员的后验估计):

def order_posterior(inferencedata, var, bottom_bool): xarray_ = inferencedata.posterior[var].mean(dim=["chain", "draw"]) return xarray_.sortby(xarray_, ascending=bottom_bool)top_theta, bottom_theta = ( order_posterior(trace, "theta", False), order_posterior(trace, "theta", True),)top_b, bottom_b = (order_posterior(trace, "b", False), order_posterior(trace, "b", True))amount = 10 # 我们希望在每个类别中显示的顶级球员数量

通过查看相关研究,并对比其中使用不同赛季数据的拉施模型结果表格,我们发现,在我们使用更大数据集(涵盖2015 - 16到2020 - 21赛季)得到的模型中,一些在两种技能(thetab)上表现最佳的球员,仍然处于前十的排名。这表明我们的模型在不同数据规模下具有一定的稳定性和可靠性。

发现额外的层级结构

一个很自然的问题是,作为被侵犯球员表现出色(即theta值高)的球员,是否也有可能在作为犯规球员时同样表现出色(即b值高),反之亦然。接下来的两张图展示了在b(或theta)方面表现最佳的球员的theta(或b)得分情况。

amount = 20 # 我们希望显示的顶级球员数量top_theta_players = top_theta["disadvantaged"][:amount].valuestop_b_players = top_b["committing"][:amount].valuestop_theta_in_committing = set(committing).intersect

这些图表明,theta得分高与b得分高或低之间没有明显的相关性。而且,对篮球稍有了解的人可以从图中直观地发现,中锋或前锋球员的b得分往往比后卫或控球后卫更高。基于这一观察,我们决定绘制直方图,展示在表现最佳的被侵犯(theta)和犯规(b)球员中,不同位置的出现频率。

从直方图中我们可以看到,表现最佳的被侵犯球员中,后卫占比最大;而表现最佳的犯规球员中,中锋占比最大,同时后卫的占比非常小。这表明在我们的模型中,或许有必要添加一个层级结构。具体来说,可以按照球员的位置对被侵犯球员和犯规球员进行分组,以考虑位置因素在评估潜在技能thetab时的作用。在拉施模型中,可以通过为按位置分组的每个球员设置均值和方差超先验来实现这一点,感兴趣的读者可以自行尝试。

结论

在本次研究中,我们运用贝叶斯项目反应理论对篮球犯规判罚数据进行了深入分析。通过构建拉施模型,结合实际数据,我们成功估计了球员在犯规和被侵犯情况下的潜在技能。
从数据收集与处理,到模型的定义、实现、采样与收敛检查,再到后验结果的详细分析,每一个环节都紧密相连。在分析过程中,我们不仅观察到了模型中部分池化的效果,还通过对后验结果的可视化,发现了球员表现与位置之间的潜在关系。
这一研究成果充分展示了贝叶斯建模在处理复杂层级结构数据时的强大能力和高度灵活性,它能够帮助我们量化在引入特定问题知识时的不确定性。同时,本次研究也为后续进一步优化篮球犯规判罚分析模型提供了重要的思路和方向,例如考虑添加基于球员位置的层级结构等。未来,我们可以在此基础上,继续探索更多影响篮球犯规判罚的因素,不断完善相关模型,为篮球比赛的分析和决策提供更有价值的参考 。


拓端tecdat
198 声望54 粉丝