大家好,我是小F。
最近不少小伙伴都會熬夜看歐洲杯。今年的歐洲杯相比起往年的歐洲杯來說,可謂是冷門頻出,出乎意料。
真的不知道,第一會花落誰家~
本期小F就和大家分享一下,用Python和Matplotlib繪制一個足球運動員的數據可視化圖表。
來看一下C羅的情況,跟老詹一樣高齡,真的佩服。
數據來源于下面兩個網站,Understat和Fbref。
鏈接:https://understat.com/
鏈接:https://fbref.com/en/
歐洲足球五大聯賽,英超、意甲、西甲、德甲、法甲。
先看一下射門數據的可視化,本質上和籃球的出手點圖差不多,都是散點圖類型。
導入相關的Python庫。
import requests
from bs4 import BeautifulSoup as soup
import json
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
從Understat網站爬取射門數據,使用BeautifulSoup、JSON和pandas解析和處理數據。
# 請求數據, C羅的ID為2371
url = 'https://understat.com/player/2371'
html = requests.get(url)
# 解析處理數據
parse_soup = soup(html.content, 'lxml')
scripts = parse_soup.find_all('script')
strings = scripts[3].string
ind_start = strings.index("('")+2
ind_end = strings.index("')")
json_data = strings[ind_start:ind_end]
json_data = json_data.encode('utf8').decode('unicode_escape')
data = json.loads(json_data)
print(data)
# 處理數據, 包含射門位置、預期進球、射門結果、賽季
x = []
y = []
xg = []
result = []
season = []
for i, _ in enumerate(data):
for key in data[i]:
if key == 'X':
x.append(data[i][key])
if key == 'Y':
y.append(data[i][key])
if key == 'xG':
xg.append(data[i][key])
if key == 'result':
result.append(data[i][key])
if key == 'season':
season.append(data[i][key])
columns = ['X', 'Y', 'xG', 'Result', 'Season']
df_understat = pd.DataFrame([x, y, xg, result, season], index=columns)
df_understat = df_understat.T
df_understat = df_understat.apply(pd.to_numeric, errors='ignore')
# 得到最終的結果
print(df_understat)
此處的ID,通過查詢球員名字可知
查詢中國球員武磊,點擊訪問,在地址欄處,可以看到球員ID。
得到數據如下。
包含射門位置(x、y)、xG(預期進球)、射門結果、賽季。
其中x、y的坐標值為0~1之間,不適合在Matplotlib顯示,所以選擇放大100倍。
df_understat['X'] = df_understat['X'].apply(lambda x: x*100)
df_understat['Y'] = df_understat['Y'].apply(lambda x: x*100)
print(df_understat)
得到結果如下。
既然已經成功獲取Understat網站的數據,就可以去獲取Fbref網站的數據啦。
這里是球員的一些個人信息,以及賽季的平均數據。
比如全名、國家、位置、俱樂部、聯賽、年齡、出生年份、上場時間、得分數據等等。
因為網頁的數據是表格形式,所以直接使用pandas的read_html函數,解析表格爬取數據。
這個網站需要取消一下證書驗證,要不然連接不成功。
# 全局取消證書驗證
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
獲取球員的相關數據。
def readfromhtml(filepath):
# 選擇第二個表格
df = pd.read_html(filepath)[0]
column_lst = list(df.columns)
for index in range(len(column_lst)):
column_lst[index] = column_lst[index][1]
df.columns = column_lst
df.drop(df[df['Player'] == 'Player'].index, inplace=True)
df = df.fillna('0')
df.set_index('Rk', drop=True, inplace=True)
try:
df['Comp'] = df['Comp'].apply(lambda x: ' '.join(x.split()[1:]))
df['Nation'] = df['Nation'].astype(str)
df['Nation'] = df['Nation'].apply(lambda x: x.split()[-1])
except:
print('Error in uploading file:' + filepath)
finally:
df = df.apply(pd.to_numeric, errors='ignore')
return df
# 獲取2020-2021歐洲五大聯賽球員數據
df_fbref = readfromhtml('https://fbref.com/en/comps/Big5/shooting/players/Big-5-European-Leagues-Stats')
print(df_fbref)
得到結果如下。
數據都已經準備好了,那么我們就可以將數據繪制到圖表上。
# 安裝
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple mplsoccer
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple highlight_text
先安裝mplsoccer、highlight_text這兩個Python庫。
其中mplsoccer庫可以自定義繪制足球場,無需我們自己繪制場地圖。
想了解更多,可以訪問它的GitHub地址。
https://github.com/andrewRowlinson/mplsoccer
初始化一些設置,畫布背景色、字體顏色、默認字體,字體大小,此處選擇中文字體。
from highlight_text import ax_text,fig_text
import mplsoccer
# 背景色
background = '#D6DBD9'
# 字體顏色
text_color = 'black'
mpl.rcParams['xtick.color'] = text_color
mpl.rcParams['ytick.color'] = text_color
mpl.rcParams['text.color'] = text_color
# 中文字體
mpl.rcParams['font.family'] = 'Songti SC'
mpl.rcParams['legend.fontsize'] = 12
新建一個畫布。
# 新建畫布
fig, ax = plt.subplots(figsize=(10, 8))
# 關閉坐標軸
ax.axis('off')
# 背景色填充
fig.set_facecolor(background)
plt.show()
顯示如下。
繪制19-20賽季,C羅的進球情況。
# 垂直方向半個足球場
pitch = mplsoccer.VerticalPitch(half=True, pitch_type='opta', line_zorder=3, pitch_color='grass')
# 圖表大小
ax_opta1 = fig.add_axes((0.05, 0.06, 0.45, 0.4))
ax_opta1.patch.set_facecolor(background)
pitch.draw(ax=ax_opta1)
plt.show()
通過設置mplsoccer的參數,繪制半個足球場。
果然,左下方有半個足球場。
將射門數據用散點圖表示,分為進球得分和未成功進球得分兩種情況。
# 2019-2020賽季, C羅射門位置散點圖(未得分), 透明度0.6
df_fil = df_understat.loc[df_understat['Season'] == 2019]
pitch.scatter(df_fil[df_fil['Result'] != 'Goal']['X'], df_fil[df_fil['Result'] != 'Goal']['Y'],
s=np.sqrt(df_fil[df_fil['Result'] != 'Goal']['xG'])*100, marker='o', alpha=0.6,
edgecolor='black', facecolor='grey', ax=ax_opta1)
plt.show()
未得分射門散點圖。
得分散點圖。
# 2019-2020賽季, C羅射門位置散點圖(得分), 透明度0.9
pitch.scatter(df_fil[df_fil['Result'] == 'Goal']['X'], df_fil[df_fil['Result'] == 'Goal']['Y'],
s=np.sqrt(df_fil[df_fil['Result'] == 'Goal']['xG'])*100, marker='o', alpha=0.9,
edgecolor='black', facecolor='#6778d0', ax=ax_opta1, label='Goal')
plt.show()
結果如下,失敗的比成功的多。
這樣,我們就將C羅在2019-2020賽季的所有射門點數據可視化出來了。
其中散點的大小,是預期進球的大小。
添加標簽及圖例,設置相應的位置、文字、字體等設置。
# 添加圖例
ax_opta1.legend(loc='lower right').get_texts()[0].set_color("black")
# 文字信息
ax_opta1.text(30, 61, '得分次數 : '+str(len(df_fil[df_fil['Result'] == 'Goal'])), weight='bold', size=11)
ax_opta1.text(30, 64, f"預期進球 : {round(sum(df_fil['xG']),2)}", weight='bold', size=11)
ax_opta1.text(30, 58, '射門次數 : '+str(len(df_fil)), weight='bold', size=11)
ax_opta1.text(90, 60, '2019-20賽季', weight='bold', size=14)
plt.show()
成功添加附加信息。
顯示賽季、xG、得分次數、射門次數信息。
同樣將20-21賽季的數據繪制出來,放置在19-20賽季的右側。
# 2020-2021賽季, C羅射門位置散點圖
ax_opta2 = fig.add_axes((0.50, 0.06, 0.45, 0.4))
ax_opta2.patch.set_facecolor(background)
pitch.draw(ax=ax_opta2)
# 根據條件, 篩選數據
df_fil = df_understat.loc[df_understat['Season'] == 2020]
# 未得分
pitch.scatter(df_fil[df_fil['Result'] != 'Goal']['X'], df_fil[df_fil['Result'] != 'Goal']['Y'],
s=np.sqrt(df_fil[df_fil['Result']!='Goal']['xG'])*100, marker='o', alpha=0.6,
edgecolor='black', facecolor='grey', ax=ax_opta2)
# 得分
pitch.scatter(df_fil[df_fil['Result']=='Goal']['X'], df_fil[df_fil['Result'] == 'Goal']['Y'],
s=np.sqrt(df_fil[df_fil['Result'] == 'Goal']['xG'])*100, marker='o', alpha=0.9,
edgecolor='black', facecolor='#6778d0', ax=ax_opta2, label='Goal')
# 添加圖例, 文字信息
ax_opta2.legend(loc='lower right').get_texts()[0].set_color("black")
ax_opta2.text(30, 61, '得分次數 : '+str(len(df_fil[df_fil['Result'] == 'Goal'])), weight='bold', size=11)
ax_opta2.text(30, 64, f"預期進球 : {round(sum(df_fil['xG']),2)}", weight='bold', size=11)
ax_opta2.text(30, 58, '射門次數 : '+str(len(df_fil)), weight='bold', size=11)
ax_opta2.text(90, 60, '2020-21賽季', weight='bold', size=14)
plt.show()
結果如下。
C羅老當益壯啊,狀態一點也沒有下滑。
下面接著繪制所有球員的數據散點圖,看看C羅的數據能在哪一檔?
# 初始化
ax_scatter = fig.add_axes([0.52, 0.57, 0.4, 0.35])
ax_scatter.patch.set_facecolor(background)
plt.show()
創建一個坐標軸。
首先對數據進行篩選,上場時間最少要有900s,而且位置為前鋒此類的。
畢竟我們不能拿個守門員,跟C羅比數據吧,參考意義不大。
# 得到散點圖的X, Y坐標值
no_90s = 10
df_fil = df_fbref[df_fbref['90s'] >= no_90s]
# 前鋒位置
df_fil = df_fil[df_fil['Pos'].apply(lambda x: x in ['FW', 'MF,FW', 'FW,MF'])]
# 每90s預期進球和得分次數
x, y = (df_fil['xG']/df_fil['90s']).to_list(), (df_fil['Gls']/df_fil['90s']).to_list()
# 生成所有前鋒位置, 數據散點圖
ax_scatter.scatter(x, y, alpha=0.3, c='#EF8804')
plt.show()
所有球員每90s預期進球和得分次數的數據情況。
現在我們篩選出C羅的數據,在散點圖上用不同的顏色及透明度來突出顯示它。
# C羅的數據
df_player = df_fil[df_fil['Player'] == 'Cristiano Ronaldo']
ax_scatter.scatter(df_player['xG']/df_player['90s'], df_player['Gls']/df_player['90s'], c='blue')
plt.show()
結果如下。
可以看到C羅的數據還是比較高效的,雖不是第一,但也是前幾的存在。
最后給散點圖添加網格線,以及x軸和y軸標簽。
# 添加網格線及標簽
ax_scatter.grid(b=True, color='grey',
linestyle='-.', linewidth=0.5,
alpha=0.4)
ax_scatter.set_xlabel('每90秒的預期進球', fontdict={'fontsize': 12, 'weight': 'bold', 'color': text_color})
ax_scatter.set_ylabel('每90秒得分', fontdict=dict(fontsize=12, weight='bold', color=text_color))
plt.show()
結果如下。
不愧是C羅,在2020-21賽季幾乎每90秒就能進1顆球。
18年就已經有一個記錄!C羅成歷史第一位在90分鐘內每分鐘都有進球的球員。
最后添加文本信息,包含標題,C羅的頭像,場上位置、年齡、效力球隊。
此處使用hightlight-text庫,可以高亮文本。
# 添加C羅的頭像
ax_player = fig.add_axes([0.03, 0.53, 0.25, 0.45])
ax_player.axis('off')
im = plt.imread('ronaldo.png')
ax_player.imshow(im)
# 添加標題信息
fig_text(0.03, 0.94, "<克里斯蒂亞諾·羅納爾多(C羅)> 賽季數據", weight='heavy', size=19, highlight_textprops=[{'color': 'blue'}])
fig_text(0.25, 0.85, '位置: <邊鋒>',weight='bold', size=15, highlight_textprops=[{'color':'#EF8804'}])
fig_text(0.25, 0.81, '年齡: <36>',weight='bold', size=15, highlight_textprops=[{'color':'red'}])
# 添加俱樂部logo
ax_team = fig.add_axes([0.27, 0.55, 0.15, 0.15])
ax_team.axis('off')
im = plt.imread('FCJ.png')
ax_team.imshow(im)
# 添加備注
fig_text(0.07, 0.03, '本圖制作者:<小F> 數據來源:Fbref.com、Understat.com',
size=12, highlight_textprops=[{'color': '#EF8804'}], weight='bold')
plt.show()
C羅的頭像、效力的隊伍logo,都是小F自己制作的。
得到結果如下。
保存為圖片。
# 保存為圖片
plt.savefig('ronaldo_viz.png', dpi=300, facecolor=background)
看起來還不錯哦。
好了,本期的分享就到此結束了,有興趣的小伙伴可以自行去實踐學習。
使用到的代碼及文件都已上傳,公眾號回復「足球」即可獲取。
快給自己喜歡的足球運動員,也制作一個賽季數據面板吧!
原文:
https://mp.weixin.qq.com/s/MiHu4vnuu0mlhx8B5x6OqA
版權聲明:本文來自互聯網整理發布,如有侵權,聯系刪除
原文鏈接:http://www.freetextsend.comhttp://www.freetextsend.com/tiyuzhishi/6640.html