python案例如何解读后防线的xG against?

wen python案例 3

后防线 xG Against 解读:Python 实战案例

xG Against 是什么

xG Against(xGA)对手在你方球门前创造的预期进球总和,用来量化"你的防线让对手获得了多少高质量机会"。

python案例如何解读后防线的xG against?

  • xGA 越低 → 防线越稳固(不是靠门将神扑)
  • xGA 高但失球少 → 门将超神 or 运气好,不可持续
  • xGA 低但失球多 → 门将拖后腿 or 运气差

它比"失球数"更能反映防线的真实防守质量,因为它剥离了门将扑救和运气的干扰。


数据准备(用 understat 或 FBref)

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from understat import Understat
import asyncio
# 方式1:直接从 FBref 抓取(以英超为例)
def get_team_defense_data(url):
    tables = pd.read_html(url)
    # FBref 的 squad defensive 表通常是第 2 个
    df = tables[1]
    df.columns = df.columns.droplevel(0)  # 处理多层表头
    return df
url = "https://fbref.com/en/comps/9/defense/2023-2024-Premier-League-Stats"
df = get_team_defense_data(url)
# 关注字段
cols = ['Squad', 'MP', 'Goals Against', 'xGA', 'xGA/90', 'PSxG', 'PSxG-GA']
df = df[cols].dropna()
df['GA-xGA'] = df['Goals Against'] - df['xGA']  # 关键差值
print(df.sort_values('xGA').head(10))

核心解读维度

单队 xGA 排名(基础层)

df_sorted = df.sort_values('xGA/90')
fig, ax = plt.subplots(figsize=(10, 10))
colors = ['#2ecc71' if x < df['xGA/90'].mean() else '#e74c3c' for x in df_sorted['xGA/90']]
ax.barh(df_sorted['Squad'], df_sorted['xGA/90'], color=colors)
ax.axvline(df['xGA/90'].mean(), color='black', linestyle='--', label='联赛平均')
ax.set_xlabel('xGA per 90 (越低越好)')
ax.set_title('各队防线 xGA/90 排名')
plt.legend()
plt.tight_layout()
plt.show()

解读

  • 绿色 = 防线强于联赛平均
  • 红色 = 防线弱于联赛平均
  • 与积分榜对比能看出"防线是否是球队成绩驱动因素"

GA vs xGA 差值分析(门将层)

fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(df['xGA'], df['Goals Against'], s=80, alpha=0.7)
# 画 y=x 参考线
lim = max(df['xGA'].max(), df['Goals Against'].max()) + 5
ax.plot([0, lim], [0, lim], 'k--', alpha=0.5, label='实际=预期')
for _, row in df.iterrows():
    ax.annotate(row['Squad'], (row['xGA'], row['Goals Against']),
                fontsize=7, alpha=0.7)
ax.set_xlabel('xGA(对手预期进球)')
ax.set_ylabel('GA(实际失球)')
ax.set_title('防线 xGA vs 实际失球')
ax.legend()
plt.tight_layout()
plt.show()

解读图

  • 点在参考线下方 → 门将/运气省了球(PSxG-GA 为正,好门将)
  • 点在参考线上方 → 门将拖后腿(如 23/24 的 Onana 早期)
  • 点离参考线远 → 结果有较大随机性 or 门将影响大

区分"防线质量" vs "门将扑救"

# FBref 中的关键指标
# PSxG (Post-Shot xG) = 射正球后的预期进球(已排除封堵)
# PSxG-GA > 0 表示门将扑救表现好于预期
df['goalkeeping_impact'] = df['PSxG'] - df['Goals Against']
df['defense_quality'] = df['xGA'] - df['PSxG']  # 越小说明防线把对手射门质量压制得越低
fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(df['defense_quality'], df['goalkeeping_impact'],
           s=80, c=df['GA-xGA'], cmap='RdYlGn_r', edgecolor='k')
for _, row in df.iterrows():
    ax.annotate(row['Squad'], (row['defense_quality'], row['goalkeeping_impact']),
                fontsize=7)
