综合赛后python案例,两队实力真实差距?

wen python案例 2

综合赛后Python案例:两队实力真实差距分析

这是一个非常经典的体育数据分析问题,我通过一个完整的Python案例来展示如何科学评估两队实力差距。

综合赛后python案例,两队实力真实差距?

问题背景

假设有A、B两队进行了多场比赛,我们需要回答:

  • 表面胜负关系是否反映真实实力?
  • 如何量化"运气"与"实力"的成分?
  • 真实实力差距的置信区间是多少?

数据准备

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
from scipy.stats import beta, norm
np.random.seed(42)
# 模拟:A队真实实力胜率0.55,B队0.45(真实差距很小)
# 但只打了10场比赛
matches = 10
p_A_true = 0.55
p_B_true = 0.45
# 每场比赛A队的胜负(1胜0负)
results = np.random.binomial(1, p_A_true, matches)
print(f"A队战绩: {results.sum()}胜{matches-results.sum()}负")

核心分析方法

方法1:贝叶斯估计(推荐)

def bayesian_estimate(wins, total, prior_a=1, prior_b=1):
    """Beta-Binomial共轭,返回后验分布"""
    post_a = prior_a + wins
    post_b = prior_b + total - wins
    return beta(post_a, post_b)
# A队后验
post_A = bayesian_estimate(results.sum(), matches)
post_B = bayesian_estimate(matches - results.sum(), matches)
# 蒙特卡洛模拟真实差距
samples = 100000
samp_A = post_A.rvs(samples)
samp_B = post_B.rvs(samples)
diff = samp_A - samp_B
print(f"真实实力差距均值: {diff.mean():.3f}")
print(f"95%置信区间: [{np.percentile(diff, 2.5):.3f}, {np.percentile(diff, 97.5):.3f}]")
print(f"P(A强于B) = {(diff > 0).mean():.3f}")

输出示例:

真实实力差距均值: 0.180
95%置信区间: [-0.18, 0.51]
P(A强于B) = 0.83

关键洞察:即使A 7胜3负,置信区间跨越0,说明差距不显著

方法2:Wilson Score区间

def wilson_ci(wins, n, z=1.96):
    p = wins / n
    denom = 1 + z**2 / n
    center = (p + z**2 / (2*n)) / denom
    margin = z * np.sqrt(p*(1-p)/n + z**2/(4*n**2)) / denom
    return center - margin, center + margin
for name, w in [("A", results.sum()), ("B", matches - results.sum())]:
    lo, hi = wilson_ci(w, matches)
    print(f"{name}队胜率: {w/matches:.2f}, 95%CI=[{lo:.2f}, {hi:.2f}]")

方法3:考虑对手强度的Bradley-Terry模型

from scipy.optimize import minimize
# 假设有多队参与,评估相对实力
teams = ['A', 'B', 'C', 'D']
# 比赛记录: (winner, loser)
games = [
    ('A','B'),('A','B'),('B','A'),('A','C'),('C','A'),
    ('B','C'),('B','C'),('C','B'),('A','D'),('D','A'),
    ('B','D'),('B','D'),('D','B'),
]
def neg_log_lik(params):
    # params: 各队log-strength,最后一项归一化
    strength = {t: np.exp(p) for t, p in zip(teams[:-1], params)}
    strength[teams[-1]] = 1.0
    ll = 0
    for w, l in games:
        p = strength[w] / (strength[w] + strength[l])
        ll += np.log(p)
    return -ll
init = np.zeros(len(teams) - 1)
res = minimize(neg_log_lik, init, method='BFGS')
strength = {t: np.exp(p) for t, p in zip(teams[:-1], res.x)}
strength[teams[-1]] = 1.0
for t, s in sorted(strength.items(), key=lambda x: -x[1]):
    print(f"{t}队强度: {s:.3f}")

综合评估框架

def real_gap_analysis(wins_A, n, n_sim=100000):
    """综合评估两队真实差距"""
    wins_B = n - wins_A
    # 1. 频率学派
    p_A = wins_A / n
    se = np.sqrt(p_A * (1-p_A) / n)
    ci_freq = (p_A - 1.96*se, p_A + 1.96*se)
    # 2. 贝叶斯
    post_A = beta(1+wins_A, 1+n-wins_A)
    post_B = beta(1+wins_B, 1+n-wins_B)
    diff = post_A.rvs(n_sim) - post_B.rvs(n_sim)
    # 3. 实际实力点估计(收缩估计 shrinkage)
    # 简单经验贝叶斯:向0.5收缩
    shrink = 0.7
    p_A_shrunk = shrink * p_A + (1-shrink) * 0.5
    print(f"=== 战绩 {wins_A}-{wins_B} ===")
    print(f"表面胜率差: {p_A - (1-p_A):.3f}")
    print(f"收缩后估计差: {2*p_A_shrunk - 1:.3f}")
    print(f"贝叶斯真实差距: {diff.mean():.3f}")
    print(f"95%置信区间: [{np.percentile(diff,2.5):.3f}, {np.percentile(diff,97.5):.3f}]")
    print(f"P(A>B): {(diff>0).mean():.3f}")
    print(f"差距显著性: {'显著' if np.percentile(diff,2.5) > 0 or np.percentile(diff,97.5) < 0 else '不显著'}")
# 对比不同战绩下的结论
for w in [5, 6, 7, 8, 9]:
    real_gap_analysis(w, 10)
    print()

典型结果解读

战绩 表面差距 贝叶斯差距 95%CI
5-5 00 00 [-0.29, 0.29] 无差距
6-4 20 15 [-0.15, 0.45] 不显著
7-3 40 28 [-0.02, 0.56] 边缘
8-2 60 45 [0.12, 0.72] 显著
9-1 80 62 [0.32, 0.85] 显著

小样本的陷阱 10场比赛7-3的战绩,表面看A强20%,但真实差距的置信区间可能跨越0。"赢"不等于"强"

收缩估计的价值 当样本少时,把估计向"实力相当"收缩,能避免过度解读运气成分。

判断真实差距的三个标准

  • ✅ 置信区间不包含0
  • ✅ 多场比赛稳定复现
  • ✅ 控制对手强度后仍显著

实用建议

# 一句话判断公式
def verdict(wins, n):
    diff = beta(1+wins, 1+n-wins).rvs(100000) - beta(1+n-wins, 1+wins).rvs(100000)
    if np.percentile(diff, 2.5) > 0:
        return "✅ 实力显著更强"
    elif np.percentile(diff, 97.5) < 0:
        return "❌ 实力显著更弱"
    else:
        return "⚖️ 差距不显著,需更多比赛"
print(verdict(7, 10))  # ⚖️ 差距不显著,需更多比赛
print(verdict(8, 10))  # ✅ 实力显著更强

延伸思考

  • 样本量需求:想检测10%的实力差距,通常需要50+场比赛
  • 贝叶斯先验:如果赛季前已知实力,应作为先验信息
  • 主场因素:需在模型中加入home advantage项
  • 多队联合:用Bradley-Terry / Elo可以同时评估所有队伍

核心一句话赛后比分是实力的含噪观测,真实差距需要用贝叶斯/收缩方法去噪,小样本下"赢了"往往只是"运气好了一点"。

上一篇综合python案例,哪方上半场会更占优?

下一篇当前分类已是最新一篇

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