python案例认为补时长短会影响进球概率吗?

wen python案例 2

本文目录导读:

python案例认为补时长短会影响进球概率吗?

  1. 分析思路
  2. Python 实现
  3. 核心结论(基于分析框架)

这是一个很有意思的足球数据分析问题,我来用Python做一个完整的案例分析,探讨补时长短是否会影响进球概率。

分析思路

关键问题是:补时越长,进球概率是否越高? 这涉及几个层次:

  1. 补时阶段本身的进球率 vs 常规时间进球率
  2. 补时长度与补时内进球数的关系
  3. 需要控制混杂因素(如比分差距、球队实力)

Python 实现

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
import statsmodels.api as sm
np.random.seed(42)
# ============================================================
# 1. 模拟数据(真实场景应替换为真实比赛数据)
# ============================================================
# 假设有 2000 场比赛,每场记录:补时分钟数、补时内进球数、比分差距等
n_matches = 2000
# 补时长度:1-8 分钟,少数更长(模拟真实分布)
added_time = np.random.choice(
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    size=n_matches,
    p=[0.05, 0.10, 0.20, 0.25, 0.20, 0.10, 0.05, 0.03, 0.01, 0.01]
)
# 基础进球率:每分钟约 0.03 球(约每 33 分钟一球)
base_rate_per_min = 0.03
# 关键假设:补时内进球率略高于常规(体力下降+拼命进攻)
# 但这里我们用"真实"生成过程来检验能否检测到效应
extra_intensity = 1.15  # 补时强度系数
# 生成补时内进球数(泊松过程)
lam = added_time * base_rate_per_min * extra_intensity
goals_in_added = np.random.poisson(lam)
# 控制变量:比分差距(落后方更拼命)
score_diff = np.random.choice([0, 1, 2, 3], size=n_matches, p=[0.35, 0.35, 0.20, 0.10])
df = pd.DataFrame({
    'added_time': added_time,
    'goals': goals_in_added,
    'score_diff': score_diff
})
print("数据概览:")
print(df.describe())
print(f"\n补时内总进球: {df['goals'].sum()}, 场均: {df['goals'].mean():.3f}")
# ============================================================
# 2. 描述性分析:不同补时长度下的进球率
# ============================================================
print("\n" + "="*60)
print("不同补时长度下的场均进球数:")
print("="*60)
grouped = df.groupby('added_time')['goals'].agg(['count', 'mean', 'sum'])
print(grouped)
# 关键指标:每分钟进球率(消除补时长度本身的影响)
df['goals_per_min'] = df['goals'] / df['added_time']
print("\n不同补时长度下的每分钟进球率:")
print(df.groupby('added_time')['goals_per_min'].mean())
# ============================================================
# 3. 统计检验
# ============================================================
print("\n" + "="*60)
print("统计检验")
print("="*60)
# 检验1:补时长度与进球数的相关性
corr, p_corr = stats.pearsonr(df['added_time'], df['goals'])
print(f"补时长度 vs 进球数 相关系数: {corr:.4f}, p值: {p_corr:.4f}")
# 检验2:补时长度与每分钟进球率的关系(核心问题)
corr_rate, p_rate = stats.pearsonr(df['added_time'], df['goals_per_min'])
print(f"补时长度 vs 每分钟进球率 相关系数: {corr_rate:.4f}, p值: {p_rate:.4f}")
# 检验3:按补时长短分组,比较进球数
short = df[df['added_time'] <= 3]['goals']
long_ = df[df['added_time'] >= 6]['goals']
t_stat, p_t = stats.ttest_ind(short, long_)
print(f"\n短补时(≤3分钟) vs 长补时(≥6分钟) 进球数 t检验: t={t_stat:.3f}, p={p_t:.4f}")
# ============================================================
# 4. 泊松回归(控制混杂因素)
# ============================================================
print("\n" + "="*60)
print("泊松回归:控制比分差距后,补时长度对进球数的影响")
print("="*60)
# 注意:这里需要用 offset 因为补时越长,暴露时间越长
# 但我们想检验的是"单位时间进球率"是否随补时变化
# 两种建模方式:
# 模型A:直接预测进球数(需含 offset=log(added_time) 检验率是否恒定)
X = sm.add_constant(df[['added_time', 'score_diff']])
model_a = sm.GLM(df['goals'], X, family=sm.families.Poisson()).fit()
print("\n模型A:进球数 ~ 补时长度 + 比分差距")
print(model_a.summary().tables[1])
# 模型B:offset 模型,检验单位时间进球率是否随补时变化
# added_time 系数不显著 → 单位时间进球率不随补时长度变化
X_b = sm.add_constant(df[['added_time', 'score_diff']])
model_b = sm.GLM(df['goals'], X_b, family=sm.families.Poisson(),
                 offset=np.log(df['added_time'])).fit()
