美国橄榄球联盟中裁判判罚方式的差异之数据可视化

引子

计算机最棒的一点在于,它们能够将表格数据转换成更直观的图形表示。常常令我费解的是,明明可以将数据转化为图形表达,但大部分人却往往放弃了这个可视化机会。

例如,在 ESPN 上有一篇关于不同裁判判罚方式之间的差异的论文。这篇论文的数据相当有趣, 包含了显示裁判判罚方式差异的数据。

然而,我发现了一件略为尴尬的事,作者企图将表格数据可视化,但最后并没有实现。这或许是因为作者不知道该如何对这些数据作出最好的可视化,来讲述他的数据故事。因此,我决定帮他达到这个目的!

给定一个前提,假设裁判的判罚方式之间的确存在差异。基于这个前提,我们想要弄明白,他们的判罚方式的差异究竟在哪里。

下面的数据是来自文章中的表格数据:

论文作者指出:

Jeff Triplette 团队迄今总共判决出了 81 次判罚,比判罚次数仅次于他们的团队高出 18 次,是另外两个团队的 2 倍多。

论文作者接下来便继续谈论了他与正在推销新书的 Mike Pereira 的会面。

虽然上面的表格很有用,但它并没有经过任何可视化操作,你看到了可能会问:“天哪,这到底说明了些什么?”。直觉上来说,表格中的数据在有些地方不太对劲...但是我又说不清楚是什么不对劲。

让我们加总一下各个裁判团队判罚的防守点球数(防守越位、侵犯和中立区犯规数),看看数据会变成什么样:

现在,我们可以看到这些数据可能向我们揭示了什么,但这对偏好图形的人来说,还是会有些理解上的困难。如果我们利用这些数据,生成散点图,那就达到利用图形来展示这个问题的目的了。我们可以通过以下代码,绘出散点图:

In [1]:

from bs4 import BeautifulSoup
import pandas as pd
import requests
import matplotlib.pyplot as plt
import scipy
import numpy

url = 'http://www.espn.com/blog/nflnation/post/_/id/225804/aaron-rodgers-could-get-some-help-from-referee-jeff-triplette'
r = requests.get(url)

tables = BeautifulSoup(r.text, 'lxml').find_all('table', class_='inline-table')

CrewName = []
DefOffside = []
Encroach = []
FalseStart = []
NeutralZone = []

for table in tables:
    for row in table.find_all('tr'):
        columns = row.find_all('td')
        try:
            CrewName.append(columns[0].text)
            DefOffside.append(int(columns[1].text))
            Encroach.append(int(columns[2].text))
            FalseStart.append(int(columns[3].text))
            NeutralZone.append(int(columns[4].text))
        except Exception as e:
            pass

print('| REFEREE | DEF. OFFSIDE | ENCROACH | FALSE START | NEUTRAL ZONE |')
print('| --- | --- | --- | --- | --- |')
for i in range(0, len(CrewName)):
    print('|'+CrewName[i]+ '|'+str(DefOffside[i])+ '|'+str(Encroach[i])+ '|'+str(FalseStart[i])+ '|'+str(NeutralZone[i])+ '|')

dic = {'Crew': CrewName, 'DefOffside': DefOffside, 'Encroach': Encroach, 'FalseStart': FalseStart, 'NeutralZone': NeutralZone}

Penalties = pd.DataFrame(dic)

Offensive = Penalties[['FalseStart']]
Deffensive = Penalties['DefOffside'] + Penalties['Encroach'] + Penalties['NeutralZone']

#for i in range(0, len(CrewName)):

N = len(CrewName)
x = Offensive
y = Deffensive

xMax = x.max()['FalseStart']
xMin = x.min()['FalseStart']
yMax = y.max()
yMin = y.min()

xMean = x.mean()['FalseStart']
yMean = y.mean()
xstd = x.std()['FalseStart']
ystd = y.std()

StdDevs = 2

plt.scatter(x,y)
plt.xlabel('Offensive Penalties')
plt.ylabel('Deffensive Penalties')
plt.title('Referee Crew Penalty Calls')

plt.axvline(x=xMean, ls="--")

graph_expansion = 15

borders = [(xMean-StdDevs*xstd)-graph_expansion, (xMean+StdDevs*xstd)+graph_expansion, (yMean-StdDevs*ystd)-graph_expansion, (yMean+StdDevs*ystd)+graph_expansion]
box = [(xMean-StdDevs*xstd), (xMean+StdDevs*xstd), (yMean-StdDevs*ystd), (yMean+StdDevs*ystd)]

plt.axhspan(ymin=box[2], ymax=box[3], xmin=(box[0] - borders[0]) / (borders[1] - borders[0]), xmax=(box[1] - borders[0]) / (borders[1] - borders[0]), facecolor='0.5', alpha= 0.25)


plt.axis(borders)

plt.axhline(y=yMean, ls="--")

for i in range(0, N):
    if x.ix[i]['FalseStart'] > xMean+StdDevs*xstd or y.ix[i] > yMean+StdDevs*ystd or x.ix[i]['FalseStart'] < xMean-StdDevs*xstd or y.ix[i] < yMean-StdDevs*ystd:
        plt.annotate(CrewName[i], (x.ix[i]['FalseStart'],y.ix[i]))
plt.show()
| REFEREE | DEF. OFFSIDE | ENCROACH | FALSE START | NEUTRAL ZONE |
| --- | --- | --- | --- | --- |
|Triplette, Jeff|39|2|34|6|
|Anderson, Walt|12|2|39|10|
|Blakeman, Clete|13|2|41|7|
|Hussey, John|10|3|42|3|
|Cheffers, Cartlon|22|0|31|3|
|Corrente, Tony|14|1|31|8|
|Steratore, Gene|19|1|29|5|
|Torbert, Ronald|9|4|31|7|
|Allen, Brad|15|1|28|6|
|McAulay, Terry|10|4|23|12|
|Vinovich, Bill|8|7|29|5|
|Morelli, Peter|12|3|24|9|
|Boger, Jerome|11|3|27|6|
|Wrolstad, Craig|9|1|31|5|
|Hochuli, Ed|5|2|33|4|
|Coleman, Walt|9|2|25|4|
|Parry, John|7|5|20|6|

上图中的蓝色水平虚线表示裁判的平均防守罚球点球数,蓝色竖直虚线表示裁判的平均侵犯判罚次数。灰色盒子表示由侵犯判罚次数和防守罚球点球数的 (μ−2σ,μ+2σμ−2σ,μ+2σ) 范围形成的区域。

是不是发现了什么?是的,我也注意到了。Jeff Triplette 团队在图上距离盒子如此之远,简直好像是他们裁判了一场假比赛一样,不过也有可能是他们看了假的裁判规则。

我希望我真正能做到的是,按照比赛规则,依据相同的分析过程,能对任一场比赛给出相应的分析。我并不指望这些分析会对 Jeff Triplette 团队以后的判罚方式产生什么影响,但不管怎样,这些分析向我们展示出了一些值得研究的异常数据点。

另外,你们能上我的 Github:https://github.com/miloardot/python-files/blob/master/Referees 找到这个项目的全部代码。

译者:胡帆

由 Editor 于 2017 年 08 月 23 日 发布在 数据科学 栏目