python案例如何分析替补奇兵的战术价值?

wen python案例 2

用Python分析"替补奇兵"的战术价值

"替补奇兵"是体育分析(尤其是足球、篮球)中的经典话题——指替补上场后对比赛产生超预期影响的球员,下面给出一个可落地的Python分析框架,涵盖数据获取、指标构建、可视化与建模。

python案例如何分析替补奇兵的战术价值?


分析思路框架

数据层 → 指标层 → 模型层 → 可视化层 → 决策层

核心要回答三个问题:

  1. 谁是奇兵?(识别异常高贡献的替补)
  2. 奇兵何时上场最有效?(情境分析:比分、时间、对手)
  3. 奇兵的战术价值有多大?(对比赛结果的因果/相关贡献)

数据准备

数据源建议

类型 来源
足球事件数据 StatsBomb Open Data、FBref、Understat
篮球阵容数据 NBA Stats API(nba_api)、PbpStats
比赛结果/赔率 Football-Data.co.uk、FiveThirtyEight

关键字段(足球为例)

# 每名球员一行
columns = [
    'match_id', 'player_id', 'player_name',
    'is_starter',        # 是否首发
    'minute_in',         # 上场时间
    'minute_out',        # 下场时间
    'goals', 'assists', 'xg', 'xa',   # 进攻贡献
    'tackles', 'interceptions',        # 防守贡献
    'team_score_at_in', 'opp_score_at_in',  # 上场时比分
    'final_result'       # 最终胜负
]

核心指标构建

单位时间贡献率(Per-90)

替补上场时间短,必须做时间归一化:

import pandas as pd
import numpy as np
def per90(df, col, minutes_col='minutes_played'):
    return df[col] / df[minutes_col] * 90
df['goal_contrib_per90'] = per90(df, 'goals') + per90(df, 'assists')
df['xg_contrib_per90']  = per90(df, 'xg') + per90(df, 'xa')

"奇兵指数"(Impact Score)

综合进攻+防守+比赛情境的加权分:

def impact_score(row):
    attack = 0.5*row['goal_contrib_per90'] + 0.3*row['xg_contrib_per90']
    defense = 0.1*row['tackles_per90'] + 0.1*row['interceptions_per90']
    # 逆境加成:落后时上场贡献权重更高
    clutch = 1.3 if row['team_score_at_in'] < row['opp_score_at_in'] else 1.0
    return (attack + defense) * clutch
df['impact_score'] = df.apply(impact_score, axis=1)

替补 vs 首发的分位对比

subs   = df[df.is_starter == False]['impact_score']
starts = df[df.is_starter == True]['impact_score']
print("替补中位数:", subs.median())
print("首发中位数:", starts.median())
print("替补90分位:", subs.quantile(0.9))  # 奇兵候选线

奇兵定义:替补出场 + impact_score 位于该队/该联赛替补群体的 90 分位以上。


情境分析:什么时候派奇兵最有效?

按上场时段分组

bins = [0, 60, 70, 80, 90]
labels = ['60前', '60-70', '70-80', '80+']
df['sub_window'] = pd.cut(df['minute_in'], bins=bins, labels=labels)
window_effect = df[df.is_starter==False].groupby('sub_window').agg(
    avg_impact=('impact_score','mean'),
    win_rate=('final_result', lambda x: (x=='win').mean()),
    n=('match_id','count')
)
print(window_effect)

按上场时比分状态分组

df['score_state'] = np.select(
    [df.team_score_at_in > df.opp_score_at_in,
     df.team_score_at_in == df.opp_score_at_in,
     df.team_score_at_in < df.opp_score_at_in],
    ['领先','平局','落后']
)
state_effect = df[df.is_starter==False].groupby('score_state')['impact_score'].mean()

因果/相关性建模:奇兵是否真的改变结果?

方法1:倾向得分匹配(PSM)

控制"上场时比分、对手强度、剩余时间"等因素,比较有/无奇兵上场的胜率:

from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import NearestNeighbors
# 处理变量: 是否派上奇兵(sub_has_star)
# 协变量: score_state, minute_in, opponent_strength, home_away
features = ['score_state_num','minute_in','opp_strength','is_home']
model = LogisticRegression().fit(X[features], y_treat)
ps = model.predict_proba(X[features])[:,1]
# 最近邻匹配后再比较 win_rate

方法2:比分状态固定下的回归

import statsmodels.formula.api as smf
# 只看落后时换上替补的比赛
tied = df[(df.is_starter==False) & (df.score_state=='落后')]
model = smf.logit('win ~ has_star_sub + minute_in + opp_strength', data=tied).fit()
print(model.summary())

has_star_sub 的系数即"奇兵"在落后情境下对胜率的边际贡献。


可视化(matplotlib / plotly)

import matplotlib.pyplot as plt
# 1. 上场时间 vs 影响值的散点,看"奇兵区"在哪
plt.figure(figsize=(9,6))
for grp, d in df.groupby('is_starter'):
    plt.scatter(d['minute_in'], d['impact_score'],
                alpha=0.4, label='首发' if grp else '替补')
plt.axhline(subs.quantile(0.9), ls='--', color='red', label='奇兵阈值')
plt.xlabel('上场分钟'); plt.ylabel('Impact Score')
plt.legend(); plt.title('替补奇兵分布图')
# 2. 上场时段 × 平均影响值的热力图
import seaborn as sns
pivot = df[df.is_starter==False].pivot_table(
    index='sub_window', columns='score_state',
    values='impact_score', aggfunc='mean')
sns.heatmap(pivot, annot=True, cmap='RdYlGn')

输出决策结论(示例)

分析结束后可产出如下洞察表:

情境 推荐奇兵类型 依据
70分钟后落后 高xG/xA前锋 impact_score在落后时最高
60分钟平局 中场绞杀型 抢断/拦截per90显著高于均值
领先需控场 经验型 失误率低、传球成功率高

进阶方向

  1. 贝叶斯层次模型:用 PyMC 估计每名球员的"替补超常发挥"后验分布,避免小样本偏差。
  2. 多臂老虎机:把"换谁上场"建模为在线决策问题,动态学习最优换人策略。
  3. 比赛态势嵌入:用 socceraction / VAEP 框架计算每次动作的期望得分增量,让"奇兵"衡量更细粒度。
  4. 因果森林:用 econml 估计不同比分/时间下替补的异质性处理效应(CATE)。

最小可运行Demo

如果你手上暂时没有真实数据,可用下面代码生成模拟数据跑通全流程:

np.random.seed(42)
n = 2000
df = pd.DataFrame({
    'is_starter': np.random.choice([True, False], n, p=[0.7,0.3]),
    'minute_in': np.random.randint(45, 90, n),
    'minutes_played': np.random.randint(10, 45, n),
    'goals': np.random.poisson(0.15, n),
    'assists': np.random.poisson(0.1, n),
    'xg': np.random.gamma(1, 0.1, n),
    'xa': np.random.gamma(1, 0.08, n),
    'tackles': np.random.poisson(1.5, n),
    'interceptions': np.random.poisson(1.0, n),
    'team_score_at_in': np.random.randint(0,3,n),
    'opp_score_at_in':  np.random.randint(0,3,n),
    'final_result': np.random.choice(['win','draw','lose'], n)
})
# 后续套用上面的 per90、impact_score、分组分析即可

这样就能从原始数据 → 奇兵识别 → 情境最优 → 因果验证 → 可视化决策跑通一个完整的Python分析案例,需要我针对足球或篮球某一种运动,细化某一步(比如VAEP、CATE估计或可视化模板)吗?

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