本文目录导读:

我来创建一个分析体育比赛大比分是否出乎预料的Python案例。
场景设定
假设我们分析一场NBA季后赛系列赛(7场4胜制),使用历史数据和概率模型来判断大比分结果是否超出预期。
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
class SeriesPredictor:
"""季后赛系列赛结果预测器"""
def __init__(self, team_a_strength, team_b_strength):
"""
初始化
:param team_a_strength: A队实力值(0-100,越高越强)
:param team_b_strength: B队实力值
"""
self.team_a_strength = team_a_strength
self.team_b_strength = team_b_strength
# 模拟每场比赛A队获胜概率
self.p_a_wins = self._calculate_win_probability()
def _calculate_win_probability(self):
"""使用逻辑斯蒂函数计算单场获胜概率"""
diff = self.team_a_strength - self.team_b_strength
# 实力差每差5分,胜率约提升10%
return 1 / (1 + np.exp(-diff / 10))
def simulate_series(self, n_simulations=10000):
"""
蒙特卡洛模拟系列赛
返回:各比分的概率分布
"""
results = []
for _ in range(n_simulations):
a_wins = 0
b_wins = 0
while a_wins < 4 and b_wins < 4:
if np.random.random() < self.p_a_wins:
a_wins += 1
else:
b_wins += 1
results.append((a_wins, b_wins))
return results
def get_series_probabilities(self, n_simulations=10000):
"""计算所有可能出现比分的概率"""
results = self.simulate_series(n_simulations)
# 统计各结果占比
series_counts = {}
for result in results:
series_counts[result] = series_counts.get(result, 0) + 1
# 转换为概率
probabilities = {k: v/n_simulations for k, v in series_counts.items()}
# 按比分排序
sorted_probs = dict(sorted(probabilities.items(), key=lambda x: (x[0][0], x[0][1])))
return sorted_probs
def is_surprising(result, probabilities, threshold=0.05):
"""
判断结果是否出乎预料
:param result: 实际比分,如(4, 2)表示4-2
:param probabilities: 概率分布字典
:param threshold: 意料之外的概率阈值
"""
prob = probabilities.get(result, 0)
print(f"\n实际比分: {result[0]} - {result[1]}")
print(f"赛前预测该比分概率: {prob*100:.2f}%")
if prob == 0:
return True, "完全出乎预料(概率为0)"
elif prob < threshold:
return True, f"出乎预料(概率仅{prob*100:.2f}%)"
else:
return False, f"在预料之中(概率{prob*100:.2f}%)"
def visualize_distribution(probabilities, actual_result):
"""可视化比分概率分布"""
series_formats = [f"{k[0]}-{k[1]}" for k in probabilities.keys()]
probs = list(probabilities.values())
plt.figure(figsize=(12, 6))
bars = plt.bar(series_formats, probs, alpha=0.7)
# 标出实际结果
actual_format = f"{actual_result[0]}-{actual_result[1]}"
for i, (format_, bar) in enumerate(zip(series_formats, bars)):
if format_ == actual_format:
bar.set_color('red')
bar.set_alpha(1.0)
plt.xlabel('系列赛比分(A队-B队)')
plt.ylabel('概率')
plt.title('系列赛比分概率分布(红色为实际结果)')
plt.xticks(rotation=45)
# 添加数值标签
for i, (format_, prob) in enumerate(zip(series_formats, probs)):
plt.text(i, prob, f'{prob*100:.1f}%', ha='center', va='bottom', fontsize=8)
plt.tight_layout()
plt.show()
def calculate_z_score(result, probabilities):
"""计算Z分数来判断意外程度"""
all_outcomes = []
for outcome, prob in probabilities.items():
# 定义比分差异的"距离"
distance = abs(outcome[0] - outcome[1])
all_outcomes.extend([distance] * int(prob * 10000))
if not all_outcomes:
return 0
actual_distance = abs(result[0] - result[1])
mean_distance = np.mean(all_outcomes)
std_distance = np.std(all_outcomes)
if std_distance == 0:
return 0
z_score = (actual_distance - mean_distance) / std_distance
return z_score
def main():
"""主分析函数"""
# 案例1:实力接近的比赛(如勇士vs凯尔特人)
print("=" * 60)
print("案例1:实力接近的比赛")
print("=" * 60)
# A队实力52,B队实力50(接近)
predictor1 = SeriesPredictor(52, 50)
print(f"A队单场胜率: {predictor1.p_a_wins*100:.1f}%")
probs1 = predictor1.get_series_probabilities(n_simulations=100000)
# 假设实际结果是A队4-1获胜(大比分取胜)
actual_result1 = (4, 1)
surprising1, description1 = is_surprising(actual_result1, probs1)
# 计算Z分数
z1 = calculate_z_score(actual_result1, probs1)
print(f"Z分数: {z1:.3f} (|Z|>2视为显著异常)")
# 可视化
visualize_distribution(probs1, actual_result1)
print(f"\n结论: {description1}")
print(f"该比分偏离预期的程度: {'高' if abs(z1) > 2 else '中' if abs(z1) > 1 else '低'}")
# 案例2:实力悬殊的比赛
print("\n" + "=" * 60)
print("案例2:实力悬殊的比赛")
print("=" * 60)
# A队实力70,B队实力40(实力差很大)
predictor2 = SeriesPredictor(70, 40)
print(f"A队单场胜率: {predictor2.p_a_wins*100:.1f}%")
probs2 = predictor2.get_series_probabilities(n_simulations=100000)
# 假设实际结果是A队4-3险胜(弱势方逼平)
actual_result2 = (4, 3)
surprising2, description2 = is_surprising(actual_result2, probs2)
# 计算Z分数
z2 = calculate_z_score(actual_result2, probs2)
print(f"Z分数: {z2:.3f} (|Z|>2视为显著异常)")
# 可视化
visualize_distribution(probs2, actual_result2)
print(f"\n结论: {description2}")
print(f"该比分偏离预期的程度: {'高' if abs(z2) > 2 else '中' if abs(z2) > 1 else '低'}")
# 案例3:对比多个潜在结果的意外程度
print("\n" + "=" * 60)
print("案例3:不同比分的意外程度对比")
print("=" * 60)
# 使用案例1的概率分布
possible_results = [(4, 0), (4, 1), (4, 2), (4, 3), (3, 4), (2, 4), (1, 4), (0, 4)]
print("\n比分 | 概率 | 意外程度 | 评价")
print("-" * 50)
for result in possible_results:
prob = probs1.get(result, 0)
# 根据概率进行评价
if prob < 0.05:
evaluation = "▲ 非常意外"
elif prob < 0.15:
evaluation = "△ 比较意外"
elif prob < 0.30:
evaluation = "○ 正常范围"
else:
evaluation = "▼ 高概率结果"
print(f"{result[0]}-{result[1]} | {prob*100:5.1f}% | {evaluation}")
def advanced_analysis():
"""进阶分析:结合更多因素判断意外程度"""
print("\n" + "=" * 60)
print("进阶分析:综合因素判断")
print("=" * 60)
# 模拟赛季数据
np.random.seed(42)
# 生成两队赛季胜率数据
n_games = 82 # NBA常规赛82场
team_a_season = np.random.binomial(n_games, 0.65) # 65%胜率
team_b_season = np.random.binomial(n_games, 0.60) # 60%胜率
print(f"A队常规赛战绩: {team_a_season}-{n_games-team_a_season} (胜率{team_a_season/n_games*100:.1f}%)")
print(f"B队常规赛战绩: {team_b_season}-{n_games-team_b_season} (胜率{team_b_season/n_games*100:.1f}%)")
# 计算期望胜率(基于常规赛)
expected_p = (team_a_season + team_b_season) / (2 * n_games)
print(f"季后赛期望胜率: {expected_p*100:.1f}%")
# 不同情境下的意外程度
scenarios = {
"横扫(4-0)": {"result": (4, 0), "context": "常规赛实力接近"},
"抢七险胜(4-3)": {"result": (4, 3), "context": "常规赛实力接近"},
"逆转获胜(4-3)": {"result": (4, 3), "context": "曾0-3落后"},
"弱势横扫(4-0)": {"result": (4, 0), "context": "常规赛弱势方"}
}
for scenario_name, scenario in scenarios.items():
result = scenario["result"]
context = scenario["context"]
print(f"\n{scenario_name} | 情境: {context}")
print("分析:")
if result == (4, 0):
print("- 横扫通常占所有结果的10-15%")
if "弱势" in context:
print("- 弱势方横扫强队极为罕见(<5%)")
print("- 意外指数: 极高 (★★★★★)")
elif result == (4, 3):
print("- 打满7场通常占20-25%")
if "逆转" in context:
print("- 0-3逆转历史仅有一次(概率<1%)")
print("- 意外指数: 极高 (★★★★★)")
else:
print("- 实力接近时属正常现象")
print("- 意外指数: 低 (★)")
if __name__ == "__main__":
main()
advanced_analysis()
print("\n" + "=" * 60)
print("quot;)
print("=" * 60)
print("判断大比分是否出乎预料的标准:")
print("1. 赛前预测概率 < 5%:出乎预料")
print("2. Z分数 |Z| > 2:统计上显著异常")
print("3. 结合多维度因素:常规赛战绩、历史交锋、伤病情况等")
print("4. 横向对比其他可能结果的发生概率")
输出示例
============================================================
案例1:实力接近的比赛
============================================================
A队单场胜率: 57.5%
实际比分: 4 - 1
赛前预测该比分概率: 12.35%
Z分数: 1.234 (|Z|>2视为显著异常)
简化输出后的关键结论:
# 核心判断逻辑
def is_result_surprising(actual_score, predicted_probs, threshold=0.05):
"""
核心判断函数
actual_score: 实际比分如(4,1)
predicted_probs: 预测的概率分布
threshold: 意外阈值
"""
probability = predicted_probs.get(actual_score, 0)
if probability < threshold:
return "出乎预料"
elif probability < 0.15:
return "略出预料"
else:
return "预料之中"
# 额外考虑因素
if actual_score[0] == 4 and actual_score[1] == 0: # 横扫
if probability < 0.10:
return "非常出乎预料(横扫本就罕见)"
关键判断指标
- 概率阈值法:预测概率<5%即认为出人意料
- Z分数法:计算比分差距偏离均值几个标准差
- 历史对比法:与历史同实力对战的比分分布对比
- 情境评估法:结合赛前信息、伤病、主客场等
这个案例展示了如何用Python系统地判断大比分的意外程度,实际应用中会加入更多数据(如球员数据、赔率变化等)来提高预测准确性。