写点什么

2018 年世界杯德国竟然输给韩国?终于找到原因了!

作者:Jackpop
  • 2021 年 11 月 23 日
  • 本文字数:3917 字

    阅读完需:约 13 分钟

程序员宝藏库:https://github.com/Jackpopc/CS-Books-Store


大家好,我是 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 Cupcompetition[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 npimport matplotlib.pyplot as pltfrom FCPython import createPitch
pitch_width = 120pitch_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 分析体育数据,特别是足球。




大家好,我是 Jackpop!我花费了半个月的时间把这几年来收集的各种技术干货整理到一起,其中内容包括但不限于 Python、机器学习、深度学习、计算机视觉、推荐系统、Linux、工程化、Java,内容多达 5T+,获取方式:https://pan.baidu.com/s/1eks7CUyjbWQ3A7O9cmYljA(提取码:0000)

发布于: 2 小时前阅读数: 5
用户头像

Jackpop

关注

还未添加个人签名 2020.09.16 加入

公众号:平凡而诗意,微信:code_7steps,全网粉丝超20万,技术进阶、优质资源、实用工具,欢迎关注!

评论

发布
暂无评论
2018年世界杯德国竟然输给韩国?终于找到原因了!