本文目录导读:

评估教练的换人得分能力,本质上是一个因果推断问题:你需要区分“换人确实带来了得分提升”和“换人只是运气好/时间巧合”,下面是一套可落地的脚本化评估框架,从数据准备到指标计算再到统计检验。
数据准备
所需数据表
| 表 | 字段示例 |
|---|---|
| 比赛事件表 | match_id, minute, event_type(goal/sub/injury), player_id, team_id |
| 换人表 | match_id, minute, player_out, player_in, team_id |
| 比分表 | match_id, minute, home_score, away_score |
| 球员表 | player_id, position, 进攻/防守能力评分 |
关键构造字段
# 对每次换人构造一个"换人事件窗口"
# 窗口定义建议:换人前 15 分钟 vs 换人后 15 分钟(可调)
sub_window = {
'pre_start': minute - 15,
'pre_end': minute,
'post_start': minute,
'post_end': minute + 15
}
核心因变量:换人后窗口内本方进球数 − 换人前窗口内本方进球数(净进球变化),或更细的 xG 变化。
核心评估指标
指标1:换人后净胜球变化(ΔGD)
def delta_goal_diff(match_events, sub, window=15):
pre = goals_in_window(sub.minute-window, sub.minute, team=sub.team_id)
post = goals_in_window(sub.minute, sub.minute+window, team=sub.team_id)
pre_against = goals_in_window(..., opponent=True)
post_against = goals_in_window(..., opponent=True)
return (post - post_against) - (pre - pre_against)
指标2:换人后 xG 增量
用事件数据(射门位置/类型)计算窗口内 xG,比进球更稳定、样本需求更小。
delta_xg = xg_post_window - xg_pre_window
指标3:换人"得分贡献值"(Sub Impact Score)
对每个替补上场球员,计算其上场后到比赛结束的:
- 进球 + 助攻 + 关键传球(加权)
- 除以出场时间,标准化为每90分钟
sub_impact = (goals*1.0 + assists*0.7 + key_passes*0.3) / minutes_played * 90
指标4:换人时机价值(Timing Value)
评估换人发生的时间点是否"划算"——比如落后时是否及时换进攻球员。
# 落后状态下,换人后15分钟内扳平/反超的概率 timing_value = P(score_change | trailing_at_sub, sub_type='attacking')
控制混杂因素(关键)
单纯比较换人前后会高估教练能力,因为:
- 比分状态驱动换人(落后才换前锋)
- 对手强度不同
- 比赛阶段本身进球率更高(最后15分钟进球多)
方法A:倾向得分匹配(PSM)
# 为每次换人构造特征:比分差、时间、对手强度、主客场、换人类型 features = ['goal_diff_at_sub', 'minute', 'opp_strength', 'home_away', 'sub_type'] # 匹配"相似情境下"的换人与不换人(或不同教练的换人) # 比较匹配后的 ΔGD
方法B:双重差分(DiD)
# 处理组:该教练的换人 # 对照组:同联赛/同赛季其他教练在相似情境下的换人 # DiD = (post-pre)_treatment - (post-pre)_control
方法C:回归模型
import statsmodels.api as sm # ΔGD ~ coach_dummy + goal_diff_at_sub + minute + opp_strength + ... model = sm.OLS(delta_gd, X).fit() coach_effect = model.params['coach_dummy']
统计稳健性检验
- 样本量门槛:每位教练至少 N 次换人(建议 ≥50),否则用贝叶斯收缩。
- 贝叶斯收缩:把小样本教练的效应向联赛均值收缩。
# 经验贝叶斯:coach_effect_shrunk = (n/(n+k))*raw + (k/(n+k))*league_mean
- 置换检验:随机打乱换人时间/球员,看真实效应是否显著高于随机。
- Bootstrap 置信区间:对 ΔGD 做重采样,给出 95% CI。
完整脚本骨架
import pandas as pd
import numpy as np
from scipy import stats
def evaluate_coach_subs(subs_df, events_df, coach_id, window=15, n_boot=1000):
coach_subs = subs_df[subs_df.coach_id == coach_id]
deltas = []
for _, sub in coach_subs.iterrows():
d = delta_goal_diff(events_df, sub, window)
deltas.append(d)
deltas = np.array(deltas)
# 点估计
mean_delta = deltas.mean()
# Bootstrap CI
boot = [np.random.choice(deltas, len(deltas)).mean() for _ in range(n_boot)]
ci_low, ci_high = np.percentile(boot, [2.5, 97.5])
# 与联赛基准比较(单样本 t 检验)
t, p = stats.ttest_1samp(deltas, popmean=league_baseline_delta)
return {
'coach_id': coach_id,
'n_subs': len(deltas),
'mean_delta_gd': mean_delta,
'ci_95': (ci_low, ci_high),
'p_value_vs_league': p
}
输出与可视化建议
- 教练排名表:按收缩后的 ΔGD 排序,附样本量和 CI。
- 换人类型分解:进攻换人/防守换人/对位换人分别的效应。
- 时间分布图:该教练换人时间 vs 效应大小,看是否存在"换太晚"模式。
- 球员层面:哪些替补上场后贡献最高(辅助判断是教练眼光还是球员个人能力)。
注意事项与局限
- 因果识别有限:观察数据无法完全排除混淆,PSM/DiD 只是缓解。
- xG 优于进球:进球样本稀疏,xG 更稳定,建议主指标用 xG 增量。
- 区分"换人决策"与"换人执行":球员上场后的表现不完全归因于教练。
- 联赛/赛季分层:不同联赛进球率不同,需分层或加固定效应。
- 小样本教练:务必用贝叶斯收缩,避免"换3次进2球"被高估。
如果需要,我可以进一步给出具体联赛数据的字段映射、xG 计算函数,或贝叶斯层级模型的 PyMC/Stan 代码,你手头的数据粒度大概是怎样的(是否有事件坐标、是否只有进球时间)?