本文目录导读:

- 案例 1:基于历史半场/全场转换表的查询法(最简单)
- 案例 2:基于泊松分布的动态均值调整(数学建模法)
- 案例 3:结合实时赔率的市场修正法(量化交易视角)
- 案例 4:混合模型(贝叶斯朴素结合)
- 生产环境中的高级技巧
利用半场数据调整全场比赛预测,本质上是贝叶斯更新的逻辑:用“先验(赛前预测)”乘以“半场发生的事实(贝叶斯因子)”,得到“后验(调整后的预测)”。
在Python中,通常有两种实现路径:
- 模型驱动法(数据科学):基于泊松分布(Poisson)或机器学习模型,输入半场比分和半场技术统计,重新预估全场预期进球数(xG)。
- 规则/赔率驱动法(量化策略):比较半场实时赔率与赛前赔率的变化,或者基于历史半场/全场结果的分布表进行映射。
下面提供几个由浅入深的Python实战案例,涵盖简单的统计调整到复杂的蒙特卡洛模拟。
案例 1:基于历史半场/全场转换表的查询法(最简单)
核心逻辑:统计历史数据中,当半场出现特定比分(如 1:0)时,全场出现各种结果(胜/平/负)的概率分布。
import pandas as pd
import numpy as np
# 模拟生成历史比赛数据(半场比分,全场比分)
np.random.seed(42)
n_games = 5000
half_home = np.random.poisson(0.8, n_games) # 半场主队进球
half_away = np.random.poisson(0.5, n_games)
full_home = half_home + np.random.poisson(1.0, n_games) # 下半场进球独立
full_away = half_away + np.random.poisson(0.6, n_games)
df = pd.DataFrame({
'half_home': half_home,
'half_away': half_away,
'full_home': full_home,
'full_away': full_away
})
# 定义结果函数
def get_result(h, a):
if h > a: return 'Home_Win'
elif h < a: return 'Away_Win'
else: return 'Draw'
df['half_result'] = df.apply(lambda r: get_result(r['half_home'], r['half_away']), axis=1)
df['full_result'] = df.apply(lambda r: get_result(r['full_home'], r['full_away']), axis=1)
# 当前半场比分(假设半场主队1:0领先)
live_half_home, live_half_away = 1, 0
# 查询所有历史中,与该半场比分相同的比赛
filtered = df[(df['half_home'] == live_half_home) & (df['half_away'] == live_half_away)]
if len(filtered) > 0:
# 计算全场比赛结果的频率分布(这就是调整后的后验概率)
prob_dist = filtered['full_result'].value_counts(normalize=True)
print(f"半场 {live_half_home}:{live_half_away} 时,全场概率分布:")
print(prob_dist.round(3))
else:
# 如果历史样本不足,退化为计算半场领先时的整体概率
filtered = df[df['half_result'] == 'Home_Win'] # 或使用回归平滑
prob_dist = filtered['full_result'].value_counts(normalize=True)
print(f"样本不足,退化为半场主队领先时的概率:")
print(prob_dist.round(3))
输出示例:半场1:0时,主胜概率可能从赛前的45%提升到65%,这就是数据驱动的调整。
案例 2:基于泊松分布的动态均值调整(数学建模法)
核心逻辑:足球进球服从泊松分布,假设半场数据显示主队进攻力强于预期(实际进1球),我们就调高下半场的预期进球数(λ),然后模拟下半场进球,结合上半场实际得分得出全场概率。
import numpy as np
from scipy.stats import poisson, gamma
# 赛前模型预期的全场平均进球(基于ElO或基本面)
pre_lambda_home = 1.35
pre_lambda_away = 1.10
# 上半场实际数据(45分钟)
actual_home_goals = 1
actual_away_goals = 0
# 由于比赛上下半场独立性较弱,这里使用“加权法”更新下半场预期:
# 权重因子(0~1),代表半场信息对下半场的修正力度(通常0.2~0.5,越大代表越重视近期状态)
adjustment_weight = 0.3
# 计算调整后的下半场期望值(半场期望值 = 40%~45%的全场期望值)
expected_first_half_home = pre_lambda_home * 0.45
expected_second_half_home = pre_lambda_home * 0.55
# 用实际半场进球数调整下半场期望:调整幅度 = (实际 - 预期) * 权重
adjusted_second_home = expected_second_half_home + (actual_home_goals - expected_first_half_home) * adjustment_weight
adj_second_away = pre_lambda_away * 0.55 + (actual_away_goals - pre_lambda_away * 0.45) * adjustment_weight
# 防止负值
adjusted_second_home = max(0.1, adjusted_second_home)
adj_second_away = max(0.1, adj_second_away)
# 蒙特卡洛模拟下半场进球(100000次)
sims = 100000
sim_second_home = np.random.poisson(adjusted_second_home, sims)
sim_second_away = np.random.poisson(adj_second_away, sims)
# 计算全场比分和结果
full_home_score = actual_home_goals + sim_second_home
full_away_score = actual_away_goals + sim_second_away
home_win_prob = np.mean(full_home_score > full_away_score)
draw_prob = np.mean(full_home_score == full_away_score)
away_win_prob = np.mean(full_home_score < full_away_score)
print(f"调整后的下半场预期进球(主/客):{adjusted_second_home:.2f} / {adj_second_away:.2f}")
print(f"调整后预测全场概率 -> 主胜:{home_win_prob*100:.2f}% | 平局:{draw_prob*100:.2f}% | 客胜:{away_win_prob*100:.2f}%")
说明:这里展示了如何用“半场实际数据”修正下半场预期,核心参数adjustment_weight决定了半场信息的权重,过大会过拟合,过小则更新过慢。
案例 3:结合实时赔率的市场修正法(量化交易视角)
核心逻辑:利用博彩公司半场赔率 / 赛前赔率的变化来反向推导市场资金流的动向,半场赔率剧烈变化意味着信息泄露或资金大量涌入。
import pandas as pd
# 模拟数据:赛前赔率 vs 半场赔率(以欧赔为例,主胜/平/客胜)
pre_odds = pd.Series({'Home': 2.10, 'Draw': 3.40, 'Away': 3.60})
half_odds = pd.Series({'Home': 1.50, 'Draw': 4.20, 'Away': 6.50})
# 去除水分(假设返还率90%)
def normalize_odds(s):
implied_prob = 1 / s
adjusted = implied_prob / implied_prob.sum() * 0.90 # 返还率
return adjusted
pre_prob = normalize_odds(pre_odds)
half_prob = normalize_odds(half_odds)
# 计算赔率变化率:比例 >1 表示支持率上升(水分去除后比较)
change_factor = half_prob / pre_prob
print("概率变化因子(>1 表示该结果概率被市场调高):")
print(change_factor.round(2))
# 最终预测概率 = 赛前概率 * 变化因子(需要归一化)
final_unnorm = pre_prob * change_factor
final_prob = final_unnorm / final_unnorm.sum()
print("\n调整后的预测概率:")
print(final_prob.round(3))
输出解读:主胜从赛前的约43%概率,因半场赔率跳水(1.5),调整后上升到约65%,这反映了市场对半场信息的定价。
案例 4:混合模型(贝叶斯朴素结合)
核心逻辑:将赛前预测(基于Elo或预期进球)与半场实际状态进行加权融合,这里模拟赛前概率,再结合半场比分的似然函数调整。
# 假设赛前模型预测主队胜率0.45,平局0.28,客胜0.27
prior = {'Home_Win': 0.45, 'Draw': 0.28, 'Away_Win': 0.27}
# 半场比分为 1:0,根据历史统计,从半场到全场的转换矩阵(
# 行(半场状态):主胜(1:0 等),列(全场结果对应概率)
# 这是一个简化的似然矩阵(实际统计表更细)
transition_matrix = {
'Home_Leading': {'Home_Win': 0.70, 'Draw': 0.20, 'Away_Win': 0.10},
'Draw': {'Home_Win': 0.25, 'Draw': 0.55, 'Away_Win': 0.20},
'Away_Leading': {'Home_Win': 0.10, 'Draw': 0.20, 'Away_Win': 0.70}
}
# 当前半场状态
half_state = 'Home_Leading' # 主队领先
# 计算后验(即调整后的概率):p(全场结果 | 半场状态)
# 需要用赛前先验和似然计算边缘概率,但简化场景我们可以直接线性融合
# 融合权重:半场信息占40%,赛前占60%
mix_weight = 0.4
likelihood = transition_matrix[half_state]
posterior = {}
for result in prior.keys():
# 归一化调整:使用似然比调整先验
posterior[result] = prior[result] * likelihood[result]
# 归一化
total = sum(posterior.values())
posterior = {k: v/total for k, v in posterior.items()}
print("赛前预测:", {k: round(v,2) for k,v in prior.items()})
print("半场调整后:", {k: round(v,2) for k,v in posterior.items()})
生产环境中的高级技巧
- 数据源扩展:半场数据远不止比分,应加入半场射门数、角球、控球率、危险进攻次数,这些可以通过逻辑回归或XGBoost预测下半场进球数。
- 时间衰减:最新的联赛数据权重应该更高(比如最近5轮比赛占70%权重)。
- 平滑处理:当半场样本极少时(例如半场3:0),需要使用
拉普拉斯平滑或者贝叶斯收缩,防止概率极端化。 - 实时爬虫:若要真正用于交易,需用
requests或selenium抓取365或Bet365的实时数据,并计算K-L散度衡量市场偏离。
- 简单业务场景:用案例1(历史映射表)最直观易懂。
- 需要赌场对手盘策略:案例3的赔率变化分析最接近市场真实。
- 科学建模需求:案例4的贝叶斯融合或案例2的泊松动态调整能提供更平滑的概率曲线。
在实际项目中,最好的做法是先离线用历史半场数据训练一个“半场特征 → 全场概率”的模型(如Gradient Boosting),比赛进行到半场时,提取实时的特征矩阵,接入模型,即可得到动态概率预测。
如果你有具体的半场数据字段(如控球率、射正数等),可以补充一下,我可以给出更针对性的Pandas/Sklearn实现。