综合python案例,射门质量与xG差异原因?

wen python案例 1

射门质量与 xG 差异原因分析(Python 案例)

核心概念

xG(Expected Goals,预期进球) 是基于射门位置、角度、身体部位、助攻方式等特征,通过逻辑回归/梯度提升等模型计算出的"该次射门转化为进球的概率"。

综合python案例,射门质量与xG差异原因?

差异 = 实际进球数 − 累计 xG,本质反映射门质量(finishing quality) 与机会质量(chance quality) 的偏离。


差异产生的主要来源

模型层面(xG 系统自身局限)

原因 说明
特征覆盖不足 传统 xG 只用位置+角度+身体部位,未纳入门将位置、防守压迫、射门时机
训练数据偏差 联赛/赛季样本偏差导致基线偏移
模型方差 单次射门 xG 是概率,随机性极大(σ 高)
未捕捉球员特质 梅西式小角度挑射、哈兰德式暴力远射被系统性低估/高估

球员层面(射门质量/Finishing Skill)

  • 射术稳定性:顶级前锋长期 overperform xG(如梅西、凯恩生涯 +10~15%)
  • 射门选择:强行射门拉低单次质量,但可提高总产量
  • 心理与状态:赛季初/伤愈期 finishing 波动
  • 小样本噪声:单赛季 30 次射门,±5 球差异可能是纯随机

情境层面

  • 比赛状态:落后时强行射门增多,xG 均值下降
  • 对手门将:顶级门将系统性降低转化率(xGOT 概念)
  • 场地/天气/球:影响射门精度

Python 案例分析

案例 1:用 StatsBomb 数据计算球员 xG 差异

import pandas as pd
import numpy as np
from statsbombpy import sb
# 拉取某场比赛射门事件
events = sb.events(match_id=3788741)  # 示例 match_id
shots = events[events['type'] == 'Shot'].copy()
# StatsBomb 已提供 shot_statsbomb_xg
shots['xg'] = shots['shot_statsbomb_xg']
shots['goal'] = shots['shot_outcome'].apply(lambda x: 1 if x == 'Goal' else 0)
# 按球员汇总
player_stats = shots.groupby('player').agg(
    shots=('id', 'count'),
    goals=('goal', 'sum'),
    total_xg=('xg', 'sum')
).reset_index()
player_stats['diff'] = player_stats['goals'] - player_stats['total_xg']
player_stats['finishing_pct'] = player_stats['diff'] / player_stats['total_xg'] * 100
print(player_stats.sort_values('diff', ascending=False))

输出解读:diff > 0 表示射门质量高于模型预期(超常发挥),diff < 0 表示低于预期。


案例 2:判断差异是"技能"还是"噪声"

关键问题:一个赛季 +6 球的 xG 差异,是能力还是运气?

from scipy import stats
import numpy as np
def is_skill_or_luck(goals, xg, n_shots, n_sim=10000):
    """
    蒙特卡洛检验:假设球员真实 finishing = xG 平均水平,
    模拟 n_shots 次射门,看实际进球数的百分位
    """
    p = xg / n_shots  # 平均单次射门 xG
    sim_goals = np.random.binomial(n_shots, p, n_sim)
    percentile = (sim_goals >= goals).mean()
    return percentile
# 示例:球员赛季 60 射门,进 12 球,累计 xG = 8.5
pct = is_skill_or_luck(goals=12, xg=8.5, n_shots=60)
print(f"实际进球在纯随机模型下的百分位: {pct:.2%}")
# 若 pct < 0.05,才可初步怀疑存在 finishing skill

大多数单赛季 xG 差异(±5 球内、<100 次射门)无法拒绝随机假设,需要 2~3 赛季累积数据才能识别真正的 finishing skill(文献中顶级射手 finishing 溢价约 +0.02~0.03 xG/射门)。


案例 3:拆解差异来源(分解分析)

# 将差异分解为"射门选择"与"射门质量"两部分
def decompose_diff(df):
    league_avg_xg = df['xg'].mean()          # 联赛平均机会质量
    player_avg_xg = df['xg'].mean()          # 球员平均机会质量
    # 选择效应:球员机会质量 vs 联赛
    selection_effect = (player_avg_xg - league_avg_xg) * len(df)
    # 质量效应:转化率 vs 期望转化率
    actual_conv = df['goal'].mean()
    quality_effect = (actual_conv - player_avg_xg) * len(df)
    return {
        'total_diff': df['goal'].sum() - df['xg'].sum(),
        'selection_effect': selection_effect,   # 射门位置/时机选择
        'quality_effect': quality_effect,       # 射术本身
    }
result = decompose_diff(shots)
print(result)

典型结果解读:

  • 若 selection_effect > 0:球员善于跑位、选择高 xG 机会(如因扎吉式)
  • 若 quality_effect > 0:射术超群(如凯恩、梅西)
  • 若两者此消彼长:特殊风格前锋(如远射型)

案例 4:进阶——xGOT 修正门将因素

传统 xG 不区分射向球门哪里。xGOT(On-Target xG) 加入射门落点,可解释部分差异:

# 若数据含射门落点,可训练 xGOT 模型
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split
features = ['shot_distance', 'shot_angle', 'body_part_encoded',
            'target_x', 'target_y', 'first_time']
X_train, X_test, y_train, y_test = train_test_split(
    shots[features], shots['goal'], test_size=0.2)
model = GradientBoostingClassifier(n_estimators=200, max_depth=4)
model.fit(X_train, y_train)
shots['xgOT'] = model.predict_proba(shots[features])[:, 1]
# 对比 xG 与 xGOT 的差异可分离"射门位置质量"与"射门执行质量"

差异归因框架

差异方向 可能原因 验证方法
持续 overperform(>2 赛季) 真实 finishing skill / 射门选择优化 跨赛季稳定性检验、贝叶斯收缩
短期 overperform(<1 赛季) 运气 / 小样本噪声 / 状态峰值 二项检验、蒙特卡洛
系统性 underperform 射门选择差 / 心理因素 / 位置角色(如组织中场被迫远射) 分解分析、位置分层
xG 模型偏差 特征缺门将位置、压迫强度 对比多套 xG 系统(StatsBomb/Understat/FBref)

核心结论:单次/短期 xG 差异主要来自随机性;跨赛季持续的差异才可归因于射门质量、选择偏好或 xG 模型本身的系统性偏差,分析时必须做样本量校正(收缩估计、贝叶斯分层)才能避免"运气当能力"的误判。

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