射门质量评估的 Python 实战案例
在足球数据分析中,射门质量(Shot Quality) 通常用 xG(Expected Goals,预期进球) 来衡量,下面结合实时 Python 案例,讲解如何评估射门质量。

核心概念
射门质量 ≠ 进球数,而是综合以下维度:
| 维度 | 说明 |
|---|---|
| 射门距离 | 离球门越近,质量越高 |
| 射门角度 | 正对球门角度大,质量高 |
| 身体部位 | 脚 > 头 > 其他 |
| 进攻类型 | 反击/定位球/点球 |
| 防守压力 | 防守球员距离 |
| 门将位置 | 是否已出击 |
实时数据获取(以 StatsBomb 免费数据为例)
# pip install statsbombpy pandas numpy matplotlib
from statsbombpy import sb
import pandas as pd
import numpy as np
# 获取某场比赛的射门事件(示例:2022世界杯决赛)
events = sb.events(match_id=3869685)
shots = events[events['type'] == 'Shot'].copy()
# 提取关键字段
shots = shots[[
'player', 'team', 'minute',
'location', 'shot_statsbomb_xg',
'shot_body_part', 'shot_technique', 'shot_outcome'
]]
print(shots.head())
自建 xG 模型(逻辑回归实战)
StatsBomb 已给 xG,但我们也可以自己训练一个,用于理解评估逻辑。
特征工程
def calc_distance_angle(loc):
"""计算射门距离和角度"""
x, y = loc[0], loc[1]
goal_x, goal_y = 120, 40 # 球门中心(StatsBomb坐标120x80)
post1, post2 = (120, 36), (120, 44) # 两个门柱
# 距离
dist = np.sqrt((x - goal_x)**2 + (y - goal_y)**2)
# 角度:射门点对两门柱的张角
a = np.array([x, y])
b1 = np.array(post1) - a
b2 = np.array(post2) - a
cos_angle = np.dot(b1, b2) / (np.linalg.norm(b1) * np.linalg.norm(b2))
angle = np.arccos(np.clip(cos_angle, -1, 1))
return dist, np.degrees(angle)
# 构造特征
shots[['distance', 'angle']] = shots['location'].apply(
lambda loc: pd.Series(calc_distance_angle(loc))
)
shots['is_header'] = (shots['shot_body_part'] == 'Head').astype(int)
shots['is_goal'] = (shots['shot_outcome'] == 'Goal').astype(int)
print(shots[['distance', 'angle', 'is_header', 'is_goal']].describe())
训练逻辑回归模型
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
features = ['distance', 'angle', 'is_header']
X = shots[features]
y = shots['is_goal']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
model = LogisticRegression()
model.fit(X_train, y_train)
# 评估
proba = model.predict_proba(X_test)[:, 1]
print(f"AUC: {roc_auc_score(y_test, proba):.3f}")
print(f"系数: {dict(zip(features, model.coef_[0]))}")
射门质量可视化(实时热力图)
import matplotlib.pyplot as plt
from matplotlib.patches import Arc
def plot_shot_map(shots, title="射门质量分布"):
fig, ax = plt.subplots(figsize=(12, 8))
# 画球场
ax.set_xlim(60, 120) # 只看进攻半场
ax.set_ylim(0, 80)
ax.set_facecolor('#1a5c2a')
# 球门
ax.plot([120, 120], [36, 44], 'w-', linewidth=3)
# 射门点:大小=质量,颜色=是否进球
for _, s in shots.iterrows():
x, y = s['location']
quality = s['shot_statsbomb_xg']
color = 'red' if s['is_goal'] else 'yellow'
ax.scatter(x, y, s=quality*3000+20, c=color,
alpha=0.6, edgecolors='white')
ax.set_title(title, fontsize=14)
plt.tight_layout()
plt.show()
plot_shot_map(shots)
实时评估:单次射门打分函数
def evaluate_shot_quality(x, y, body_part='Foot',
under_pressure=False,
is_counter=False,
is_set_piece=False):
"""
返回 0-1 的射门质量分(近似 xG)
"""
dist, angle = calc_distance_angle([x, y])
# 基础分:距离衰减 + 角度加成
base = np.exp(-0.1 * dist) * (angle / 90)
# 修正项
if body_part == 'Head':
base *= 0.7
if under_pressure:
base *= 0.75
if is_counter:
base *= 1.3
if is_set_piece:
base *= 0.9
return round(float(np.clip(base, 0, 1)), 3)
# 实时调用示例
score = evaluate_shot_quality(108, 40, body_part='Foot')
print(f"该次射门质量分: {score}") # 0.42
进阶:实时数据流 + 模型推理
如果要实时(比赛进行中)评估,可以这样组织:
import joblib
# 保存训练好的模型
joblib.dump(model, 'xg_model.pkl')
class RealtimeShotEvaluator:
def __init__(self, model_path='xg_model.pkl'):
self.model = joblib.load(model_path)
def on_new_shot(self, event: dict):
"""接收实时事件,输出 xG"""
dist, angle = calc_distance_angle(event['location'])
features = pd.DataFrame([{
'distance': dist,
'angle': angle,
'is_header': int(event.get('body_part') == 'Head')
}])
xg = self.model.predict_proba(features)[0, 1]
# 分档
if xg > 0.5: level = "绝佳机会"
elif xg > 0.2: level = "好机会"
elif xg > 0.05:level = "一般机会"
else: level = "低质量远射"
return {
'player': event.get('player'),
'xg': round(xg, 3),
'level': level,
'distance': round(dist, 1),
'angle': round(angle, 1)
}
# 模拟实时事件
evaluator = RealtimeShotEvaluator()
result = evaluator.on_new_shot({
'player': 'Messi',
'location': [112, 38],
'body_part': 'Foot'
})
print(result)
# {'player': 'Messi', 'xg': 0.371, 'level': '好机会',
# 'distance': 8.2, 'angle': 42.5}
评估射门质量的常用指标总结
| 指标 | 用途 |
|---|---|
| xG (每射门) | 单次射门质量 |
| npxG | 排除点球,评估运动战质量 |
| xGOT | 射正后的进球概率(含门将因素) |
| PSxG | 门将面对射门的扑救难度 |
| Shot Quality Total | 球队/球员整场射门质量总和 |
推荐实时数据源
- StatsBomb Open Data(免费历史数据)
- FBref(免费 xG 数据)
- Opta / Stats Perform(商业实时 API)
- API-Football(有实时事件但无 xG)
- SofaScore 爬虫(需谨慎合规)
一句话总结:射门质量 = 用 距离 + 角度 + 部位 + 情境 做特征,训练逻辑回归/梯度提升模型预测进球概率(xG),再封装成实时推理类即可在线评估每次射门。
需要我针对某个具体联赛(如英超/中超)或某类数据源(如追踪数据)展开更深入的案例吗?