print("\n模型B:进球数 ~ 补时长度 + 比分差距 + offset(log补时)")
print("(added_time 系数检验:单位时间进球率是否随补时长度变化)")
print(model_b.summary().tables[1])
# ============================================================
# 5. 可视化
# ============================================================
fig, axes = plt.subplots(1, 3, figsize=(16, 4.5))
# 图1:补时长度分布与进球
ax = axes[0]
stats_by_time = df.groupby('added_time')['goals'].agg(['mean', 'sem'])
ax.errorbar(stats_by_time.index, stats_by_time['mean'],
            yerr=stats_by_time['sem'], marker='o', capsize=3, color='#2c7fb8')
ax.set_xlabel('补时长度(分钟)')
ax.set_ylabel('补时内场均进球')
ax.set_title('补时长度 vs 场均进球数')
ax.grid(alpha=0.3)
# 图2:每分钟进球率(关键图)
ax = axes[1]
rate_by_time = df.groupby('added_time')['goals_per_min'].agg(['mean', 'sem'])
ax.errorbar(rate_by_time.index, rate_by_time['mean'],
            yerr=rate_by_time['sem'], marker='s', capsize=3, color='#d95f0e')
ax.axhline(y=base_rate_per_min, color='gray', linestyle='--', label='常规时间基准')
ax.set_xlabel('补时长度(分钟)')
ax.set_ylabel('每分钟进球率')
ax.set_title('补时长度 vs 每分钟进球率\n(关键:是否随补时变化?)')
ax.legend()
ax.grid(alpha=0.3)
# 图3:不同比分差距下的关系(交互效应)
ax = axes[2]
for diff in [0, 1, 2, 3]:
    sub = df[df['score_diff'] == diff]
    m = sub.groupby('added_time')['goals'].mean()
    ax.plot(m.index, m.values, marker='o', label=f'分差={diff}')
ax.set_xlabel('补时长度(分钟)')
ax.set_ylabel('场均进球')
ax.set_title('按比分差距分层的补时长度效应')
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('added_time_analysis.png', dpi=100, bbox_inches='tight')
plt.show()
# ============================================================
# 6. 效应量估计
# ============================================================
print("\n" + "="*60)
print("效应量估计(泊松回归)")
print("="*60)
coef = model_b.params['added_time']
print(f"补时长度每增加1分钟,单位时间进球率的变化: {(np.exp(coef)-1)*100:.2f}%")
print(f"(若接近0且不显著 → 补时长度不影响单位时间进球率)")
# 补时的"边际"效应:每多1分钟补时,预期多进多少球
# 若单位进球率恒定 = λ,则 E[进球] = λ * 补时
# 但若检验发现率不变,则补时越长,进球总数越多(仅因暴露时间更长)

核心结论(基于分析框架)

回答你的问题:补时长短会影响"总进球概率",但不一定影响"单位时间进球率"

需要区分两个问题:

问题 答案 原因
补时更长 → 补时内总进球更多? 是 暴露时间更长,即使每分钟进球率不变,总进球数也线性增加
补时更长 → 每分钟进球率更高? 需要数据检验 这才是"补时长度是否改变进球强度"的真正问题

真实世界的混杂因素(必须控制)

  1. 比分差距:落后方会拼命进攻 → 补时长通常与胶着/落后局面相关,而这类比赛本就进球多
  2. 比赛强度:强强对话可能补时更长
  3. 选择偏差:补时决定本身可能受比赛进程影响(VAR、换人多的比赛补时长且进球多)

关键洞察

补时长度与进球之间是"内生"关系,直接比较长短补时的进球率会产生偏误,正确做法是:

  • 用 offset 泊松回归(offset=log(补时分钟))检验单位时间进球率是否随补时变化
  • 或用 每分钟进球率 作为因变量
  • 控制比分差距、球队实力等混杂变量

如果要用真实数据

建议数据源:football-data.co.uk、StatsBomb、FBref,记录每场补时分钟数、补时内进球、比分状态等,然后跑上面的回归,历史上英超补时改革(2023-24 赛季平均补时从 7 分钟增至 ~11 分钟)是天然的准实验,可以用 断点回归 / 双重差分 估计补时延长的因果效应。

需要我用真实示例数据结构完整演示,或补上 DID/断点回归的设计吗?

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