大家好,我是Jackpop。
今天来跟大家聊一下足球。
首先表明,我并不是一个足球爱好者。
我很少关注世界杯、欧冠这些名气较大的足球比赛,更不会去留意国足相关的内容。
虽然对于足球比赛竞技本身并不感兴趣,但是作为一个数据科学领域的开发人员,对比赛背后的数据还是充满着浓厚的兴趣。
数据科学是一种从数据中挖掘和捕捉有价值信息的方法,数据科学正在影响着许多领域,这也包括足球。
足球包含大量的数据,从个人到团队方面都有涉及。有了这些数据,我们就能以一种更有意义的方式了解比赛。
另外,对于球队来说,数据可以创造出指导决策的信息。
因此,团队可以找到赢得比赛的策略。
在这篇文章中,我将带你了解如何使用Python分析足球赛事数据。
在这里,我们将分析2018年国际足联世界杯德国和韩国的比赛。
不用多说,让我们开始吧!
数据获取
对于数据,我们将使用StatsBomb的数据。
StatsBomb是一家专门在足球领域工作的分析公司,他们提供了大量的足球数据,尤其是事件数据。
对于那些想学习足球分析的人来说,得益于StatsBomb已经公布了公开数据,能够节省很多获取数据的时间。
这些数据由已经结束的足球联赛的比赛组成,数据链接:https://github.com/statsbomb/open-data
注意:在获取数据时,请耐心等待,因为数据量真的很大。
数据探索
在你下载数据后,下一步是探索它。
数据的文件夹结构如下所示:
| LICENSE.pdf | README.md | +---data | | competitions.json | | | +---events | | 15946.json | | 15956.json | | 15973.json | | 15978.json | | 15986.json | | | +---lineups | | 15946.json | | 15956.json | | 15973.json | | 15978.json | | 15986.json | | | \---matches | +---11 | | 1.json | | 2.json | | | +---16 | | 1.json | | 2.json | | | +---doc | Open Data Competitions v2.0.0.pdf | Open Data Events v4.0.0.pdf | Open Data Lineups v2.0.0.pdf | Open Data Matches v3.0.0.pdf | StatsBomb Open Data Specification v1.1.pdf | \---img statsbomb-logo.jpg
还有一些文件夹,如事件(events)、阵容(lineups)和比赛(matches):
- 事件文件夹包含以JSON格式回顾比赛的文件
- 阵容文件夹包含每场比赛中各队的阵容
- 比赛文件夹包含每场比赛的比赛。它也被分为几个比赛的不同季节
那么,在数据里面有很多文件的情况下,我们怎样才能检索到一个特定的比赛呢?正如我之前提到的,我们将分析德国和韩国的世界杯比赛。
在下一步,我将告诉你如何检索数据。
数据检索
事件数据可以通过以下步骤来检索。
首先,我们打开competitions.json
文件。这个文件是访问StatsBomb数据第一步要做的事情。
这样做的原因是,我们需要比赛和赛季的ID,以便从中获取比赛的列表。
为了处理JSON文件,pandas库提供了通过使用read_json
函数将JSON文件读成DataFrame的函数:
import pandas as pd competition = pd.read_json('open-data/data/competitions.json') competition.head()
现在,你可以看到包含StatsBomb提供的所有比赛信息的行。
总之,这些数据中包括的比赛有西甲(西班牙联赛)、欧洲杯、国际足联世界杯(男子和女子)以及欧洲冠军联赛。
现在,我们想取有国际足联世界杯信息的那一行。
让我们用下面这行代码来过滤数据集。
# Get the FIFA World Cup competition[competition.competition_name == 'FIFA World Cup']
从上面可以看出,国际足联世界杯的比赛和赛季的ID分别是43和3。
现在让我们来访问包含ID的文件夹。
对于每个比赛,文件夹都是以比赛ID命名的,而每个文件夹都包含JSON文件。
每个文件都是以赛季ID为名称的附件。
现在让我们通过使用这几行代码来访问该文件:
import json with open('open-data/data/matches/43/3.json') as f: data = json.load(f) data
哇,你会发现,这是个很大的数据,而且读起来很混乱。
让我们首先通过使用循环来整理它。
在每一次迭代中,我们都要取得比赛的ID、球队的名字和分数:
with open('open-data/data/matches/43/3.json') as f: data = json.load(f) for i in data: print('ID:', i['match_id'], i['home_team']['home_team_name'], i['home_score'], '-', i['away_score'], i['away_team']['away_team_name'])
现在,它比以前更整洁了。
让我们来看看德国对韩国的比赛。
最后的比分是2-0,韩国是赢家(你可能不知道,德国和韩国之间的比赛是惊人的)。
这个结果也使得德国人在小组赛阶段就被淘汰出局,这是自1938年以来,德国第一次在第一轮比赛中被淘汰。
回到主题,德国对阵韩国的比赛ID是7567。
通过如下几行代码来访问这个文件:
with open('open-data/data/events/7567.json') as f: korger = json.load(f) korger
这比之前的数据要多得多。
为了便于我们分析,pandas库提供了json_normalize
函数,这个函数之所以如此强大,是因为它可以处理嵌套的JSON。
现在我们来写这几行代码:
# from pandas.io.json import json_normalize df = pd.json_normalize(korger, sep='_').assign(match_id="7567") df.head()
比以前更有可读性,接下来让我们从数据中创建一些可视化的东西。
数据可视化
我们可以创建的可视化之一是射门图。
在这张图上,我们想看看每支球队有多少次射门。此外,还想知道进球的几率有多大,我们称这种机会为预期进球。
为了创建可视化,我们需要首先获取事件数据。然后,根据事件名称来过滤数据。
在这种情况下,我们需要射门的相关数据:
shots = df[df.type_name == 'Shot'].set_index('id') shots.head()
在我们得到数据后,现在让我们来写这些代码,对数据进行可视化:
import numpy as np import matplotlib.pyplot as plt from FCPython import createPitch pitch_width = 120 pitch_height = 80 fig, ax = createPitch(pitch_width, pitch_height, 'yards', 'gray') home_team = 'South Korea' away_team = 'Germany' for i, shot in shots.iterrows(): x = shot['location'][0] y = shot['location'][1] goal = shot['shot_outcome_name']=='Goal' team_name = shot['team_name'] circle_size = 2 circle_size = np.sqrt(shot['shot_statsbomb_xg'] * 15) if team_name == home_team: if goal: shot_circle = plt.Circle((x, pitch_height-y), circle_size, color='red') plt.text((x+1), pitch_height-y+1, shot['player_name']) else: shot_circle = plt.Circle((x, pitch_height-y), circle_size, color='red') shot_circle.set_alpha(.2) elif team_name == away_team: if goal: shot_circle = plt.Circle((pitch_width-x, y), circle_size, color='blue') plt.text((pitch_width-x+1), y+1, shot['player_name']) else: shot_circle = plt.Circle((pitch_width-x, y), circle_size, color='blue') shot_circle.set_alpha(.2) ax.add_patch(shot_circle) plt.text(5, 75, away_team + ' shots') plt.text(80, 75, home_team + ' shots') plt.title('Germany vs South Korea at 2018 FIFA World Cup') fig.set_size_inches(10, 7) fig.savefig('korger_shots.png', dpi=300) plt.show()
让我先解释一下代码。
首先,创建一个足球场。然后,还要创建一个点的集合,对应于已经进行的射击。
为了创建这个球场,我们可以使用FCPython中的createPitch
函数。
关于FCPython,你可以在这里查看[GitHub资源库](SoccermaticsForPython/FCPython.py at master · Friends-of-Tracking-Data-FoTD/SoccermaticsForPython · GitHub)。
为了生成圆点,我们需要遍历dataframe中的数据行。对于每一次迭代,需要做如下两件事情:
- 把坐标和预期目标(xG)值一起拿出来。
- 根据前面的参数生成一个圆。xG值将被用作圆圈大小的值,我们还为不是进球的射门设置透明度。
如果你的代码写得正确,它应该产生一个像下面这样的效果图:
现在我们可以从数据中得到启示。
正如我们从上面看到的,我们知道德国有很多的机会,但他们无法从中获得任何进球。
另外,他们有几次射门的xG值很大。xG值越大,进球的机会就越大。但不幸的是,德国人无法将其转换为进球。
在韩国方面,我们可以看到,他们没有很多机会。他们也不像德国人那样每次射门都有巨大的预期进球。
但是,在比赛结束时,德国队犯了一些错误,导致了一个尴尬的结果。最后,孙兴民和金英权成了韩国队的英雄。
结语
这是你可以创建的可视化之一,我们可以做的数据可视化有很多。
除此之外,我们可以做一个传球热图,或者进球过程中的控球链,或者每个球员的传球图。
正如我之前所说,足球赛事数据有很多值得挖掘的信息。
因此,这有助于团队和外面的足球爱好者更加了解这个比赛。
现在你已经学会了如何用Python分析StatsBomb的足球事件数据。
我希望它能激励你开始使用Python分析体育数据,特别是足球。