本文目录导读:

- 案例目标
- 所需Python库
- 第一步:构建模拟射门数据集
- 第二步:计算xG(预期进球)
- 第三步:定义“射门质量”指标
- 第四步:计算“差异指标”(核心分析)
- 第五步:数据可视化(找出差异原因)
- 第六步:深入分析差异原因
- 最终结论与业务解读
- 如何改进这个分析?
这是一个非常专业的足球数据分析问题,要分析“射门质量”与“预期进球(xG)”之间的差异原因,核心在于理解:xG衡量的是“射门前那一刻”的得分概率,而射门质量衡量的是“射门执行”的好坏。
一个高xG但低质量的射门(比如打飞)意味着球员浪费了机会;一个低xG但高质量的射门(比如世界波)意味着球员创造了奇迹。
下面我将提供一个完整的Python分析案例,通过模拟和真实数据逻辑(如果数据可用)来定量分析这种差异。
案例目标
- 构建xG模型(基于射门位置)。
- 计算“射门质量分数”(基于射门角度、射正与否、射门部位)。
- 找出哪些射门的 实际结果(进球/射正)与 xG预测 严重不符。
- 可视化分析差异原因(远射世界波 vs 门前空门打飞)。
所需Python库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import brier_score_loss, log_loss
import warnings
warnings.filterwarnings('ignore')
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
第一步:构建模拟射门数据集
我们将生成一组包含关键特征的射门数据。
# 设置随机种子以保证可复现性
np.random.seed(42)
n_shots = 1000
# 1. 基本位置特征 (影响xG)
distance_to_goal = np.random.uniform(1, 35, n_shots) # 距离球门距离(码)
angle_to_goal = np.random.uniform(0, 90, n_shots) # 射门角度(度)
# 2. 射门质量特征 (影响实际结果)
# 射正与否 (0=射偏/被封堵, 1=射正)
on_target = np.random.binomial(1, 0.7, n_shots)
# 射门部位 (0=脚, 1=头)
header = np.random.binomial(1, 0.2, n_shots)
# 射门动作类型 (0=普通, 1=凌空/倒钩, 2=点球)
shot_type = np.random.choice([0, 1, 2], n_shots, p=[0.7, 0.2, 0.1])
# 防守压力 (0=无压力, 1=有干扰, 2=贴身)
pressure = np.random.choice([0, 1, 2], n_shots, p=[0.3, 0.5, 0.2])
# 3. 实际结果 (进球)
# 概率随距离增加而下降,随角度增加而下降,随压力增加而下降
goal_prob_base = 0.3 * np.exp(-distance_to_goal / 15) * np.cos(np.radians(angle_to_goal)) * (1 - pressure*0.15)
goal_prob = np.clip(goal_prob_base, 0, 1)
goal = np.random.binomial(1, goal_prob, n_shots)
# 组装DataFrame
df_shots = pd.DataFrame({
'distance': distance_to_goal,
'angle': angle_to_goal,
'on_target': on_target,
'header': header,
'shot_type': shot_type,
'pressure': pressure,
'goal': goal
})
print("数据集预览:")
print(df_shots.head())
print(f"\n总射门数: {len(df_shots)}")
print(f"总进球数: {df_shots['goal'].sum()}")
第二步:计算xG(预期进球)
为了演示,我们使用一个简化的逻辑回归模型来预测xG,在实际项目中,你会使用更复杂的模型(如XGBoost)。
from sklearn.linear_model import LogisticRegression
# 特征工程:选择用于xG模型的特征
features = ['distance', 'angle', 'header', 'shot_type']
# 训练一个简单的xG模型
X = df_shots[features]
y = df_shots['goal']
# 使用logistic回归(实际中会用更复杂的模型)
xg_model = LogisticRegression(C=1.0, solver='liblinear')
xg_model.fit(X, y)
# 预测xG值(概率)
df_shots['xG'] = xg_model.predict_proba(X)[:, 1]
print("\nxG模型系数:")
for feature, coef in zip(features, xg_model.coef_[0]):
print(f" {feature}: {coef:.4f}")
print(f"\nxG统计描述:")
print(df_shots['xG'].describe())
第三步:定义“射门质量”指标
这是一个自定义指标,这里我们基于射门执行的关键因素:
- 射正:这是最重要的质量指标。
- 射门角度:更刁钻的角度(靠近立柱)质量更高。
- 防守压力:在压力下完成的射门质量体现。
# 计算射门质量分数 (0-1分)
df_shots['quality_score'] = (
# 射正 +0.5分
df_shots['on_target'] * 0.5 +
# 角度奖励 (选择刁钻角度,假设45-65度是理想角度)
(1 - np.abs(df_shots['angle'] - 55) / 55) * 0.3 +
# 压力惩罚 (在强压下射门质量更高,但这里定义为体现难度)
(1 - df_shots['pressure'] * 0.1) * 0.2
)
# 归一化到0-1区间
df_shots['quality_score'] = (df_shots['quality_score'] - df_shots['quality_score'].min()) / \
(df_shots['quality_score'].max() - df_shots['quality_score'].min())
print("射门质量分数统计:")
print(df_shots['quality_score'].describe())
第四步:计算“差异指标”(核心分析)
这是最关键的部分,我们计算两个指标:
- xG - 实际进球 (xG_diff):衡量机会浪费(正数)或超常发挥(负数)。
- 质量 - xG (quality_vs_xg):衡量射门执行质量是否配得上机会质量。
# 核心差异指标
df_shots['xG_diff'] = df_shots['xG'] - df_shots['goal'] # 正=浪费,负=超常
df_shots['quality_vs_xg'] = df_shots['quality_score'] - df_shots['xG'] # 正=射得好,负=射得差
# 分类:射门质量与xG的关系
def classify_shot(row):
if row['quality_vs_xg'] > 0.3:
return '高质量低xG (世界波)'
elif row['quality_vs_xg'] < -0.3:
return '低质量高xG (浪费机会)'
elif abs(row['xG_diff']) > 0.3:
return 'xG与实际不符'
else:
return '正常'
df_shots['classification'] = df_shots.apply(classify_shot, axis=1)
print("\n射门分类统计:")
print(df_shots['classification'].value_counts())
第五步:数据可视化(找出差异原因)
散点图:位置 vs 射门质量
plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
scatter = plt.scatter(
df_shots['distance'],
df_shots['angle'],
c=df_shots['quality_vs_xg'],
s=df_shots['xG'] * 300, # 点的大小代表xG
cmap='coolwarm',
alpha=0.6,
edgecolors='black',
linewidth=0.5
)
plt.colorbar(scatter, label='质量 vs xG')
plt.xlabel('距离球门 (码)')
plt.ylabel('射门角度 (度)')'射门位置 vs 质量与xG差异')
plt.grid(True, alpha=0.3)
# 添加分类标签
for cat in ['高质量低xG (世界波)', '低质量高xG (浪费机会)']:
sub = df_shots[df_shots['classification'] == cat]
plt.scatter(sub['distance'], sub['angle'],
s=50, marker='o', label=cat, alpha=0.7)
plt.legend()
plt.tight_layout()
plt.show()
柱状图:各类别的关键特征对比
# 按分类统计平均特征
comparison = df_shots.groupby('classification').agg({
'xG': 'mean',
'quality_score': 'mean',
'distance': 'mean',
'angle': 'mean',
'on_target': 'mean',
'pressure': 'mean',
'goal': 'mean'
}).round(3)
print("\n不同射门类别的平均特征对比:")
print(comparison)
# 可视化
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
metrics = ['xG', 'quality_score', 'distance', 'angle']s = ['平均xG', '平均射门质量', '平均距离', '平均角度']
for ax, metric, title in zip(axes.flatten(), metrics, titles):
comparison[metric].plot(kind='bar', ax=ax, color=['#3498db', '#e74c3c', '#2ecc71', '#f39c12'])
ax.set_title(title, fontsize=12)
ax.set_ylabel(metric)
ax.set_xlabel('射门分类')
ax.tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.show()
差异原因分析表
# 找出最极端的案例
worst_shots = df_shots.nlargest(10, 'xG_diff')[['distance', 'angle', 'xG', 'quality_score', 'goal', 'classification']]
best_shots = df_shots.nsmallest(10, 'xG_diff')[['distance', 'angle', 'xG', 'quality_score', 'goal', 'classification']]
print("\n=== 最浪费机会的10次射门(高xG未进球) ===")
print(worst_shots.to_string(index=False))
print("\n=== 最精彩的10次射门(低xG进球) ===")
print(best_shots.to_string(index=False))
第六步:深入分析差异原因
从上面的分析中,我们可以系统性地总结出射门质量与xG差异的5大原因:
# 自动化原因分析
def analyze_discrepancy(df):
reasons = {}
# 1. 射正率差异
high_xg_missed = df[(df['xG'] > 0.3) & (df['goal'] == 0)]
low_xg_scored = df[(df['xG'] < 0.1) & (df['goal'] == 1)]
reasons['射正率'] = {
'高xG未进球': f"{len(high_xg_missed)}次, 其中射正率: {high_xg_missed['on_target'].mean():.1%}",
'低xG已进球': f"{len(low_xg_scored)}次, 其中射正率: {low_xg_scored['on_target'].mean():.1%}"
}
# 2. 距离影响
reasons['距离影响'] = {
'平均距离差': f"{low_xg_scored['distance'].mean():.1f}码 vs {high_xg_missed['distance'].mean():.1f}码"
}
# 3. 防守压力
reasons['防守压力'] = {
'高xG未进球平均压力': f"{high_xg_missed['pressure'].mean():.2f}",
'低xG已进球平均压力': f"{low_xg_scored['pressure'].mean():.2f}"
}
# 4. 射门动作类型
reasons['动作类型'] = {
'高xG未进球': high_xg_missed['shot_type'].value_counts().to_dict(),
'低xG已进球': low_xg_scored['shot_type'].value_counts().to_dict()
}
return reasons
reasons = analyze_discrepancy(df_shots)
print("\n=== 射门质量与xG差异原因深度分析 ===")
for category, detail in reasons.items():
print(f"\n [{category}]")
for key, value in detail.items():
print(f" {key}: {value}")
最终结论与业务解读
通过这个Python案例,我们可以得出以下关键结论:
-
射正率是最大变量:
- 高xG(如门前5码)但射偏 → 巨大负差异(浪费机会)。
- 低xG(如30码远射)但射正且角度刁钻 → 世界波(正差异)。
-
防守压力是关键调节变量:
- 相同xG下,压力更大时射门质量更难保证,但一旦进球就是“高质量”。
- 无压力下的高xG射门未进,通常是最大的“失分点”(需要重点训练)。
-
射门动作复杂性:
- 凌空抽射、倒钩等复杂动作,虽然xG低(因为位置差或角度小),但如果执行完美(高质量),进球概率远高于xG预测。
- 头球攻门,xG模型通常低估(因为模型多基于脚射门数据)。
-
模型局限性:
- 简单的xG模型(如Logistic回归)只考虑位置等静态特征,无法捕捉“射门瞬间的执行质量”。
- 真实业务中,需要引入射门速度、触球部位精度、门将位置等特征来缩小差异。
如何改进这个分析?
- 采用更真实的xG模型:使用XGBoost或神经网络,加入更多特征(如助攻类型、防守阵型、球员惯用脚等)。
- 引入时序数据:分析“射门前调整时间”。
- 球员个体差异:有些球员天生“逆xG”(如梅西在小角度进球多),有些球员稳定“低于xG”。
- 门将影响:加入门将的扑救能力(PSxG - 预期失球扑出数)来更精确分析射门质量。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播