本文目录导读:

这是一个很有意思的足球数据分析问题,我理解你想用Python来验证一个假设:短角球是否比传中(通常指直接开向禁区的角球)更有效。
由于我没有你手头的具体数据集,我先给你一个完整的分析框架和可运行的Python代码示例,你可以把真实数据替换进去。
问题定义
首先需要明确几个关键指标:
| 概念 | 定义 |
|---|---|
| 短角球 | 角球开出后传给近距离队友(lt;10-15米),而非直接吊入禁区 |
| 传中角球 | 角球直接开向禁区(禁区中路/后点等) |
| 有效性 | 可用多种指标衡量:射门率、进球率、预期进球(xG)、二次进攻形成率等 |
Python分析框架
import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import seaborn as sns
# ========== 1. 假设数据结构 ==========
# 真实数据可从 StatsBomb、Opta、FBref 等获取
# 每行代表一次角球
np.random.seed(42)
n = 1000
df = pd.DataFrame({
'corner_id': range(n),
'type': np.random.choice(['short', 'cross'], n, p=[0.25, 0.75]),
'team': np.random.choice(['A','B','C','D','E'], n),
'minute': np.random.randint(1, 90, n),
# 结果变量
'led_to_shot': np.random.binomial(1, 0.28, n), # 是否产生射门
'led_to_goal': np.random.binomial(1, 0.03, n), # 是否直接进球
'xg_generated': np.random.gamma(2, 0.05, n), # 产生的xG
})
# 让短角球略微"更有效"以模拟假设
mask = df['type'] == 'short'
df.loc[mask, 'led_to_shot'] = np.random.binomial(1, 0.34, mask.sum())
df.loc[mask, 'xg_generated'] = np.random.gamma(2, 0.06, mask.sum())
print(df.groupby('type')[['led_to_shot','led_to_goal','xg_generated']].mean())
统计检验
# ========== 2. 对比分析 ==========
summary = df.groupby('type').agg(
n=('corner_id','count'),
shot_rate=('led_to_shot','mean'),
goal_rate=('led_to_goal','mean'),
xg_per_corner=('xg_generated','mean')
).round(4)
print(summary)
# ========== 3. 卡方检验:射门率差异是否显著 ==========
short = df[df['type']=='short']['led_to_shot']
cross = df[df['type']=='cross']['led_to_shot']
contingency = pd.crosstab(df['type'], df['led_to_shot'])
chi2, p, dof, _ = stats.chi2_contingency(contingency)
print(f"\n卡方检验:chi2={chi2:.3f}, p={p:.4f}")
# ========== 4. t检验:xG差异 ==========
t, p_t = stats.ttest_ind(
df[df['type']=='short']['xg_generated'],
df[df['type']=='cross']['xg_generated']
)
print(f"xG独立样本t检验:t={t:.3f}, p={p_t:.4f}")
# ========== 5. 效应量(Cohen's h)==========
def cohens_h(p1, p2):
return 2*np.arcsin(np.sqrt(p1)) - 2*np.arcsin(np.sqrt(p2))
h = cohens_h(short.mean(), cross.mean())
print(f"Cohen's h = {h:.3f}")
可视化
# ========== 6. 可视化 ==========
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# 射门率对比
sns.barplot(data=df, x='type', y='led_to_shot', ax=axes[0],
errorbar='ci', palette='Set2')
axes[0].set_title('射门转化率')
axes[0].set_ylabel('Probability')
# xG分布
sns.violinplot(data=df, x='type', y='xg_generated', ax=axes[1], palette='Set2')
axes[1].set_title('每次角球产生的xG分布')
# 各球队差异
team_stats = df.groupby(['team','type'])['led_to_shot'].mean().unstack()
team_stats.plot(kind='bar', ax=axes[2], color=['#66c2a5','#fc8d62'])
axes[2].set_title('各队短角球 vs 传中效率')
axes[2].tick_params(axis='x', rotation=0)
plt.tight_layout()
plt.show()
进阶分析建议
如果做严肃研究,还需要考虑:
# ========== 7. 控制变量的回归模型 ========== import statsmodels.api as sm # 控制时间、球队、比分状态等混杂因素 df['is_short'] = (df['type']=='short').astype(int) X = pd.get_dummies(df[['is_short','minute','team']], drop_first=True) X = sm.add_constant(X) y = df['led_to_shot'] model = sm.Logit(y, X.astype(float)).fit() print(model.summary()) # 逻辑回归系数可解释为:控制其他变量后,短角球对射门概率的边际影响
关键混杂变量:
- 球队战术风格(有的队天生爱打短角球)
- 比分状态(落后时更可能传中冲抢)
- 对手身高/防空能力
- 比赛时间
- 角球方向(左/右)
真实数据的经验结论(供参考)
根据公开研究(如 StatsBomb、American Soccer Analysis、五三十八 的分析):
- 短角球射门率往往略高,因为它避免了第一点被解围,但单次xG通常更低——因为球最终仍要传中,且防守已回位。
- 传中角球进球率波动大,但第一点争顶成功时xG很高。
- 净效应:多数研究发现两者总xG差异不显著,短角球的优势主要体现在保持控球、减少被打反击,而非直接提升进球。
建议
如果你有具体数据(CSV/Excel),把列名告诉我,我可以帮你:
- 定制清洗和特征工程
- 跑出针对你数据的p值和效应量
- 用逻辑回归/XGBoost建模并做SHAP归因
你手头的数据是什么格式?来自哪个数据源(StatsBomb/Opta/FBref/自采)?