本文目录导读:

这是一个很典型的数据分析+业务归因的案例,下面我用一个完整的 Python 复盘框架,来量化“主力伤退”对球队/业务的影响。
案例背景设定
假设我们有一支篮球队(或电竞战队、销售团队),核心主力球员 A 因伤缺阵,我们想知道:
- 他缺阵后,球队胜率/业绩下降了多少?
- 这个下降是“真的因为缺阵”,还是赛程、对手强弱等混杂因素导致?
- 影响有多大?能否量化?
Python 复盘完整代码
数据准备
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
# 模拟数据:一个赛季 60 场比赛
np.random.seed(42)
dates = pd.date_range('2024-10-01', periods=60, freq='3D')
opponent_strength = np.random.choice(['强', '中', '弱'], 60, p=[0.3, 0.4, 0.3])
# 主力A是否出战(前30场出战,后30场伤退)
a_plays = [1]*30 + [0]*30
# 基础胜率:主场/客场、对手强度影响
base_win_prob = {'强': 0.35, '中': 0.55, '弱': 0.75}
win_prob = np.array([base_win_prob[o] for o in opponent_strength])
# 主力A在场时 +15% 胜率加成
win_prob = np.where(np.array(a_plays)==1, win_prob + 0.15, win_prob)
win_prob = np.clip(win_prob, 0, 1)
wins = np.random.binomial(1, win_prob)
df = pd.DataFrame({
'date': dates,
'opponent': opponent_strength,
'a_plays': a_plays,
'win': wins
})
print(df.head())
简单对比:有无主力的胜率
summary = df.groupby('a_plays')['win'].agg(['mean', 'count', 'sum'])
summary.index = ['A伤退', 'A出战']
print(summary)
输出示例:
mean count sum
A伤退 0.433 30 13
A出战 0.767 30 23
表面看:胜率从 76.7% 降到 43.3%,下降 33.4 个百分点,但这可能是混杂因素(比如伤退期间对手更强)。
控制混杂因素:分层分析
# 按对手强度分层对比
pivot = df.pivot_table(index='opponent', columns='a_plays',
values='win', aggfunc='mean')
pivot.columns = ['A伤退', 'A出战']
pivot['差值'] = pivot['A出战'] - pivot['A伤退']
print(pivot)
输出示例:
A伤退 A出战 差值
opponent
中 0.50 0.75 0.25
强 0.20 0.50 0.30
弱 0.62 0.92 0.30
分层后,每个对手强度层级下,A伤退都导致胜率下降 25~30 个百分点,说明影响是真实的,不是赛程造成的。
统计显著性检验
# 卡方检验:伤退与胜负是否独立
contingency = pd.crosstab(df['a_plays'], df['win'])
chi2, p, dof, exp = stats.chi2_contingency(contingency)
print(f"卡方值={chi2:.2f}, p值={p:.4f}")
# p < 0.05 说明伤退与胜负显著相关
# 效应量:风险差 & 相对风险
p_play = df[df.a_plays==1]['win'].mean()
p_inj = df[df.a_plays==0]['win'].mean()
print(f"绝对风险差(ARR) = {p_play - p_inj:.3f}")
print(f"相对风险(RR) = {p_inj / p_play:.3f}") # 伤退后胜率是原来的多少
解读: 若 RR = 0.56,说明主力伤退后胜率只剩原来的 56%。
可视化
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
# 图1:整体胜率对比
df.groupby('a_plays')['win'].mean().plot(kind='bar', ax=axes[0],
color=['#e74c3c','#2ecc71'])
axes[0].set_xticklabels(['A伤退','A出战'], rotation=0)
axes[0].set_title('主力出战 vs 伤退:胜率对比')
axes[0].set_ylabel('胜率')
# 图2:分对手强度
pivot[['A伤退','A出战']].plot(kind='bar', ax=axes[1])
axes[1].set_title('分层对比:不同对手强度下的胜率')
axes[1].set_ylabel('胜率')
plt.tight_layout()
plt.show()
进阶:用回归模型剥离各因素贡献
import statsmodels.formula.api as smf
# 逻辑回归:win ~ 主力出战 + 对手强度
model = smf.logit('win ~ a_plays + C(opponent)', data=df).fit()
print(model.summary())
# 计算主力的边际效应(平均边际效应)
mfx = model.get_margeff()
print(mfx.summary())
解读: 回归系数 / 边际效应给出的是在控制对手强度后,主力出战对胜率的净贡献,比如边际效应 = 0.28,意味着主力在场平均提升 28 个百分点的胜率,这才是“伤退影响有多大”的干净估计。
复盘结论模板
| 指标 | 数值 | 含义 |
|---|---|---|
| 表面胜率下降 | 4 pp | 含混杂因素 |
| 分层后平均下降 | ~28 pp | 控制对手强度后 |
| 相对风险 RR | 56 | 伤退后胜率仅剩 56% |
| 卡方检验 p | <0.001 | 影响统计显著 |
| 回归净效应 | +28 pp | 主力出战的净贡献 |
主力伤退导致胜率下降约 28~33 个百分点,且在各对手强度层级下均显著,统计检验支持该影响非偶然。影响非常大,属于核心级依赖。
方法论要点(复盘时容易踩的坑)
- 辛普森悖论:不做分层可能高估或低估影响,必须先控制赛程/对手等混杂变量。
- 样本量:30 场伤退样本偏小,置信区间宽,结论要谨慎。
- 因果 vs 相关:伤退可能伴随其他变化(如战术调整、替补磨合),回归只能控制已观测变量。
- 替代指标:胜率之外,还应看净胜分、进攻效率等连续指标,信息量更大。
如果你有真实数据(比赛记录、球员出场、比分等),把字段发我,我可以直接帮你跑一套针对你数据的完整复盘脚本,你手头的数据大概是什么结构?