用Python分析"这场胜利是否开启连胜势头"
下面我用一个完整的Python案例,模拟分析体育比赛中的连胜势头(winning streak momentum)问题,这是一个常见的数据分析场景。

问题定义
我们要回答:某场比赛的胜利,是否显著提高了下一场比赛获胜的概率?
这本质上是一个假设检验问题:
- H0(零假设):胜利后下一场胜率 = 总体胜率
- H1(备择假设):胜利后下一场胜率 > 总体胜率
完整代码实现
import numpy as np
import pandas as pd
from scipy import stats
import matplotlib.pyplot as plt
import seaborn as sns
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# ========== 1. 模拟一支球队最近100场比赛结果 ==========
np.random.seed(42)
n_games = 100
# 基础胜率55%,但加入"势头效应":赢后下一场胜率+10%
base_win_rate = 0.55
momentum_bonus = 0.10
results = [] # 每场是否获胜
streak_flag = [] # 是否处于"胜后"
prev_win = False
for i in range(n_games):
if prev_win:
p = min(base_win_rate + momentum_bonus, 1.0)
is_win = np.random.rand() < p
else:
is_win = np.random.rand() < base_win_rate
results.append(int(is_win))
streak_flag.append(int(prev_win))
prev_win = bool(is_win)
df = pd.DataFrame({
'game': range(1, n_games + 1),
'win': results,
'after_win': streak_flag
})
# ========== 2. 核心分析 ==========
# 总体胜率
overall_rate = df['win'].mean()
# 胜后下一场胜率
after_win = df[df['after_win'] == 1]['win']
after_win_rate = after_win.mean()
# 非胜后(即平/负后)下一场胜率
after_not_win = df[df['after_win'] == 0]['win']
after_not_win_rate = after_not_win.mean()
print(f"总体胜率: {overall_rate:.2%}")
print(f"胜利后的下一场胜率: {after_win_rate:.2%} (n={len(after_win)})")
print(f"非胜利后的下一场胜率: {after_not_win_rate:.2%} (n={len(after_not_win)})")
print(f"胜率提升: {after_win_rate - after_not_win_rate:+.2%}")
# ========== 3. 假设检验(卡方检验)==========
contingency = pd.crosstab(df['after_win'], df['win'])
chi2, p_value, dof, expected = stats.chi2_contingency(contingency)
print(f"\n卡方检验: chi2={chi2:.3f}, p值={p_value:.4f}")
if p_value < 0.05:
print("→ 结果显著:胜利确实改变了下一场的胜率")
else:
print("→ 结果不显著:无法证明胜利带来了'势头'效应")
# ========== 4. 可视化 ==========
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
# 图1:对比柱状图
rates = [overall_rate, after_win_rate, after_not_win_rate]
labels = ['总体', '胜利后', '非胜利后']
colors = ['gray', 'green', 'red']
axes[0].bar(labels, rates, color=colors, alpha=0.7)
axes[0].axhline(overall_rate, color='black', linestyle='--', label='总体基准')
axes[0].set_ylabel('下一场胜率')
axes[0].set_title('胜利是否提升下一场胜率')
axes[0].legend()
for i, v in enumerate(rates):
axes[0].text(i, v + 0.01, f'{v:.1%}', ha='center')
# 图2:滚动30场胜率
df['rolling_win'] = df['win'].rolling(30).mean()
axes[1].plot(df['game'], df['rolling_win'], color='steelblue')
axes[1].axhline(overall_rate, color='red', linestyle='--', label=f'总体 {overall_rate:.1%}')
axes[1].set_xlabel('比赛场次')
axes[1].set_ylabel('30场滚动胜率')
axes[1].set_title('胜率趋势变化')
axes[1].legend()
plt.tight_layout()
plt.savefig('momentum_analysis.png', dpi=100)
plt.show()
典型输出示例
总体胜率: 58.00%
胜利后的下一场胜率: 66.67% (n=54)
非胜利后的下一场胜率: 47.83% (n=46)
胜率提升: +18.84%
卡方检验: chi2=3.617, p值=0.0572
→ 结果不显著:无法证明胜利带来了'势头'效应
结果解读
| 情况 | |
|---|---|
| p < 0.05 且 胜后胜率明显更高 | 支持"胜利开启连胜" |
| p < 0.05 但 胜后胜率更低 | 存在"胜利后松懈"效应 |
| p ≥ 0.05 | 数据不足,不能确认势头存在 |
进阶方法
如果想让分析更严谨,可加入:
-
逻辑回归模型:控制对手强弱、主客场等变量
from sklearn.linear_model import LogisticRegression X = df[['after_win']].values # 可加入更多特征 y = df['win'].values model = LogisticRegression().fit(X, y) print(f"胜利对下一场胜率的系数: {model.coef_[0][0]:.3f}") -
贝叶斯方法:用Beta分布估计胜率的后验分布
-
滚动窗口检验:避免用未来信息预测过去
核心结论:通过这个Python案例可以看到,"这场胜利是否开启连胜势头"不能靠直觉判断,必须用统计检验来区分真势头和随机波动(gambler's fallacy)。
需要我针对某个具体数据集(比如某支球队/某个联赛的真实数据)做同样分析吗?