ax.axvline(0, color='grey', lw=0.8)
ax.axhline(0, color='grey', lw=0.8)
ax.set_xlabel('防线质量(越右越差:让对手射门质量高)')
ax.set_ylabel('门将贡献(越上越好:扑救超预期)')
ax.set_title('防线 vs 门将:功劳归属')
plt.colorbar(ax.collections[0], label='GA - xGA')
plt.tight_layout()
plt.show()

四象限解读

门将好 门将差
防线差 靠门将硬撑 灾难区(丢球多)
防线好 顶级防守 门将拖后腿

时间序列:防守趋势

# 用 understat 抓逐场数据
async def get_match_xga(team):
    async with Understat('epl', '2023') as u:
        return await u.get_team_results(team)
matches = asyncio.run(get_match_xga('Arsenal'))
df_m = pd.DataFrame(matches)
df_m['datetime'] = pd.to_datetime(df_m['datetime'])
df_m = df_m.sort_values('datetime')
# 计算滚动 5 场 xGA 均值
df_m['xGA_rolling'] = df_m['xGA'].rolling(5).mean()
plt.figure(figsize=(12, 5))
plt.plot(df_m['datetime'], df_m['xGA_rolling'], marker='o')
plt.axhline(df_m['xGA_rolling'].mean(), color='red', linestyle='--')'阿森纳赛季防线 xGA 滚动趋势(5场)')
plt.ylabel('xGA(滚动均值)')
plt.tight_layout()
plt.show()

解读

  • 曲线上升 → 防线恶化(伤病?战术调整?)
  • 曲线下降 → 防线收紧(新援融入?阵型变化?)

进阶解读角度

对手强度调整

# 按对手强队/弱队分组
strong_teams = ['Man City', 'Arsenal', 'Liverpool', 'Aston Villa']
df['opponent_tier'] = df['Squad'].apply(
    lambda x: 'Strong' if x in strong_teams else 'Weak')
# 但 xGA 是累计的,需要拆逐场数据来算

真正的解读:把 xGA 按对手强度拆分——对强队时 xGA 表现如何,比总 xGA 更能反映防线的"抗压能力"。

分阶段(定位球 vs 运动战)

FBref 提供:

  • xGA - Set Pieces(定位球)
  • xGA - Open Play(运动战)
  • xGA - Penalty Kicks
  • xGA - Corners / Free Kicks
set_piece_xga = df[['Squad', 'xGA', 'Set Piece xGA']].copy()
set_piece_xga['sp_ratio'] = set_piece_xga['Set Piece xGA'] / set_piece_xga['xGA']
# 定位球 xGA 占比高的球队 → 防空/盯人训练不足

与 PPDA / 高位逼抢结合

# PPDA 越低 = 逼抢越凶 = 允许对手传球越少
# 高 PPDA + 低 xGA = 典型"低位防守"(如穆里尼奥风格)
# 低 PPDA + 低 xGA = 典型"高位逼抢"(如克洛普风格)

报告模板(三段式解读)

对一支球队的防线 xGA 解读:

总体水平:本赛季 xGA/90 = 1.15,联赛第 4 低,防线处于前列。

与失球对比:GA = 42,xGA = 41.3,GA-xGA ≈ +0.7,说明门将表现基本符合预期,防线成果真实。

分解:定位球 xGA 占比 28% 偏高,运动战 xGA 联赛前 3 低,说明开放比赛防守优秀,但定位球是短板,可针对训练防空。


常见误区

误区 正确理解
xGA 低 = 失球一定少 ❌ 门将差/运气差会导致 GA 高
看单场 xGA 就下结论 ❌ 有随机性,至少 5-10 场滚动
忽略对手强度 ❌ 对弱队刷低 xGA 没说服力
忽视定位球 ❌ FBref 分类数据要看

推荐工具组合

  • 数据源:FBref(免费、分类细)、Understat(逐场 xG)、StatsBomb(付费、最细)
  • Python 库pandas + matplotlib/plotly + mplsoccer(画图)
  • 可视化:雷达图对比多队、散点图看相关、热力图看分时段

如果你有具体某队/某赛季的数据,我可以帮你写一段针对性解读代码。

抱歉,评论功能暂时关闭!